我怀疑您的代码运行良好,但最终遇到了麻烦:您希望在a++ 之后会发生什么?
mymain() 只是一个普通的 C 函数,它会尝试返回给它的调用者。
但是您已将其设置为 ELF 入口点,这会告诉 ELF 加载器在将程序段加载到正确的位置后跳转到它 - 而且它不希望您返回。
那些“其他目标文件,如crt1.o、crti.o 和crtn.o”通常会为 C 程序处理这些内容。 C 程序的 ELF 入口点不是 main() - 相反,它是为 main() 设置适当环境的包装器(例如,在堆栈或寄存器中设置 argc 和 argv 参数,取决于平台),调用main()(期望它可能返回),然后调用exit系统调用(返回代码来自main())。
[更新以下 cmets:]
当我使用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
$