Как связать библиотеку времени выполнения 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