如何使用ld手动运行产生elf可执行文件?

时间:2019-03-06 20:02:44

标签: gcc linker ld

我正在努力弄清生成可执行文件时链接过程的工作方式。为此,我正在阅读Ian Taylor's blog series,但目前还很多,所以我想看看它如何在实践中起作用。

此刻,我生成一些目标文件,并通过gcc将它们链接:

gcc -m32 -o test.o -c test.c
gcc -m32 -o main.o -c main.c
gcc -m32 -o test main.o test.o

如何使用gcc -m32 -o test main.o test.o复制ld阶段?

我已经尝试过非常幼稚:ld -A i386 ./test.o ./main.o

但这会返回这些错误:

ld: i386 architecture of input file `./test.o' is incompatible with i386:x86-64 output
ld: i386 architecture of input file `./main.o' is incompatible with i386:x86-64 output
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
./test.o: In function `print_hello':
test.c:(.text+0xd): undefined reference to `_GLOBAL_OFFSET_TABLE_'
test.c:(.text+0x1e): undefined reference to `puts'
./main.o: In function `main':
main.c:(.text+0x15): undefined reference to `_GLOBAL_OFFSET_TABLE_

我对_start_GLOBAL_OFFSET_TABLE_的丢失感到困惑-gccld提供了哪些附加信息来添加它们?

以下是文件:

main.c

#include "test.h"


void main() 
{
    print_hello();
}

test.h

void print_hello();

test.c

#include <stdio.h>


void print_hello()
{
    puts("Hello, world");
}

1 个答案:

答案 0 :(得分:1)

@sam:因为我是汇编的初学者,所以我不是回答您问题的最佳人选。我知道如何编译程序,但我不太了解所有细节(https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools) 因此,我今年决定尝试了解编译的工作原理,并且我尝试或多或少地执行了几天前的尝试。由于没有人回答,我将公开我所做的事情,但我希望专家会补充我的回答。

简短的回答:建议不要直接使用ld,而应直接使用gcc。然而,正如您所写,了解链接过程的工作方式很有趣。此命令在我的计算机上有效:
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc.so /usr/lib/crti.o /usr/lib/crtn.o

很长的答案:
我如何找到上面的命令?
按照n.m的建议,使用-v选项运行gcc。
gcc -v -m32 -o test main.o test.o

  

...
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 ...(很多   选项和参数)
....

如果使用这些选项和参数(复制和粘贴)运行ld,它应该可以工作。
尝试使用-m elf_i386命令(参见collect2参数)
ld -m elf_i386 test.o main.o

  

ld:警告:找不到条目符号_start; ....

在完整ld命令中使用的目标文件中查找符号_start。
readelf -s /usr/lib/crt1.o(或objdump -t)

  

符号表'.symtab'包含18个条目:
数值:值大小   类型Bind Vis Ndx名称
...
11:00000000 0 FUNC   全局默认值2 _开始

将此对象添加到您的ld命令中:
ld -m elf_i386 test.o main.o /usr/lib/crt1.o

  

...对`__libc_csu_fini'的未定义引用...

在目标文件中查找此新引用。由于-L,-l选项以及某些.so包含其他库,因此知道使用哪个库/目标文件并不是很明显。例如,cat /usr/lib/libc.so。但是,带有--trace选项的ld会有所帮助。尝试以下命令
ld --trace ... (collect2 parameters)
最后,您应该找到
ld -m elf_i386 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc_nonshared.a /lib/libc.so.6 /usr/lib/crti.o
或更短(请参见cat /usr/lib/libc.so )
ld -m elf_i386 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc.so /usr/lib/crti.o
它可以编译,但不能运行(尝试运行./test)。它需要正确的-dynamic-linker选项,因为它是动态链接的ELF可执行文件。 (请参见collect2参数进行查找)
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc.so /usr/lib/crti.o
但是,由于您需要_init和_fini函数的结尾({{3 }})。添加ctrn.o对象。
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc.so /usr/lib/crti.o /usr/lib/crtn.o
./test

  

你好,世界