程式從一堆字元怎麼變成乙個可執行的程式呢,在這我們使用最簡單的乙個hello word 程式來演示程式的乙個完整流程。
在這過程中找到一些有用的除錯方法,幫助我們在debug時快速解決問題。
程式編譯時的整體流程經過:
執行環境
windows 10 + cygwin按照c語言語法規則,將字元組合成一段有特定含義的文字。gcc 版本 7.4.0
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"
塊
在keil 中生成預處理檔案選項如下:# 1 "/usr/include/_ansi.h" 1 3 4
上面一行表示下面的內容是來自/usr/include/_ansi.h
檔案,並且它是乙個系統檔案開頭。在預處理時新增-p引數,可不顯示#line內容。
在對原始檔進行預處理完成後,要對預處理檔案進行編譯,將原始檔進行詞法分析,轉換為組合語言。
在命令列中輸入命令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檔案...