Линкер по умолчанию для динамического c
ld
для i386 - /usr/lib/libc.so.1
, что сегодня неправильно в большинстве Linux систем. Вы пытались переопределить это, но путь, который вы указали, тоже был неправильным. Два варианта:
- Вручную передайте правильный при ссылке:
ld hello.o -o hello -dynamic-linker /lib/ld-linux.so.2 -lc -m elf_i386
- Используйте вместо этого
gcc
, в качестве упомянутого фуза : gcc -nostartfiles -m32 -o hello hello.o
Если вам интересно, как я узнал, какой правильный компоновщик динамического c был для варианта 1, я сделал это, выполнив сначала один вариант 2 и проверив, какой он использовал.
См. Также Ошибка Red Hat 868662 - /lib/ld64.so.1: плохой ELF Интерпретатор: Нет такого файла или каталога , кто-то другой, у кого была практически та же проблема, что и у вас (но они получили более полезное сообщение об ошибке, чем вы по какой-то причине).
Редактировать: в вашем коде есть две другие потенциальные проблемы, которые могут вызвать проблемы в реальном коде, но не в этом крошечном примере:
Во-первых, как Используемый русский указал в комментарии , glib c ожидает, что его собственный код инициализации из его crt
будет запущен до того, как код вашего приложения начнет вызывать его функции. Ты был счастливчиком; поскольку вы создали двоичный файл с динамической связью, использование glib c для линкера Dynami c привело к его инициализации для вас. Если бы вы сделали статически связанный бинарный файл, он бы не сработал. Чтобы таким образом не полагаться на динамический компоновщик c, самое простое решение - использовать main
в качестве точки входа вместо _start
, а затем использовать gcc -m32 -o hello hello.o
для связи (обратите внимание, что мы не используем -nostartfiles
больше). Теоретически вы все равно можете использовать ld
напрямую для связи, но это достаточно сложно, чтобы понять, что в принципе нет причин для беспокойства.
Во-вторых, вы неправильно выравниваете стек. Вы должны убедиться, что он выровнен по 16-байтовой границе, прежде чем call
другие функции. В начале _start
(если вы по-прежнему используете это по какой-то причине), стек уже будет выровнен таким образом, так что вам просто нужно его сохранить. В начале main
или любой другой функции 4-байтовый обратный адрес будет сдвинут, поэтому вам понадобится еще sh 12 байт для его выравнивания.
С обоими выше исправления, вот ваш новый hello.asm
:
;hello.asm
global main
extern scanf, printf, exit
section .data
read_name db '%255s', 0
msg db 'Hello, %s', 0
section .text
main:
sub esp, 260 ; the 4 extra bytes here are padding for alignment. If you wanted to get value out of them, you could use %259s instead of %255s now
push esp
push read_name
call scanf
add esp, 8
push esp
push msg
call printf
add esp, 260 ; we pushed 268 bytes so far, but I'm leaving 8 bytes for alignment
push dword 0
call exit
Кроме того, теперь, когда вы используете main
, а не _start
, вы можете просто вернуться из него вместо вызова exit
. Вам просто нужно убедиться, что вы положили указатель стека туда, где он был в начале. Для этого замените все после call printf
следующим:
add esp, 268
xor eax, eax
ret
Последнее замечание: если вам интересно, почему я сделал xor eax, eax
, а не mov eax, 0
, см. Что лучше способ установить регистр в ноль в сборке x86: xor, mov или и? .