Как связать объектный файл C с объектным файлом на языке ассемблера? - PullRequest
10 голосов
/ 01 января 2012

У меня проблемы со связыванием 2 объектных файлов, один из которых был сгенерирован из исходного файла на языке ассемблера, а другой - из исходного файла C.

Исходный код C:

//main2.c
extern int strlength(char *);
int main(){
    char * test = "hello";
    int num = strlength(test);
    return num;
}

Исходный код сборки:

#strlength.s
.include "Linux32.s"

.section .text
.globl strlength
.type strlength, @function
strlength:
 pushl %ebp
 movl %esp, %ebp
 movl $0, %ecx
 movl 8(%ebp), %edx
read_next_byte:
 movb (%edx), %al
 cmpb $END_OF_FILE, %al
 jle end
 incl %edx
 incl %ecx
 jmp read_next_byte
end:
 movl %ecx, %eax
 popl %ebp
 ret

Когда я компилирую и запускаю, используя 'gcc', вот так:

gcc main2.c strlength.s -m32 -o test
./test
echo $?

Я получаю 5, что правильно.Однако, когда я компилирую / собираю отдельно, а затем связываюсь с 'ld' следующим образом:

as strlength.s --32 -o strlength.o
cc main2.c -m32 -o main2.o
ld -melf_i386 -e main main2.o strlength.o -o test
./test

Я получаю ошибку сегментации.Чем это вызвано?Я не соблюдаю правила вызова C на 100% правильно?

Ответы [ 2 ]

11 голосов
/ 01 января 2012

ld -melf_i386 -e main main2.o strlength.o -o test

Не делай этого.Сделайте это вместо:

gcc -m32 main2.o strlength.o -o test

(Вы, вероятно, не должны называть свой тест возможным test, так как он может конфликтовать со /bin/test, стандартом в большинстве систем UNIX.)

Пояснение: Двоичные файлы UNIX не обычно начинают выполняться с main.Они начинают выполняться в функции с именем _start, которая происходит от crt1.o или аналогичной («Запуск во время выполнения C»). Этот файл является частью libc и обеспечивает различные инициализации, необходимые для правильного запуска вашего приложения.

Ваша программа на самом деле не требует что-либо с libc, поэтому вы смогли связать его с ld.

Однако рассмотрим, что произойдет после того, как ваш main вернется. Обычно , код в crt1.o будет выполняться (эквивалентно) exit(main(argc, argv));.Поскольку вы установили связь без crt1.o, никто не может сделать этот окончательный exit для вас, поэтому код возвращается в ... неопределенное место и быстро падает.

4 голосов
/ 01 января 2012

Вам также необходимо связать crt1.o (может иметь другое имя, содержит необходимый код до вызова main) и необходимые библиотеки. GCC также обычно должен ссылаться на libgcc.so, который содержит необходимые вспомогательные функции (например, при выполнении 64-разрядных вычислений в 32-разрядной системе), а также другие системные библиотеки. Например, на моем Mac также требуется ссылка на libSystem, которая также содержит обычные функции C, такие как printf. В Linux это обычно libc.

Обратите внимание, что ваша программа не может напрямую запускаться с main (как вы пытаетесь это сделать с ld .. -e main), точка входа должна настроить несколько вещей перед вызовом функции C main. Это то, что делает ранее упомянутый crt1.o. Я предполагаю, что ошибка сегментации является результатом этой недостающей установки.

Чтобы увидеть, что GCC точно делает в вашей системе , позвоните:

gcc main2.c strlength.s -m32 -o test -v
...