20

我试图了解更深层次的链接过程和链接器脚本...查看 binutils 文档,我发现了一个简单的链接器脚本实现,我通过添加一些命令对其进行了改进:

OUTPUT_FORMAT("elf32-i386", "elf32-i386",
          "elf32-i386")
OUTPUT_ARCH(i386)

ENTRY(mymain)

SECTIONS
{
   . = 0x10000;
   .text : { *(.text) }
   . = 0x8000000;
   .data : { *(.data) }
   .bss : { *(.bss) }
}

我的程序是一个非常简单的程序:

void mymain(void)
{
  int a;
  a++;
}

现在我尝试构建一个可执行文件:

gcc -c main.c
ld -o prog -T my_script.lds main.o

但是如果我尝试运行它会在启动期间prog收到一个。SIGKILL我知道当一个程序被编译并与命令链接时:

gcc prog.c -o prog

最终的可执行文件也是其他目标文件的产物,crt1.o但我的情况呢?使用此链接器脚本的正确方法是什么?crti.ocrtn.o

4

2 回答 2

23

我怀疑你的代码运行得很好,最后遇到了麻烦:你期望a++?

mymain()只是一个普通的 C 函数,它会尝试返回给它的调用者。

但是你已经将它设置为 ELF 入口点,它告诉 ELF 加载器在将程序段加载到正确的位置后跳转到它——它不希望你返回。

那些“像crt1.o,crti.ocrtn.o”这样的其他目标文件通常会为 C 程序处理这些东西。C 程序的 ELF 入口点不是main()- 相反,它是一个包装器,它为(例如,根据平台在堆栈或寄存器中main()设置argc和参数)、调用(期望它可能返回),然后调用系统调用(返回代码来自)。argvmain()exitmain()


[更新以下评论:]

当我尝试使用您的示例时gdb,我发现它确实在从mymain(): 返回时失败了,在设置断点之后mymain,然后逐步执行指令,我看到它执行了增量,然后在函数尾声中遇到了麻烦:

$ gcc -g -c main.c
$ ld -o prog -T my_script.lds main.o
$ gdb ./prog
...
(gdb) b mymain
Breakpoint 1 at 0x10006: file main.c, line 4.
(gdb) r
Starting program: /tmp/prog 

Breakpoint 1, mymain () at main.c:4
4         a++;
(gdb) display/i $pc
1: x/i $pc
0x10006 <mymain+6>:     addl   $0x1,-0x4(%ebp)
(gdb) si
5       }
1: x/i $pc
0x1000a <mymain+10>:    leave  
(gdb) si
Cannot access memory at address 0x4
(gdb) si
0x00000001 in ?? ()
1: x/i $pc
Disabling display 1 to avoid infinite recursion.
0x1:    Cannot access memory at address 0x1
(gdb) q

至少对于 i386,ELF 加载器在进入加载的代码之前设置了一个合理的堆栈,因此您可以将 ELF 入口点设置为 C 函数并获得合理的行为;但是,正如我上面提到的,您必须自己处理干净的进程退出。如果你不使用 C 运行时,你最好不要使用任何依赖于 C 运行时的库。

因此,这里有一个示例,使用您的原始链接器脚本 - 但修改了 C 代码以初始化a为已知值,并调用exit系统调用(使用内联汇编),最终值为a退出代码。(注意:我刚刚意识到您并没有确切说明您使用的是什么平台;我假设这里是 Linux。)

$ cat main2.c
void mymain(void)
{
  int a = 42;
  a++;
  asm volatile("mov $1,%%eax; mov %0,%%ebx; int $0x80" : : "r"(a) : "%eax" );
}
$ gcc -c main2.c
$ ld -o prog2 -T my_script.lds main2.o
$ ./prog2 ; echo $?
43
$ 
于 2011-08-24T23:59:30.560 回答
2

是的,要在 linux 上运行,我们需要更改 .lds 文件

SECTIONS
{
   . = 0x8048000;
   .text : { *(.text) 
}
于 2018-07-28T04:15:46.603 回答