1. 程式人生 > >C語言中沒有main函式生成可執行程式的幾種方法

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

轉自:http://www.linuxidc.com/Linux/2013-09/90061.htm

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 <[email protected]>:
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 <[email protected]>:
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 <[email protected]>

0000000000400340 <[email protected]>:
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 <

[email protected]>

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 <[email protected]>
40035e: bf 00 00 00 00 mov $0x0,%edi
400363: e8 d8 ff ff ff callq 400340 [email protected] 
上面的結果是完整的反彙編結果,我們可以看到_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 <[email protected]>:
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 <[email protected]>:
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 <[email protected]>

0000000000400340 <[email protected]>:
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 <[email protected]>

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 <[email protected]>
40036b: bf 00 00 00 00 mov $0x0,%edi
400370: e8 cb ff ff ff callq 400340 <[email protected]>
從上面我們可以看到指定的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,然後使用下面的命令編譯:
[[email protected]CentOS_190 ~]# gcc -nostartfiles p.c
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400398
在單獨使用nostartfiles選項時會報警告,生成的可執行程式可以執行,但是會產生段錯誤,去掉對func()函式的呼叫就不會產生段錯誤了。將生成的可執行程式反彙編,和使用前面的方法生成可執行程式的反彙編結果比較,發現除了函式名不一樣外,沒有其他區別,不知道為什麼會產生段錯誤。知道的麻煩告知一聲,拜謝!

相關閱讀:

相關推薦

C語言沒有main函式生成執行程式方法

轉自:http://www.linuxidc.com/Linux/2013-09/90061.htm 1、define預處理指令 這種方式很簡單,只是簡單地將main字串用巨集來代替,或者使用##拼接字串。示例程式如下: #include <stdio.h>

C語言筆記19--main函式的引數

main函式有兩個引數,一般的寫法為int main(int argc,char *argv[])或者int(int argc,char **argv)。第一個引數是說明main函式有多少個引數,這個值至少為1,因為在執行exe檔案時,檔名會作為main函式的一個引數。char *argv[]是一個

c++可以把任意基本型別轉換為string, 類似於c 語言的 sprintf函式

//c++中按照格式輸入輸出 類似於c 語言中的 sprintf函式 #include<iostream> #include<sstream>//std::stringstream 標頭檔案 int main() { std::string str = "高海文"

C語言變數和函式的宣告與定義

一、變數在將變數前,先解釋一下宣告和定義這兩個概念。宣告一個變數意味著向編譯器描述變數的型別,但並不為變數分配儲存空間。定義一個變數意味著在宣告變數的同時還要為變數分配儲存空間。在定義一個變數的同時還可以對變數進行初始化。 區域性變數通常只定義不宣告,而全域性變數多在原始檔中定義,在標頭檔案中宣告。 區域性變

C語言返回字串函式的四實現方法

其實就是要返回一個有效的指標,尾部變數退出後就無效了。 使用分配的記憶體,地址是有效 char   *fun() {         char*   s   =   (char*)calloc(100,   sizeof(char*)   );         if   (s)                

C語言沒有定義bool型別

今天才發現C語言中原來沒有定義bool型別。 如果要在C語言中使用bool型別,可以自己定義。 定義方式如下: #ifndef __cplusplus typedef char bool; #define false 0 #define true 1 #endif

C語言沒有String型別

C 是靜態弱型別語言。意味著型別(包括 size 等資訊,你使用 sizeof 就是編譯時候得到的)在編譯的時候就能確定下來。 它的原生簡單型別有 char int float 這樣的,原生複合型別有 struct,以及“偉大”的 指標型別 primitive_type * . 這些型別的設計是在編譯時可以

關於C語言一些常用函式的說明

1.I/O函式 (1)scanf函式: int scanf(const char *format…..); 從標準輸入流stdin中按格式format將資料寫到引數表中;若操作成功,返回寫到引數表中的引數個數,否則返回EOF; 注意以下幾點: ①scanf函式沒

C語言使用靜態函式的好處

          靜態函式會被自動分配在一個一直使用的儲存區,直到退出應用程式例項,避免了呼叫函式時壓棧出棧,速度快很多。 關鍵字“static”,譯成中文就是“靜態的”,所以內部函式又稱靜態函式。但此處“static”的含義不是指儲存方式,而是指對函式的作用域僅侷限於本

C語言有關外部函式呼叫的問題

首先指出一點,我們通常所說的編譯器並非僅指編譯器,確切來說是編譯工具鏈,裡面包括了預編譯器、編譯器、彙編器和聯結器。 對於外部函式實體(處於呼叫函式所在原始檔之外的其他原始檔中的函式),是在連結過程中,才會被尋找和新增程序序,一旦沒有找到函式實體,就會報錯,無法成功連結。

C語言常用的函式

字串函式     複製函式strcpy()  格式 strcpy(字串陣列名,"字串");     連線函式strcat(字串陣列1,字串陣列2)   格式  strcat(字串1,字串2);     比較函式strcmp()  格式 strcmp(字串1,字串2);  

C語言中static的作用及C語言使用靜態函式有何好處

在C語言中,static的字面意思很容易把我們匯入歧途,其實它的作用有三條,分別是: 一是隱藏功能,對於static修飾的函式和全域性變數而言 二是保持永續性功能,對於static修飾的區域性變數而言。 三是因為存放在靜態區,全域性和區域性的static修飾的變數,都預設初始化為0 下面我逐一給

C語言檔案操作函式彙總

#include <stdio.h> #include <stdlib.h> int main() { FILE* fd = fopen("test.txt","r"); if(NULL == fd)//檔案開啟失敗 { perror("fope

C語言的內部函式與外部函式

內部函式:   如果一個函式只能被本檔案中其它函式所呼叫,它稱為內部函式。在定義內部函式時,在函式名和函式型別的前面加static。即   static 型別識別符號 函式名 (形參表) 如: static int fun (int a, int b) 內部函式又稱靜態函

C語言:在main函式之前和之後執行函式

前段時間,需要寫個簡單的用C語言實現的輕量級httpd服務,想實現在新增api的時候,只要在Makefile中新增對應的api C檔案,就能做到將此api加入到api列表以提供響應的功能,類似於linux的驅動程式編寫,在編寫驅動的時候,只要編譯我們寫的C程式,

C語言的子函式和主函式有什麼聯絡啊?它們是怎麼編寫的?

函式定義的一般形式1.無參函式的一般形式 型別說明符 函式名() { 型別說明 語句 }  其中型別說明符和函式名稱為函式頭。 型別說明符指明瞭本函式的型別,函式的型別實際上是函式返回值的型別。 該型別說明符與第二章介紹的各種說明符相同。 函式名是由使用者定義的識別符號,函式名後有一個空括號,其中無引數,但括

C語言的靜態函式的作用

轉載 在C語言中為什麼要用靜態函式(static function)? 如果不用這個static關鍵字,好象沒有關係。那麼,用了static以後,有什麼作用呢? 我們知道,用了static的變數,叫做靜態變數,其意義是,該變數的值在下次呼叫時,還繼續保留前次呼叫時的值。

C語言通過sprintf()函式構造sql語句

一、C語言如何構造sql 做專案時,由嵌入式開發慢慢涉及到後臺開發,接觸資料庫慢慢就多了,一般情況下,sql定義成一個char *,或者一個字元陣列,裡面就寫sql語句就行了。例如: char *sql; sql = "create ta

沒有main函式時的Java程式執行

Java初學者大都是從main函式在控制檯列印HelloWorld來開始Java學習的, 其形式為:public static void main(String[ ] arg)。 首先,main方法是JVM(java虛擬機器)自動呼叫,JVM呼叫main方法的位置自然不會在某個類中、或某個包中,

C++函式返回陣列指標的方法總結

因為陣列不能被拷貝,所以函式不能返回陣列。不過,函式可以返回陣列的指標或引用。返回陣列指標的方法有以下幾種:     1、方法一:使用類型別名。如下         typedef int arrt[10];//arrT是一個類型別名,它表示的型別是含有10個整數的陣列