如何在没有main()函数的情况下编写一个C程序来打印“Hello world”? 一开始,执行一个没有 main() 因为 main() 函数是任何程序的入口点。
首先,让我们了解在Linux系统中执行C程序时会发生什么,如何调用main(),以及如何在没有main()的情况下执行程序。
本演示将考虑以下设置。
- Ubuntu 16.4 LTS操作系统
- GCC 5.4.0编译器
- objdump实用程序
从C/C++编程的角度来看,程序的入口点是main()函数。然而,从程序执行的角度来看,情况并非如此。在执行流到达main()之前,很少调用其他函数,这些函数用于设置参数、为程序执行准备环境变量等。
编译C源代码后创建的可执行文件是 可执行和可链接格式(ELF)文件 . 每个ELF文件都有一个ELF头,其中有一个 e_条目 包含程序内存地址的字段,可执行文件将从该地址开始执行。这个内存地址指向 _开始() 作用 加载程序后,loader会查找 e_条目 ELF文件头中的字段。 可执行和可链接格式(ELF) 是UNIX系统中用于可执行文件、目标代码、共享库和核心转储的通用标准文件格式。
让我们用一个例子来看一下。我在创造一个 实例C 文件来演示这一点。
int main() { return (0); } |
现在使用以下命令编译
gcc -o example example.c
现在 实例 创建可执行文件后,让我们使用objdump实用程序来检查这一点
objdump -f example
这将在我的机器上输出以下可执行文件的关键信息。看看下面的起始地址,这是指向_start()函数的地址。
example: file format elf64-x86-64 architecture: i386:x86-64, flags 0x00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0x00000000004003e0
我们可以通过解汇编可执行文件来交叉检查这个地址,输出很长,所以我只是粘贴显示这个地址的输出 0x00000000004003e0 指的是
objdump --disassemble example
输出:
00000000004003e0 <_start>: 4003e0: 31 ed xor %ebp,%ebp 4003e2: 49 89 d1 mov %rdx,%r9 4003e5: 5e pop %rsi 4003e6: 48 89 e2 mov %rsp,%rdx 4003e9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 4003ed: 50 push %rax 4003ee: 54 push %rsp 4003ef: 49 c7 c0 60 05 40 00 mov $0x400560,%r8 4003f6: 48 c7 c1 f0 04 40 00 mov $0x4004f0,%rcx 4003fd: 48 c7 c7 d6 04 40 00 mov $0x4004d6,%rdi 400404: e8 b7 ff ff ff callq 4003c0 400409: f4 hlt 40040a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
我们可以清楚地看到,这是指向_start()函数的。
_start()函数的作用
函数_start()为另一个函数准备输入参数 _libc_start_main() 下一个将被称为。这是我的原型 _libc_start_main() 作用这里我们可以看到由_start()函数准备的参数。
int __libc_start_main( int (*main) ( int , char * *, char * *), /* address of main function*/ int argc, /* number of command line args*/ char ** ubp_av, /* command line arg array*/ void (*init) ( void ), /* address of init function*/ void (*fini) ( void ), /* address of fini function*/ void (*rtld_fini) ( void ), /* address of dynamic linker fini function */ void (* stack_end) /* end of the stack address*/ ); |
_libc_start_main()函数的作用
_libc_start_main()函数的作用如下——
- 为程序执行准备环境变量
- 电话 _init() 函数,它在main()函数启动之前执行初始化。
- 登记 _菲尼() 和 _rtld_fini() 函数在程序终止后执行清理
- 完成所有先决条件操作后,_libc_start_main()调用main()函数。
在没有main()的情况下编写程序
现在我们知道如何调用main()。为了说明这一点,main()只不过是启动代码的一个约定术语。我们可以给启动代码起任何名字,它不一定是“main”。由于_start()函数在默认情况下调用main(),如果我们想执行自定义启动代码,就必须更改它。我们可以重写_start()函数,使其调用自定义启动代码not main()。让我们举个例子,另存为 诺曼。C –
#include<stdio.h>
#include<stdlib.h>
void
_start()
{
int
x = my_fun();
//calling custom main function
exit
(x);
}
int
my_fun()
// our custom main function
{
printf
(
"Hello world!"
);
return
0;
}
现在我们必须强制编译器不要使用它自己的_start()实现。在GCC中,我们可以使用 -nostartfiles
gcc -nostartfiles -o nomain nomain.c
执行nomain中的可执行文件
./nomain
输出:
Hello world!
工具书类
- http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html
- Milan Stevanovic编写的高级C/C++程序
本文由 阿图尔·库马尔 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 贡献极客。组织 或者把你的文章寄到contribute@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。
如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。