2011-12-31 45 views
9

Tôi gặp sự cố khi liên kết 2 tệp đối tượng trong đó một tệp được tạo từ Tệp nguồn ngôn ngữ hội và tệp khác được tạo từ tệp nguồn C.Làm thế nào để liên kết tệp đối tượng C với tệp đối tượng Ngôn ngữ hội?

C mã nguồn:

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

mã nguồn hội:

#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 

Khi tôi biên dịch và chạy bằng 'gcc' như thế này:

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

tôi nhận được 5 tương đương với chính xác. Tuy nhiên khi tôi biên dịch/lắp ráp riêng biệt và sau đó liên kết với 'ld' như thế này:

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 

tôi nhận được một lỗi segmentation. Điều gì gây ra điều này? Tôi có không tuân thủ quy ước gọi điện 100% chính xác không?

Trả lời

11

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

Đừng làm điều đó. Làm điều này thay vì:

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

(Bạn nên có lẽ không gọi kiểm tra exectuable bạn test, vì nó có thể xung đột với /bin/test, tiêu chuẩn trên hầu hết các hệ thống UNIX.)

Giải thích: UNIX nhị phân làm không thường bắt đầu thực hiện tại main. Chúng bắt đầu thực hiện trong một hàm có tên là _start, xuất phát từ crt1.o hoặc tương tự ("Khởi động thời gian chạy C"). Tệp này là một phần của libc và nó sắp xếp cho các lần khởi tạo khác nhau, bắt buộc để khởi động đúng ứng dụng của bạn.

Chương trình của bạn thực sự không yêu cầu bất cứ điều gì từ libc, đó là lý do tại sao bạn có thể liên kết nó với ld.

Tuy nhiên, hãy cân nhắc điều gì xảy ra sau khi bạn trả về số main. Thông thường, mã trong crt1.o sẽ thực thi (tương đương) exit(main(argc, argv));. Vì bạn đã liên kết mà không có crt1.o, không có ai để làm điều đó cuối cùng exit cho bạn, do đó, mã trở lại ... vị trí không xác định và nhanh chóng bị treo.

4

Bạn cũng cần liên kết crt1.o (có thể có tên khác, chứa mã cần thiết cho đến khi main có thể được gọi) và thư viện cần thiết. GCC cũng thường cần phải liên kết đến libgcc.so chứa các hàm trợ giúp cần thiết (ví dụ, khi thực hiện các phép tính 64 bit trên hệ thống 32 bit) cộng với các thư viện hệ thống khác. Ví dụ, trên máy Mac của tôi, nó cũng cần liên kết đến libSystem cũng chứa các hàm C thông thường như printf. Trên Linux, thường là libc.

Lưu ý rằng chương trình của bạn không thể bắt đầu trực tiếp với main (như bạn đang cố gắng thực hiện với ld .. -e main), điểm nhập cần thiết lập một vài điều trước khi gọi hàm C main. Đó là những gì được đề cập trước đây crt1.o đang hoạt động. Tôi đoán lỗi phân đoạn là kết quả của quá trình thiết lập bị thiếu này.

Để xem những gì GCC được chính xác làm trên hệ thống của bạn, hãy gọi:

gcc main2.c strlength.s -m32 -o test -v 
Các vấn đề liên quan