如何正确使用简单的链接器脚本?可执行文件在运行时获得 SIGKILL [英] How to correctly use a simple linker script? Executable gets SIGKILL when run

查看:18
本文介绍了如何正确使用简单的链接器脚本?可执行文件在运行时获得 SIGKILL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to understand deeper linking process and linker scripts...looking at binutils doc i found a simple linker script implementation that i've improved by adding some commands:

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

ENTRY(mymain)

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

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

My program is a very simple program:

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

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

Now i tried to build an executable:

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

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

But if i try to run prog it receives a SIGKILL during startup. I know that when a program is compiled and linked with the command:

gcc prog.c -o prog

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

the final executable is the product also of other object files like crt1.o, crti.o and crtn.o but what about my case? Which is the correct way to use this linker scripts?

推荐答案

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

I suspect that your code is running just fine, and getting into trouble at the end: what do you expect to happen after the a++?

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

mymain() is just an ordinary C function, which will try to return to its caller.

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

But you've set it as the ELF entry point, which tells the ELF loader to jump to it once it has loaded the program segments in the right place - and it doesn't expect you to return.

那些其他对象文件,如 crt1.ocrti.ocrtn.o"通常为 C 程序处理这些内容.C 程序的 ELF 入口点不是 main() - 相反,它是一个为 main() 设置适当环境的包装器(例如,设置 main()code>argc 和 argv 参数在堆栈或寄存器中,取决于平台),调用 main() (期望它可能返回),然后调用 exit 系统调用(使用 main() 的返回码).

Those "other object files like crt1.o, crti.o and crtn.o" normally handle this stuff for C programs. The ELF entry point for a C program isn't main() - instead, it's a wrapper which sets up an appropriate environment for main() (e.g. setting up the argc and argv arguments on the stack or in registers, depending on platform), calls main() (with the expectation that it may return), and then invokes the exit system call (with the return code from main()).

[更新以下评论:]

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

When I try your example with gdb, I see that it does indeed fail on returning from mymain(): after setting a breakpoint on mymain, and then stepping through instructions, I see that it performs the increment, then gets into trouble in the function epilogue:

$ 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 运行时的库.


For i386 at least, the ELF loader sets up a sensible stack before entering the loaded code, so you can set the ELF entry point to a C function and get reasonable behaviour; however, as I mentioned above, you have to handle a clean process exit yourself. And if you're not using the C runtime, you'd better not be using any libraries that depend on the C runtime either.

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

So here is an example of that, using your original linker script - but with the C code modified to initialise a to a known value, and invoke an exit system call (using inline assembly) with the final value of a as the exit code. (Note: I've just realised that you haven't said exactly what platform you're using; I'm assuming Linux here.)

$ 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
$ 

这篇关于如何正确使用简单的链接器脚本?可执行文件在运行时获得 SIGKILL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆