從編碼到執行 程式編譯過程詳解

2021-10-11 18:38:59 字數 4364 閱讀 5057

程式從一堆字元怎麼變成乙個可執行的程式呢,在這我們使用最簡單的乙個hello word 程式來演示程式的乙個完整流程。

在這過程中找到一些有用的除錯方法,幫助我們在debug時快速解決問題。

程式編譯時的整體流程經過:

執行環境

windows 10 + cygwin

gcc 版本 7.4.0

按照c語言語法規則,將字元組合成一段有特定含義的文字。

main.c

#include

char hello=

;int

main

(void

)

預處理主要是將原始檔中的預處理指令(#define, #include)展開、替換或者對條件編譯選項進行選擇刪除。

在gcc中與預處理有關常用的檢視指令有:

在命令列中輸入gcc -e maic.c -o main.i生成預處理後的檔案:

`#` 1 "main.c"

`#` 1 ""

`#` 1 "《命令列》"

`#` 1 "main.c"

`#` 1 "/usr/include/stdio.h" 1 3 4

`#` 29 "/usr/include/stdio.h" 3 4

`#` 1 "/usr/include/_ansi.h" 1 3 4

... 省略若干 ...

`#` 41 "/usr/include/machine/_default_types.h" 3 4

typedef signed char __int8_t;

typedef unsigned char __uint8_t;

`#` 55 "/usr/include/machine/_default_types.h" 3 4

typedef short int __int16_t;

typedef short unsigned int __uint16_t;

`#` 77 "/usr/include/machine/_default_types.h" 3 4

typedef int __int32_t;

... 省略若干 ...

`#` 797 "/usr/include/stdio.h" 3 4

`#` 2 "main.c" 2

`#` 3 "main.c"

char hello=;

int main(void)

在main.i中它將所有巨集和標頭檔案全部展開,該選項在除錯巨集有個**十分有用。

在上面的檔案中除了我們已知的巨集替換外,還有很多以#開頭的行,這些是標頭檔案替換的位置資訊。

其語法格式為:

#行號 檔名 屬性標識

標識含義1

乙個新檔案的開始

2表示從乙個被包含的檔案中返回

3表示後面的內容來自系統檔案

4表示後面的內容應當被當作乙個隱式的extern "c"

# 1 "/usr/include/_ansi.h" 1 3 4上面一行表示下面的內容是來自/usr/include/_ansi.h檔案,並且它是乙個系統檔案開頭。

在預處理時新增-p引數,可不顯示#line內容。

在keil 中生成預處理檔案選項如下:

在對原始檔進行預處理完成後,要對預處理檔案進行編譯,將原始檔進行詞法分析,轉換為組合語言。

在命令列中輸入命令gcc main.i -s -o main.s生成彙編檔案

展開檢視main.i

.file	"main.c"

.text

.globl hello

.data

.align 8

hello:

.ascii "hello gcc\0"

.def __main; .scl 2; .type 32; .endef

.section .rdata,"dr"

.lc0:

.ascii "str:%s,ptr:%p,size:%d\12\0"

.text

.globl main

.def main; .scl 2; .type 32; .endef

.seh_proc main

main:

pushq %rbp

.seh_pushreg %rbp

movq %rsp, %rbp

.seh_setframe %rbp, 0

subq $32, %rsp

.seh_stackalloc 32

.seh_endprologue

call __main

movl $10, %r9d

leaq hello(%rip), %r8

leaq hello(%rip), %rdx

leaq .lc0(%rip), %rcx

call printf

movl $0, %eax

addq $32, %rsp

popq %rbp

ret.seh_endproc

.ident "gcc: (gnu) 7.4.0"

.def printf; .scl 2; .type 32; .endef

使用編譯命令將原始檔編譯為彙編檔案後,可比較彙編的結果,針對某些**進行優化使用-c引數可在命令列中只進行彙編成.o檔案而不生成可執行檔案

在命令列中輸入命令gcc main.s -c -o main.o生成。在目標檔案(*.o)檔案中,包含了該原始檔的函式和資料資訊。

可通過objdump工具檢視其中各個資料段的說明和符號表,或者使用ida等反編譯軟體生成彙編或者c檔案。

下面是使用ida 對main.o反彙編後的結果,其**結構與c原檔案基本一致。

使用gcc中不使用其他引數,只是用-o預設進行鏈結操作。在鏈結過程中會將所有目標檔案、靜態庫、動態庫一起按照指定規則(鏈結指令碼),進行鏈結生成可執行檔案。

在命令列中輸入gcc main.o -wl,-map=main.map -o main.exe生成可執行檔案,並匯出map檔案。

由於在該程式中只使用了系統庫,而沒有使用外部庫,所以沒有指定動態/靜態庫名和搜尋路徑。

-l:指定待鏈結的庫名。

-l:指定鏈結庫的查詢路徑。

-wl,-map=file.map:生成map檔案,其中包含了符號表和位址對映等資訊。

在命令列中輸入./main.exe執行程式。輸出結果如下:

$ ./main.exe

str:hello gcc,ptr:0x100402010,size:10

開啟main.map,可看到hello字串的變數位址和變數大小。與列印的值一致。

.data          0x0000000100402000       0x10 /usr/lib/gcc/x86_64-pc-cygwin/7.4.0/crtbegin.o

0x0000000100402000 __dso_handle

.data 0x0000000100402010 0x10 main.o

0x0000000100402010 hello

.data 0x0000000100402020 0x0 /usr/lib/gcc/x86_64-pc-cygwin/7.4.0/../../../../lib/libcygwin.a(cygwin_crt0.o)

.data 0x0000000100402020 0x0 /usr/lib/gcc/x86_64-pc-cygwin/7.4.0/../../../../lib/libcygwin.a(premain0.o)

C語言從編譯到執行過程詳解

目錄 最近在看csapp 深入理解計算機系統 然後以前也學過c語言,但是從來沒有深究寫好的c 是怎麼編譯再到執行的。所以現在自己學習,然後記錄下來。以最常用的hello world!程式為例 程式名 main.c include int main hel程式設計客棧lo程式的生命週期是從乙個高階c語...

C C 從原始碼到可執行程式的過程

1.預處理 將巨集 條件編譯指令 標頭檔案包含等指令進行展開。也就是說,這是乙個 替換的工作。c c 原始碼經預處理後,不再存在各種巨集指令。但展開後依然是 的形式,是人可讀的。2.編譯 將c c 翻譯成彙編 然後翻譯成平台的機器 最終會生成乙個與cpp檔案同名的目標檔案,其字尾名為.o或者.obj...

關於源程式到可執行程式的過程

源程式,是指未經編譯的,按照一定的程式語言規範書寫的,人類可讀的文字檔案,我們通常理解為源程式就是我們所寫好的 可執行程式,我們常說的.exe程式,可以執行程式,完成計算機功能。在c語言中,c檔案就是所謂的原始檔,接下來,我們剖析一下,源程式到可執行程式的過程。在這個過程中,會發生如下的變化 c檔案...