Учебник, который вы читаете, не совсем правильный. Существуют два различных соглашения для глобальных символов в исполняемых файлах ELF (Executable и Linkable Format). Одно соглашение гласит, что все глобальные символы C должны иметь префикс _
, другое соглашение не ставит префикс символов C. В GNU / Linux, особенно в x86-64 ABI, глобальные символы не имеют префикса _
. Однако руководство, которое вы связали, может быть подходящим для какого-нибудь другого компилятора для Linux / ELF, который не использовал GNU libc.
Теперь в исходном коде происходит то, что ваша функция ассемблера будет отображаться как _write
в коде C, а не write
. Вместо этого символ write
находится в libc
(оболочке для системного вызова write(2)
):
ssize_t write(int fd, const void *buf, size_t count);
Теперь вы объявили this write
как функцию void write(void);
, которая при вызове приводит к неопределенному поведению как таковому. Вы можете использовать strace ./program
, чтобы узнать, какие системные вызовы он выполняет:
% strace ./program
...
write(1, "\246^P\313\374\177\0\0\0\0\0\0\0\0"..., 140723719521144) = -1 EFAULT (Bad address)
...
Таким образом, он вызвал системный вызов write
не с вашими предполагаемыми аргументами, а с любым мусором, который был в регистрах, предоставленных оболочке glibc write
. (на самом деле здесь известен «мусор» - первый аргумент - это argc
, второй аргумент - это значение argv
, а третий аргумент - это значение char **environ
). И когда ядро заметило, что буфер, начинающийся с (void*)argv
и длиной 140723719521144 байта, не полностью содержится в отображенном адресном пространстве, оно вернуло EFAULT
из этого системного вызова. Результат: ни сбоя, ни сообщения.
write
не является зарезервированным словом как таковое в C. Это функция и, возможно, макрос в POSIX. Вы можете перезаписать его, порядок связывания имеет значение - если вы в программе определяете write
, другой код будет связан с этим определением вместо того, который находится в glibc. Однако это будет означать, что другой код, вызывающий write
, вместо этого вызовет вашу несовместимую функцию.
Таким образом, решение состоит в том, чтобы не использовать имя, которое является функцией в GNU libc или в любых других библиотеках, с которыми вы связались. Таким образом, в ассемблере вы можете использовать:
.global writehello
writehello:
а потом
extern void writehello(void);
как вы сами выяснили.
person
Antti Haapala
schedule
30.04.2016
write
- зарезервированное слово в 'C'. Переименуйте свою функцию и попробуйте еще раз. - person KevinDTimm   schedule 13.04.2016