C语言中没有main函数生成可执行程序的几种方法
2013-09-13 21:38
363 查看
1、define预处理指令
这种方式很简单,只是简单地将main字符串用宏来代替,或者使用##拼接字符串。示例程序如下:
#include <stdio.h>
#define begin main
int begin(void)
{
printf(
"Hello, World!\n");
return
0;
}
#include <stdio.h>
#define begin m##a##i##n
int begin(void)
{
printf(
"Hello, World!\n");
return
0;
}
#include <stdio.h>
#include <stdlib.h>
_start(void) {
printf(
"Hello, World!\n");
exit(
0);
}
编译上面的程序的命令为:
gcc
-nostartfiles _start.c
-o a.out
反汇编生成的可执行程序,如下所示:
a.out
: file format elf64
-x86
-
64
Disassembly of section .plt
:
0000000000400320
<puts@plt
-0x10
>
:
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
<puts@plt
>
:
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
<puts@plt
-0x10
>
0000000000400340
<
exit@plt
>
:
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
<puts@plt
-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
<puts@plt
>
40035e
: bf 00 00 00 00 mov $0x0,
%edi
400363
: e8 d8 ff ff ff callq
400340
exit@plt
上面的结果是完整的反汇编结果,我们可以看到_start函数中只有我们调用printf和exit函数相关的一些指令,并且.txt段中只有_start函数,没有看到main函数。如果将源代码中的_start替换为main,重新编译程序,反汇编的结果中会看到_start函数会调用到main。
另外还有一点需要注意,因为这里重写了_start函数,所以gcc为默认的main函数准备的清理动作就没用上,所以如果退出的时候直接使用return,会导致程序崩溃。所以这里要使用exit()来退出程序。具体的原因可以参见这篇文章。
3、gcc的-e选项
示例程序如下:
#include <stdio.h>
#include <stdlib.h>
int nomain(int i, int j, int k) {
printf(
"Hello, World!\n");
exit(
0);
}
将上面的程序保存为m.c,编译命令如下所示:
gcc
-nostartfiles
-e nomain m.c
-o a.out
继续使用objdump反汇编生成的可执行程序,结果如下:
a.out
: file format elf64
-x86
-
64
Disassembly of section .plt
:
0000000000400320
<puts@plt
-0x10
>
:
400320
: ff
35 f2
01
20 00 pushq 0x2001f2(
%rip)
# 600518 <_GLOBAL_OFFSET_TABLE_+0x8>
400326
: ff
25 f4
01
20 00 jmpq
*0x2001f4(
%rip)
# 600520 <_GLOBAL_OFFSET_TABLE_+0x10>
40032c
: 0f
1f
40 00 nopl 0x0(
%rax)
0000000000400330
<puts@plt
>
:
400330
: ff
25 f2
01
20 00 jmpq
*0x2001f2(
%rip)
# 600528 <_GLOBAL_OFFSET_TABLE_+0x18>
400336
:
68 00 00 00 00 pushq $0x0
40033b
: e9 e0 ff ff ff jmpq
400320
<puts@plt
-0x10
>
0000000000400340
<
exit@plt
>
:
400340
: ff
25 ea
01
20 00 jmpq
*0x2001ea(
%rip)
# 600530 <_GLOBAL_OFFSET_TABLE_+0x20>
400346
:
68
01 00 00 00 pushq $0x1
40034b
: e9 d0 ff ff ff jmpq
400320
<puts@plt
-0x10
>
Disassembly of section .text
:
0000000000400350
<nomain
>
:
400350
:
55 push
%rbp
400351
:
48
89 e5 mov
%rsp,
%rbp
400354
:
48
83 ec
10 sub $0x10,
%rsp
400358
:
89
7d fc mov
%edi,
-0x4(
%rbp)
40035b
:
89
75 f8 mov
%esi,
-0x8(
%rbp)
40035e
:
89
55 f4 mov
%edx,
-0xc(
%rbp)
400361
: bf
75
03
40 00 mov $0x400375,
%edi
400366
: e8 c5 ff ff ff callq
400330
<puts@plt
>
40036b
: bf 00 00 00 00 mov $0x0,
%edi
400370
: e8 cb ff ff ff callq
400340
<
exit@plt
>
从上面我们可以看到指定的nomain函数位于.text段的开始位置,同样在函数结束的时候没有gcc为main函数准备的清理动作,所以在这里也只能使用exit()来退出程序,而不能使用return。
4、nostartfiles选项
前面已经多次使用了该选项,不过都是配合其他选项使用的,这个选项也可以单独使用,其含义为"Do not use the standard system startup files when linking"。
示例程序如下:
#include <stdio.h>
#include <stdlib.h>
void func() {
printf(
"I am func....\n");
}
int nomain1(int i, int j, int k) {
func();
printf(
"%s: Hello, World!\n", __func__);
exit(
0);
}
上面的程序保存为k.c,然后使用下面的命令编译:
[root@CentOS_190
~]
# gcc -nostartfiles p.c
/usr
/bin
/ld
: warning
: cannot find entry symbol _start; defaulting to
0000000000400398
这种方式很简单,只是简单地将main字符串用宏来代替,或者使用##拼接字符串。示例程序如下:
#include <stdio.h>
#define begin main
int begin(void)
{
printf(
"Hello, World!\n");
return
0;
}
#include <stdio.h>
#define begin m##a##i##n
int begin(void)
{
printf(
"Hello, World!\n");
return
0;
}
严格来说,这种方式只算是一种技巧......
2、_start函数
_start函数是C程序的入口函数,会调用main函数。在调用main函数之前,会先执行_start函数分配必要的资源,然后再调用main函数。但是在用gcc编译程序时可以使用-nostartfiles选项来重写_start函数。示例程序如下:
#include <stdio.h>
#include <stdlib.h>
_start(void) {
printf(
"Hello, World!\n");
exit(
0);
}
编译上面的程序的命令为:
gcc
-nostartfiles _start.c
-o a.out
反汇编生成的可执行程序,如下所示:
a.out
: file format elf64
-x86
-
64
Disassembly of section .plt
:
0000000000400320
<puts@plt
-0x10
>
:
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
<puts@plt
>
:
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
<puts@plt
-0x10
>
0000000000400340
<
exit@plt
>
:
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
<puts@plt
-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
<puts@plt
>
40035e
: bf 00 00 00 00 mov $0x0,
%edi
400363
: e8 d8 ff ff ff callq
400340
exit@plt
上面的结果是完整的反汇编结果,我们可以看到_start函数中只有我们调用printf和exit函数相关的一些指令,并且.txt段中只有_start函数,没有看到main函数。如果将源代码中的_start替换为main,重新编译程序,反汇编的结果中会看到_start函数会调用到main。
另外还有一点需要注意,因为这里重写了_start函数,所以gcc为默认的main函数准备的清理动作就没用上,所以如果退出的时候直接使用return,会导致程序崩溃。所以这里要使用exit()来退出程序。具体的原因可以参见这篇文章。
3、gcc的-e选项
示例程序如下:
#include <stdio.h>
#include <stdlib.h>
int nomain(int i, int j, int k) {
printf(
"Hello, World!\n");
exit(
0);
}
将上面的程序保存为m.c,编译命令如下所示:
gcc
-nostartfiles
-e nomain m.c
-o a.out
继续使用objdump反汇编生成的可执行程序,结果如下:
a.out
: file format elf64
-x86
-
64
Disassembly of section .plt
:
0000000000400320
<puts@plt
-0x10
>
:
400320
: ff
35 f2
01
20 00 pushq 0x2001f2(
%rip)
# 600518 <_GLOBAL_OFFSET_TABLE_+0x8>
400326
: ff
25 f4
01
20 00 jmpq
*0x2001f4(
%rip)
# 600520 <_GLOBAL_OFFSET_TABLE_+0x10>
40032c
: 0f
1f
40 00 nopl 0x0(
%rax)
0000000000400330
<puts@plt
>
:
400330
: ff
25 f2
01
20 00 jmpq
*0x2001f2(
%rip)
# 600528 <_GLOBAL_OFFSET_TABLE_+0x18>
400336
:
68 00 00 00 00 pushq $0x0
40033b
: e9 e0 ff ff ff jmpq
400320
<puts@plt
-0x10
>
0000000000400340
<
exit@plt
>
:
400340
: ff
25 ea
01
20 00 jmpq
*0x2001ea(
%rip)
# 600530 <_GLOBAL_OFFSET_TABLE_+0x20>
400346
:
68
01 00 00 00 pushq $0x1
40034b
: e9 d0 ff ff ff jmpq
400320
<puts@plt
-0x10
>
Disassembly of section .text
:
0000000000400350
<nomain
>
:
400350
:
55 push
%rbp
400351
:
48
89 e5 mov
%rsp,
%rbp
400354
:
48
83 ec
10 sub $0x10,
%rsp
400358
:
89
7d fc mov
%edi,
-0x4(
%rbp)
40035b
:
89
75 f8 mov
%esi,
-0x8(
%rbp)
40035e
:
89
55 f4 mov
%edx,
-0xc(
%rbp)
400361
: bf
75
03
40 00 mov $0x400375,
%edi
400366
: e8 c5 ff ff ff callq
400330
<puts@plt
>
40036b
: bf 00 00 00 00 mov $0x0,
%edi
400370
: e8 cb ff ff ff callq
400340
<
exit@plt
>
从上面我们可以看到指定的nomain函数位于.text段的开始位置,同样在函数结束的时候没有gcc为main函数准备的清理动作,所以在这里也只能使用exit()来退出程序,而不能使用return。
4、nostartfiles选项
前面已经多次使用了该选项,不过都是配合其他选项使用的,这个选项也可以单独使用,其含义为"Do not use the standard system startup files when linking"。
示例程序如下:
#include <stdio.h>
#include <stdlib.h>
void func() {
printf(
"I am func....\n");
}
int nomain1(int i, int j, int k) {
func();
printf(
"%s: Hello, World!\n", __func__);
exit(
0);
}
上面的程序保存为k.c,然后使用下面的命令编译:
[root@CentOS_190
~]
# gcc -nostartfiles p.c
/usr
/bin
/ld
: warning
: cannot find entry symbol _start; defaulting to
0000000000400398
在单独使用nostartfiles选项时会报警告,生成的可执行程序可以执行,但是会产生段错误,去掉对func()函数的调用就不会产生段错误了。将生成的可执行程序反汇编,和使用前面的方法生成可执行程序的反汇编结果比较,发现除了函数名不一样外,没有其他区别,不知道为什么会产生段错误。知道的麻烦告知一声,拜谢!
相关文章推荐
- C语言中没有main函数生成可执行程序的几种方法
- C语言中没有main函数生成可执行程序的几种方法
- C语言中没有main函数生成可执行程序的几种方法
- 语言中没有main函数生成可执行程序的几种方法
- 没有main函数生成可执行程序的几种方法 http://www.linuxidc.com/Linux/2013-09/90061.htm
- android 没有main函数,怎么找到程序执行入口呢?以及activity主要生命周期的方法说明
- android 没有main函数,怎么找到程序执行入口呢?以及activity主要生命周期的方法说明
- android 没有main函数,怎么找到程序执行入口呢?以及activity主要生命周期的方法说明
- win7 win8计算机-右键-管理“该文件没有与之关联的程序来执行该操作”问题的解决方法
- C# WinForm获取 当前执行程序路径的几种方法
- vc++生成程序不需要.net运行环境的可以执行exe程序的方法
- IOS程序启动时main函数前执行的部分-load方法和静态函数
- 一个简单的执行程序的GNU automake自动生成Makefile的方法及案例
- 一个简单的执行程序的GNU automake自动生成Makefile的方法及案例
- IIS7.5 伪静态 脚本映射 配置方法,添加伪静态后没有为扩展名“”注册的生成提供程序。CSS JS 样式丢失问题
- QT5.6+opencv2.49+vs2015生成的exe可执行程序打包方法
- Linux 程序开发打印 Debug 信息的使用技巧--C语言中几种输出调试信息的方法
- 探讨Windows窗体程序执行周期性任务的几种方法
- FTP 打开文件夹提示"该文件没有程序与之关联来执行该操作 请在控制面板"解决方法
- C# WinForm获取当前执行程序路径的几种方法