Как связать библиотеку времени выполнения C с "ld"?

Я изучаю сборку с NASM для класса, который у меня есть в колледже. Я хотел бы связать библиотеку времени выполнения C с ld, но я просто не могу обернуться вокруг нее. У меня есть машина 64 bit с Linux Mint.

Причина, по которой я запутался, заключается в том, что, насколько мне известно, вместо связывания C runtime, gcc копирует то, что вам нужно в вашу программу. Возможно, я ошибаюсь, поэтому, не стесняйтесь, поправьте меня на это, пожалуйста.

То, что я сделал до этого момента, - это связать его с помощью gcc. Это создает беспорядок машинного кода, который я не могу выполнить, хотя для небольшой программы, например, для замены rax с rbx, что не так хорошо для обучения. (Обратите внимание, что программа работает.)

Я не уверен, что это актуально, но это команды, которые я использую для компиляции и ссылки:

# compilation
nasm -f elf64 swap.asm
# gcc
gcc -o swap swap.o
# ld, no c runtime
ld -s -o swap swap.o

Заранее благодарю вас!


Вывод:

Теперь, когда у меня есть правильный ответ на вопрос, вот несколько вещей, которые я хотел бы упомянуть. Связывание glibc динамически может быть выполнено, как в ответе Z boson (для 64-битных систем). Если вы хотите сделать это статически, выполните эту ссылку (что я повторно отправляю из ответа Z бозона).

Вот статья, которую опубликовал Jester, о как запускаются программы в Linux.

Чтобы узнать, что делает gcc, чтобы связать ваш .o -s, попробуйте эту команду: gcc -v -o swap swap.o. Обратите внимание, что 'v' означает 'verbose'.

Кроме того, вы должны прочитать это, если вы заинтересованы в 64-битной сборке.

Спасибо за ваши ответы и полезную информацию! Конец речи.

Ответ 1

Вот пример, который использует libc без использования GCC.

extern printf
extern _exit

section .data
    hello:     db 'Hello world!',10

section .text
    global _start   
_start:
    xor eax, eax
    mov edi, hello
    call printf
    mov rax, 0    
    jmp _exit

Компилировать и ссылаться так:

nasm -f elf64 hello.asm
ld hello.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -m elf_x86_64

До сих пор это работало нормально для меня, но для статической связи это сложно.

Ответ 2

Если вы хотите вызвать простые библиотечные функции, такие как atoi, но все же избегайте использования среды выполнения C, вы можете это сделать. (т.е. вы пишете _start, а не просто записываете main, который вызывается после того, как запускается куча кода котельной.)

gcc -o swap -nostartfiles swap.o

Как говорят в комментариях, некоторые части glibc зависят от конструкторов/деструкторов, которые запускаются из стандартных файлов запуска. Вероятно, это так для stdio (puts/printf/scanf/getchar) и, возможно, malloc. Однако многие функции - это "чистые" функции, которые просто обрабатывают ввод, который им предоставляется. sprintf/sscanf может быть в порядке.

Например:

$ cat >exit64.asm  <<EOF
section .text

extern exit

global _start
_start:

    xor edi, edi
    jmp exit            ; doesn't return, so optimize like a tail-call

    ;; or make the syscall directly, if the jmp is commented
    mov eax, 231    ;  exit(0)
    syscall

;   movl eax, 1     ; 32bit call
;   int 0x80
EOF

$ yasm -felf64 exit64.asm && gcc -nostartfiles exit64.o -o exit64-dynamic
$ nm exit64-dynamic
0000000000601020 D __bss_start
0000000000600ec0 d _DYNAMIC
0000000000601020 D _edata
0000000000601020 D _end
                 U [email protected]@GLIBC_2.2.5
0000000000601000 d _GLOBAL_OFFSET_TABLE_
00000000004002d0 T _start
$ ltrace ./exit64-dynamic 
enable_breakpoint pid=11334, addr=0x1, symbol=(null): Input/output error
exit(0 <no return ...>
+++ exited (status 0) +++
$ strace ... # shows the usual system calls by the runtime dynamic linker