歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C語言中沒有main函數生成可執行程序的幾種方法

C語言中沒有main函數生成可執行程序的幾種方法

日期:2017/3/1 9:53:11   编辑:Linux編程

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;
}
嚴格來說,這種方式只算是一種技巧......
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()函數的調用就不會產生段錯誤了。將生成的可執行程序反匯編,和使用前面的方法生成可執行程序的反匯編結果比較,發現除了函數名不一樣外,沒有其他區別,不知道為什麼會產生段錯誤。知道的麻煩告知一聲,拜謝!

Copyright © Linux教程網 All Rights Reserved