Как связать объектный файл C с объектным файлом языка ассемблера?

У меня возникли проблемы с связыванием двух объектных файлов, один из которых был создан из исходного файла языка ассемблера, а другой — из исходного файла 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% правильно?


person Hudson Worden    schedule 31.12.2011    source источник


Ответы (2)


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 Runtime"). Этот файл является частью libc и обеспечивает различные инициализации, необходимые для правильного запуска вашего приложения.

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

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

person Employed Russian    schedule 01.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
person DarkDust    schedule 01.01.2012