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

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

问题描述

我试图理解更深层的链接过程和链接器脚本......看着binutils doc,我发现了一个简单的链接器脚本实现,我通过添加了一些命令来改进它:

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.o crti.o crtn.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.o crti.o crtn.o 通常为C程序处理这些东西。 C程序的ELF入口点不是 main() - 相反,它是一个为 main()设置合适环境的包装器(例如,在堆栈或寄存器中设置 argc argv 参数,具体取决于平台),调用 main()(期望它可以返回),然后调用出口系统调用(来自 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()返回时确实失败,然后逐步执行指令,我发现它执行增量,然后在函数epilogue中遇到麻烦:

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 初始化为已知值,并调用出口系统调用(使用内联汇编),最终值为 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天全站免登陆