c01_由一個hello.c源文件到生成hello可執行文件的有趣過程。

對於c語言,老是想搞搞,之前編寫c語言都是在IDE中一個build一個run就看結果,雖然對預處理彙編之類的中間過程也有過了解,但時間一久就忘了,so,今天就寫下個筆記,來看看由一個hello.c源文件到產生一個可執行文件的整個過程。本例子在linux平臺下運行:linux

寫個hello.c源文件吧.編程

#include<stdio.h>
int main()vim

{ide

  printf("hello,world\n");ui

  return 0;spa

}翻譯

ok,hello.c源文件寫完。string

一個.c源文件到產生目標文件要經歷4個步驟,分別是:預處理階段 -> 編譯階段 -> 彙編階段 -> 連接階段。it

(1)預處理階段(加入相應庫,宏替換等)io

gcc hello.c -E > e.txt  ( >號是重定向,把預處理的結果輸入到e.txt文件中 )

預處理階段是用預處理器處理的,它只是簡單的把stdio.h庫簡單的加入到文件中,查看e.txt文件以下:

...前面省略不少

extern char *ctermid (char *__s) __attribute__ ((__nothrow__));
# 908 "/usr/include/stdio.h" 3 4
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__));

extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__)) ;

extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__));
# 938 "/usr/include/stdio.h" 3 4

# 2 "hello.c" 2
int main()
{
printf("hello,world\n");

return 0;
}

能夠看到,在這裏預處理階段只是把相應的庫文件添加到程序中。

(2)編譯階段(生成彙編程序)

gcc hello.c -S   這裏hello.c通過預處理和編譯,生成hello.s的彙編文件,它是一個文本文件,以下:

.file "hello.c"
.section .rodata
.LC0:
.string "hello,world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
call puts
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-23)"
.section .note.GNU-stack,"",@progbits

(3)彙編階段(把彙編語言程序彙編成機器語言指令,並使用可重定位目標程序的格式來打包指令)

gcc hello.c -c   運行會把hello.c文件經過預處理,編譯,彙編三個階段,生成.o文件,hello.o文件是一個二進制文件不是文本文件,在這個階段彙編器會把第二階段生成的hello.s文件翻譯成機器語言指令,並把這些指令打包成一種叫作可重定位目標程序的格式,而後將結果存放在hello.o文件中,使用vim -b hello.o查看能夠發現都是亂碼,其中一行比較醒目以下:

^@UH<89><e5><bf>^@^@^@^@<e8>^@^@^@^@<b8>^@^@^@^@<c9><c3>^@^@^@hello,world^@^@GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-23)^@^@^@^@^@^@^@^T^@^@^@^@^@^@^@^AzR^@^Ax^P^A

看到hello,world字符,就是咱們要打印的字符。

(4)連接階段(合併各個打包好的機器語言指令,生成可執行二進制文件)

gcc hello.o  -o hello 

在這個階段,連接器把printf.o文件和hello.o文件合併在一塊兒,生成一個可執行文件hello,經過運行:./hello

結果輸入以下:

hello,world

比較有意思的事情是,當咱們使用vim -b hello查看可執行目標文件時,發現頗有意思的一行以下:

3>^@^@^A^@^B^@^@^@^@^@^@^@^@^@^@^@^@^@hello,world^@^A^[^C; ^@^@^@^C^@^@^@<e0><fe><ff><ff><^@^@^@<fc><fe><ff><ff>\^@^@^@^L<ff><ff><ff>t^@^@^@^T^@^@^@^@^@^@^@^AzR^@^A

看到沒,那個hello,world簡直就是黑夜中的燈火,太亮眼了,我想,若是我直接改這幾個字符再運行,會不會輸出另外的結果呢?

因而把 hello,world 改爲 hello,c ,而後保存,在期待中運行: ./hello

輸出:

段錯誤 (core dumped)

這是什麼狀況???

查查發現,這應該和彙編階段產生的可重定位目標程序的格式有關,連接時已經使用該格式來描述程序。關於可重定位的內容比較複雜,這裏就不說了,在這個可執行hello程序中,若是想經過改變hello的內容來讓結果改變,要作的就是一個蘿蔔一個坑,使用其餘文本代替hello,world文件便可。

在這裏使用 ilovehaha,c 來代替 hello,world, 要注意代替的字符必須和被代替的字符長度相同,好了,更改後在期待中輸入:./hello

輸出:

ilovehaha,c

驗證結論,這就是一個hello.c源文件通過處理後生成hello可執行文件的有趣過程,over

相關文章
相關標籤/搜索