先看一段程序
#includevoid test() { printf("Hello Word!\n"); return 0; }
没有main函数,编译一定不会通过,在gcc下编译会提示以下信息:
/usr/lib/gcc/i686-linux-gnu/4.7/../../../i386-linux-gnu/crt1.o:在函数‘_start’中:
(.text+0x18):对‘main’未定义的引用
collect2: 错误: ld 返回 1
可以看到错误信息提示,提到了一个“crt1.o”这个文件,其中crt是“C runtime library”的缩写,其含义是“C运时库”。
C运行时库除了给我们提供必要的库函数调用(如memcpy、printf、malloc等)之外,它提供的另一个最重要的功能是为应用程序添加启动函数。C运行时库启动函数的主要功能为进行程序的初始化,对全局变量进行赋初值,加载用户程序的入口函数。
从给出的错误提示信息中还可以得知,在crt1.o中,有一个名为“_start”的函数。现在网上找到一份crt1.o的伪代码:
section .text:
__start:
:
init stack;
init heap;
open stdin;
open stdout;
open stderr;
:
push argv;
push argc;
call _main; (调用 main)
:
destory heap;
close stdin;
close stdout;
close stderr;
:
call __exit;
从伪代码可以看出,在这个_start函数中,调用了main函数。那么我们可不可以用什么手段,去修改入口函数,把入口函数改成test函数呢?
gcc test.c -etest -nostartfiles
其中-e选项为修改函数的入口地址,可惜在网上和man手册里都没有找到有关-e这个选项的解释,只搜索到了-E这个选项。问了好多人才明白原来-e指的 就是entrance。这里把entrance指定为test函数。
-nostartfiles选项的作用是通知编译器不自动加入启动函数以及别的库级 别的初始化,这样就不会调用到crt1.o中的_start函数。
此时,编译通过。可是执行程序,可以成功打印出Hello Word!字样,但是却会提示“段错误”。
Hello Word!
段错误
原来,在编译的时候加上了-nostartfiles这个选项的同时,使得最后的return语句不能正常执行。解决方案是,把程序最后的return语句改成exit语句,让exit语句来做最后的“善后工作”。
#includevoid test() { printf("Hello Word\n"); exit(0); }
没有任何错误。
还有两种方法。。。
方法一:
#include#define start main int start() { printf("Hello Word\n"); return 0; }
#include#define start m##a##i##n int start() { printf("Hello Word\n"); return 0; }
方法二:
_start函数
_start函数是C程序的入口函数,会调用main函数。在调用main函数之前,会先执行_start函数分配必要的资源,然后再调用main函数。但是在用gcc编译程序时可以使用-nostartfiles选项来重写_start函数。示例程序如下:
#include#include _start(void) { printf("Hello Word\n"); exit(0); }
编译指令:
gcc -nostartfiles _start.c -o main.out
反汇编生成的执行程序
a.out: file format elf64-x86-64 Disassembly of section .plt: 00000000004003200x10>: 400320: ff 35 ea 01 20 00 pushq 0x2001ea(%rip) # 600510 <_GLOBAL_OFFSET_TABLE_+0x8> 400326: ff 25 ec 01 20 00 jmpq *0x2001ec(%rip) # 600518 <_GLOBAL_OFFSET_TABLE_+0x10> 40032c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000400330 : 400330: ff 25 ea 01 20 00 jmpq *0x2001ea(%rip) # 600520 <_GLOBAL_OFFSET_TABLE_+0x18> 400336: 68 00 00 00 00 pushq $0x0 40033b: e9 e0 ff ff ff jmpq 400320 0x10> 0000000000400340 : 400340: ff 25 e2 01 20 00 jmpq *0x2001e2(%rip) # 600528 <_GLOBAL_OFFSET_TABLE_+0x20> 400346: 68 01 00 00 00 pushq $0x1 40034b: e9 d0 ff ff ff jmpq 400320 0x10> Disassembly of section .text: 0000000000400350 <_start>: 400350: 55 push %rbp 400351: 48 89 e5 mov %rsp,%rbp 400354: bf 68 03 40 00 mov $0x400368,%edi 400359: e8 d2 ff ff ff callq 400330 40035e: bf 00 00 00 00 mov $0x0,%edi 400363: e8 d8 ff ff ff callq 400340 exit@plt