日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

GCC全过程详解+剖析生成的.o文件

發布時間:2025/4/5 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GCC全过程详解+剖析生成的.o文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

使用GCC編譯一個.c文件影藏了哪些過程?

?

GCC四步詳解

第一步:預處理(也叫預編譯)

????????gcc -E ?hello.c ?-o hello.i

? ??? ??或者 cpp hello.c > hello.i ? ? 【cpp是預編譯器】

? ??? ??將所有#define刪除,并且展開所有的宏定義

? ??? ??處理所有的條件預編譯指令,如#if #ifdef ?#undef ?#ifndef ?#endif #elif

? ??? ??處理#include,將包含的文件插入到此處,這是一個遞歸的過程

? ??? ??刪除所有注釋 ? // ? /* */

? ??? ??添加行號和文件名標識,以便于編譯時產生的錯誤警告能顯示行號

? ??? ??保留#pragma編譯器指令

第二步:編譯
? ??? ??gcc ?-S ?hello.i ? -o ?hello.s
? ??? ??將預處理完的.i文件進行一系列的詞法分析、語法分析、語義分析及優
? ??? ??化后生成相應的匯編代碼文件,這是整個程序構建的最核心的部分,也是最復雜的部分

第三步:匯編
????????gcc ?-c ?hello.s ?-o ?hello.o或者 as ?hello.s -o ?hello.o
????????匯編是將第二步生成的匯編代碼變成機器可執行的指令,每一個匯編語句幾乎都對應一條機器指令

第四步:鏈接

?????????鏈接動態庫和靜態庫

?


生成的目標文件有什么,什么是目標文件?

目標文件就是源代碼經過編譯后但未進行鏈接的那些中間文件
Linux下的 .o文件就是目標文件,目標文件和可執行文件內容和
格式幾乎都一樣,所以我們可以廣義地將目標文件和可執行文化
看成一類型文件。他們都是按照ELF文件格式存儲的

?

Linux下有哪些ELF類型的文件?

.o文件、可執行文件、核心轉儲文件(core dump)、.so文件(動態鏈
鏈接庫)
?

可執行文件的概貌詳解

File ?Header 、.text section 、.data section 、.bss section
文件頭(File Header)
描述了整個文件的文件屬性,包括目標文件是否可執行、是靜態鏈接還 是動
態鏈接及入口地址、目標硬件、目標操作系統等信息、段表(描述文件中各
個段的偏移位置及屬性等)
代碼段(.text)
存放了程序源代碼編譯后生成的機器指令
數據段(.data)
存放已初始化的全局靜態與非靜態變量和已初始化的局部靜態變量
.bss段
存放未初始化的全局變量(全局靜態和非靜態變量)和局部靜態變量
但是.bss段只是為這些變量預留位置而已,并沒有內容,所以這些變量
在.bss段中也不占據空間

?

深入挖掘 .o文件


使用命令:

?

objdump ?-h ?xxxx.o

????????打印主要段的信息

objdump ?-x ?xxxx.o?
????????????打印更多的詳細信息
objdump ?-s ?xxx.o
????????????將所有段的內容以16進制方式打印出來
objdump ?-d ?xxx.o ?或者-S
????????????將所有包含指令的段反匯編
objdump ? -t ? xxx.o
????????????查看所有的符號以及他們所在段
readelf ?-h ? xxx.o
????????????查看.o文件的文件頭詳細信息
readelf ? -S ? xxx.o
????????????顯示.o文件中的所有段,即查看段表
size xxx.o
????????????查看.o文件中各個段所占大小
nm xxx.o?
????????????查看.o文件中所有的符號


使用命令gcc -c test.c編譯下面這個test.c程序生成test.o文件,然后查看test.o文件結構

?

test.c

?
  • /* this is a test code */

  • /* test.c */

  • ?
  • int printf(const char *format, ...);

  • ?
  • int g_var2 = 10;

  • int g_var2;

  • ?
  • void func(int i)

  • {

  • printf("%d\n",i);

  • }

  • ?
  • int main(void)

  • {

  • static int static_var1 = 20;

  • static int static_var2;

  • ?
  • int var3 = 1;

  • int var4;

  • func(static_var1 + static_var2 + var3 + var4);

  • return var3;

  • }


  • 然后查看生成的test.o文件的結構
    objdump -h test.o

    行:
    ????.text ?:代碼段(存放函數的二進制機器指令)
    ????.data :數據段(存已初始化的局部/全局靜態變量、未初始化的全局靜態變量)
    ????.bss ?:bss段(聲明未初始化變量所占大小)
    ????.rodata :只讀數據段(存放 " " 引住的只讀字符串)
    ????.comment :注釋信息段
    ????.node.GUN-stack :堆棧提示段
    列:
    ????Size:段的長度
    ????File Off :段的所在位置(即距離文件頭的偏移位置)
    段的屬性:
    ????CONTENTS:表示該段在文件中存在
    ????ALLOC :表示只分配了大小,但沒有存內容

    ?

    ?

    關于.bss段

    我們說.bss段是存放未初始化的全局變量(靜態與非靜態)和局部靜態變量的
    所以我們程序中的g_var2和stactic_var2應該都在.bss段中被預留位置,所以
    .bss段的size應該是8個字節,但是結果卻是4個字節,怎么回事呢?
    這就是不用的編譯器實現不一樣的原因了,有些編譯器會將未初始化的全局非靜態變量放在.bss段,有些則不放,只是預留一個未定義的全局變量符號,等到最終鏈接成可執行文件的時候再在.bss段分配空間。而我的編譯器是沒有將g_var2(全局未初始化的非靜態變量)放在任何段
    下面讓我們真正的查看一下g_var2
    首先,我們使用 ?readelf -S ?test.o ?查看段表(主要為了查看每個段的段號)


    然后我們再使用 readelf -s ?test.o看一下符號表(我們定義的變量名都是符號,包括函數名)

    ?

    符號表里會顯示這個符號所在的位置


    我們看到static_var1和g_var1所在段的段號為3(3是.data段),static_var2所在段的段號為4(4是.bss段),而g_var2卻沒有被放入任何一個段,只是用COM標記了一下,那這個COM表示什么意思呢?COM標記的符號被稱為弱符號,一個變量名是弱符號,則這個變量的大小在編譯的時候不能被確定,而在鏈接之后才能確定該變量的大小。test.o文件在鏈接之后,g_var2會被放入.bss段(當然,也只是說明g_var2所需要的空間大小,并不會存放內容),而在程序運行的時候g_var2這樣的變量才會真正去占用內存空間


    強制將某變量或者某函數放入某個段

    __attribute__((section(".data"))) ?int ? g_var2; ? //強制將g_var2放入.data段中

    ?

    各種變量所在位置總結
    ????全局已初始化非靜態變量、局部已初始化靜態變量會被放入.data段
    ????全局未初始化靜態變量會被放入.bss段
    ????全圖未初始化非靜態變量不會被放入任何一個段,只是用COM標記一下

    總結

    以上是生活随笔為你收集整理的GCC全过程详解+剖析生成的.o文件的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。