刑天
刑天,是中国远古神话传说人物,手使一柄巨斧和盾牌,身强力壮,体型巨大的上古巨人,炎帝手下大将,和黄帝争位,被斩去头颅,失了首级后,以双乳为眼,肚脐为口,再战黄帝。
刑天没有了头仍然可以战斗,程序没有了 main 函数,还能跑吗?
答案是:可以的。
代码
nomain.c
#include
#include
void nomain()
{
printf("hello world\n");
exit(0);
}
编译
$ gcc -nostartfiles nomain.c -o nomain.out
/usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050
忽略警告
-nostartfiles 选项是让链接器在链接时不使用标准启动文件
运行
$ ./nomain.out
hello world
探索
我们使用 -s 参数将 c 程序编译成汇编,一探究竟
$ gcc -s -nostartfiles nomain.c
liyongjun@box:~/project/c/c_study/others/ld$ cat nomain.s
.file "nomain.c"
.text
.section .rodata
.lc0:
.string "hello world"
.text
.globl nomain
.type nomain, @function
nomain:
.lfb6:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .lc0(%rip), %rdi
call puts@plt
movl $0, �i
call exit@plt
.cfi_endproc
...
可以看到程序的入口点确实是 nomain,所以我们的程序可以正常运行。
疑问
如果代码里有两个函数,程序该选择哪个作为入口呢?
#include
#include
void nomain()
{
printf("hello world\n");
exit(0);
}
void nomain_2()
{
printf("hello world 2\n");
exit(0);
}
$ gcc -nostartfiles nomain.c -o nomain.out
/usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050
$ ./nomain.out
hello world
从执行的结果可以看到程序选择了 nomain() 函数作为了程序入口点,看下汇编的内容:
$ gcc -s -nostartfiles nomain.c
liyongjun@box:~/project/c/c_study/others/ld$ cat nomain.s
.file "nomain.c"
.text
.section .rodata
.lc0:
.string "hello world"
.text
.globl nomain
.type nomain, @function
nomain:
.lfb6:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .lc0(%rip), %rdi
call puts@plt
movl $0, �i
call exit@plt
.cfi_endproc
.lfe6:
.size nomain, .-nomain
.section .rodata
.lc1:
.string "hello world 2"
.text
.globl nomain_2
.type nomain_2, @function
nomain_2:
.lfb7:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .lc1(%rip), %rdi
call puts@plt
movl $0, �i
call exit@plt
.cfi_endproc
...
显然,在 c 程序中,nomain 函数在 nomain_2 函数之前,编译成汇编后,nomain 依然在前面,就被选择作为了程序的入口点。如果我们把 nomain_2 写在前面,那么 nomain_2 就会被选为函数的入口点,验证如下:
#include
#include
void nomain_2()
{
printf("hello world 2\n");
exit(0);
}
void nomain()
{
printf("hello world\n");
exit(0);
}
$ gcc -nostartfiles nomain.c -o nomain.out
/usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050
$ ./nomain.out
hello world 2
指定
在不改变代码的情况下,我们可以使用 gcc 的 -e 选项来指定程序的入口函数
#include
#include
void nomain_2()
{
printf("hello world 2\n");
exit(0);
}
void nomain_3()
{
printf("hello world 3\n");
exit(0);
}
void nomain()
{
printf("hello world\n");
exit(0);
}
$ gcc -nostartfiles -e nomain_3 nomain.c -o nomain.out
$ ./nomain.out
hello world 3