Как я могу динамически связываться с glib c в Ubuntu - PullRequest
2 голосов
/ 25 апреля 2020

Я пытаюсь собрать и связать этот крошечный код сборки x86 в Linux (Ubuntu 18.04 LTS):

;hello.asm
global _start

extern scanf, printf, exit

section .data
    read_name db '%255s', 0
    msg db 'Hello, %s', 0

section .text
_start:
    sub esp, 256
    push esp
    push read_name
    call scanf
    add esp, 8
    push esp
    push msg
    call printf
    add esp, 264
    push dword 0
    call exit

Я использую nasm для сборки и ld для ссылки. Как вы, вероятно, можете сказать, код использует C функций, поэтому он должен быть связан с glibc. Поскольку мой код использует _start, а не main, я решил, что было бы лучше связать с общей библиотекой, так как для выполнения C требуется некоторый код запуска для запуска в _start, если двоичный файл связан статически.

Проблема в том, что я не могу получить свой код для ссылки, скорее всего потому, что я не использую правильный glib c .so. Вот способ, которым я собираю и связываю:

nasm -f elf32 hello.asm
ld hello.o -o hello -dynamic-linker /lib/libc.so.6 -lc -m elf_i386

Выходной файл создается, однако, когда я пытаюсь запустить его, вот что я получаю:

./hello
bash: ./hello: No such file or directory

Выполнение быстрого поиска получается, что это все lib c .so s на моем компьютере:

locate libc.so
/lib/x86_64-linux-gnu/libc.so.6
/snap/core/8268/lib/i386-linux-gnu/libc.so.6
/snap/core/8268/lib/x86_64-linux-gnu/libc.so.6
/snap/core/8689/lib/i386-linux-gnu/libc.so.6
/snap/core/8689/lib/x86_64-linux-gnu/libc.so.6
/snap/core18/1668/lib/i386-linux-gnu/libc.so.6
/snap/core18/1668/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/libc.so

Может кто-нибудь сказать мне, как сделать ссылку на glib c? (Я тоже получаю ту же проблему для 64-битного кода)

1 Ответ

3 голосов
/ 26 апреля 2020
Линкер по умолчанию для динамического c

ld для i386 - /usr/lib/libc.so.1, что сегодня неправильно в большинстве Linux систем. Вы пытались переопределить это, но путь, который вы указали, тоже был неправильным. Два варианта:

  1. Вручную передайте правильный при ссылке: ld hello.o -o hello -dynamic-linker /lib/ld-linux.so.2 -lc -m elf_i386
  2. Используйте вместо этого 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 или и? .

...