靜態鏈接——編譯和鏈接

来源:https://www.cnblogs.com/SunshineMiles/archive/2023/07/07/17521349.html
-Advertisement-
Play Games

# 一、編譯和鏈接的過程 ## 1、GCC生成可執行文件的總體過程 在日常的開發過程中,IDE總是會幫我們將編譯和鏈接合併,一鍵式的執行,即使在liunx中,使用命令行來編譯一個源文件也只是簡單的一句"gcc hello.c"。我們並沒有過多的關註編譯和鏈接的運行機制和機理,我想從本質出發,深入瞭解 ...


一、編譯和鏈接的過程

1、GCC生成可執行文件的總體過程

在日常的開發過程中,IDE總是會幫我們將編譯和鏈接合併,一鍵式的執行,即使在liunx中,使用命令行來編譯一個源文件也只是簡單的一句"gcc hello.c"。我們並沒有過多的關註編譯和鏈接的運行機制和機理,我想從本質出發,深入瞭解這些機制。對於下麵一段hello.c代碼
#include <stdio.h>
int main()
{ 
  printf("Hello World\n");
  return 0;
}
在liunx中,當我們用GCC來編譯時只需要`gcc hello.c`即可生成`a.out`文件(並不是所有可執行文件都是`.out`),使用`./a.out`即可運行輸出。實際上,上述的過程可以分解為四個步驟,分別是預處理(Prepressing)、編譯(Compilation)、彙編(Assembly)和鏈接(Linking)。

                                                                            GCC編譯過程分解

1.1、預編譯(Prepressing)

預編譯是指將源代碼文件`(hello.c)`和相關頭文件`(stdio.h)`等被**預編譯器cpp**預編譯成一個`.i`文件。需要註意的是對於C++程式來說,它的源代碼文件的擴展名可能是`.cpp或.cxx`,頭文件的擴展名可能是`.hpp`,而預編譯後的文件擴展名是`.ii`。第一步預編譯的過程相當於如下命令(E表示只進行預編譯):` gcc -E hello.c -o hello.i 或者 cpp hello.c > hello.i`

預編譯過程主要處理那些源代碼文件中的以“#”開始的預編譯指令。比如“#include”、“#define”等,主要處理規則如下:
  • 將所有的“define”刪除,並且展開所有的巨集定義。

  • 處理所有條件預編譯指令,比如“#if”、“#ifdef'”、“#elif”、“#else”、“#endif'”。

  • 處理“#include”預編譯指令,將被包含的文件插入到該預編譯指令的位置。註意,這個過程是遞歸進行的,也就是說被包含的文件可能還包含其他文件。

  • 刪除所有的註釋。

  • 添加行號和文件名標識,比如#2“hello.c”2,以便於編譯時編譯器產生調試用的行號信息及用於編譯時產生編譯錯誤或警告時能夠顯示行號。

  • 保留所有的#pragma編譯器指令,因為編譯器要使用它們。

# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 375 "/usr/include/features.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 392 "/usr/include/sys/cdefs.h" 3 4
# 1 "/usr/include/bits/wordsize.h" 1 3 4
# 393 "/usr/include/sys/cdefs.h" 2 3 4
# 376 "/usr/include/features.h" 2 3 4
# 399 "/usr/include/features.h" 3 4
# 1 "/usr/include/gnu/stubs.h" 1 3 4
# 10 "/usr/include/gnu/stubs.h" 3 4
# 1 "/usr/include/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/gnu/stubs.h" 2 3 4
# 400 "/usr/include/features.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4
部分展示

經過預編譯後的.i文件不包含任何巨集定義,因為所有的巨集已經被展開,並且包含的文件也已經被插入到.i文件中。所以當我們無法判斷巨集定義是否正確或頭文件包含是否正確時,可以查看預編譯後的文件來確定問題。

1.2、編譯(Compilation)

編譯過程就是把預處理完的文件進行一系列詞法分析、語法分析、語義分析及優化後生產相應的彙編代碼文件,這個過程往往是我們所說的整個程式構建的核心部分,也是最複雜的部分之一。上面的編譯過程相當於如下命令:

gcc -S hello.i -o hello.s

現在版本的GCC把預編譯和編譯兩個步驟合併成一個步驟,使用一個叫做cc1的程式來完成這兩個步驟。這個程式位於`usr/lib/gcc/i486-linux-gnu/4.1/`,我們也可以直接調用 ccl來完成它:

usr/lib/gcc/i486-linux-gnu/4.1/cc1 hello.c或者gcc -S hello.c -o hello.s都可以得到彙編輸出文件helIo.s。對於C語言的代碼來說,這個預編譯和編譯的程式是ccI,對於C++來說,有對應的程式叫做cclplus:Objective-C是cclobj::fortran是f77l;Java是 jc1。所以實際上gcc這個命令只是這些後臺程式的包裝,它會根據不同的參數要求去調用預編譯編譯程式cc1、彙編器as、鏈接器ld。

	.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
	movl	$0, %eax
	call	printf
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)"
	.section	.note.GNU-stack,"",@progbits
展示hello.s中的內容

1.3 彙編

彙編器是將彙編代碼轉變成機器可以執行的指令,每一個彙編語句幾乎都對應一條機器指令。所以彙編器的彙編過程相對於編譯器來講比較簡單,它沒有複雜的語法,也沒有語義,也不需要做指令優化,只是根據彙編指令和機器指令的對照表一一翻譯就可以了,“彙編”這個名字也來源於此。上面的彙編過程我]可以調用彙編器as來完成:

as hello.s -o hello.o或者gcc -c hello.s -o hello.o或者使用gcc命令從C源代碼文件開始,經過預編譯、編譯和彙編直接輸出目標文件(Object File):gcc -c hello.c -o hello.o

1.4 鏈接

鏈接通常是一個讓人比較費解的過程,為什麼彙編器不直接輸出可執行文件而是輸出一個目標文件呢?鏈接過程到底包含了什麼內容?為什麼要鏈接?這恐怕是很多讀者心中的疑惑。正是因為這些疑惑總是揮之不去,所以我們特意用這一章的篇幅來分析鏈接,具體地說分析靜態鏈接的章節。下麵讓我們來看看怎麼樣調用ld才可以產生一個能夠正常運行的 HelloWorld程式:
$ld -static /usr/lib/crt1.o /usr/lib/crti.o /uar/lib/gcc/1486-linux-gnu/4.1.3/crtbeginT.o -L/usr/lib/gcc/1486-linux-gnu/4.1.3 -L/usr/lib -L/lib hello.o --start-group-lgcc -lgcc_eh -1c --end-group /uar/lib/gcc/1486-linux-gnu/4.1.3/crtend.o /usr/lib/crtn.o

如果把所有的路徑都省略掉,那麼上面的命令就是:

ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o
	可以看到,我們需要將一大堆文件鏈接起來才可以得到“a.out”,即最終的可執行文件。看了這行複雜的命令,可能很多讀者的疑惑更多了,ctl.o、crti.o、crtbegin T.o、crtend.o、 crtn.o這些文件是什麼?它們做什麼用的?-lgcc-lgcc_ehlc這些都是什麼參數?為什麼要使用它們?為什麼要將它們和hello.o鏈接起來才可以得到可執行文件?等等。後面我們會陸續講解

參考:《程式員的自我修養》俞甲子


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 數字化轉型會帶來大量的研發需求,如何更好更快的交付這些需求成為一個突出問題,該怎麼打造一個平臺去解決該問題?能不能用第一性原理思維去推導出發展方向? ...
  • 只要將配置信息存放在與源代碼不同的存儲庫中,將其鎖好,僅對有權訪問的人開放,並且管理員能夠根據過程、程式和執行人等授予或撤銷對相關配置信息的訪問許可權,那麼配置信息也可以存放在版本控制系統中 ...
  • ### 構造器參數 - maxFrameLength:指定解碼器所能處理的數據包的最大長度,超過該長度則拋出 TooLongFrameException 異常; - lengthFieldOffset:指定長度欄位的起始位置; - lengthFieldLength:指定長度欄位的長度:目前支持1( ...
  • # 概述 NumPy是一個開源的科學計算庫,它提供了高效的數值計算和數組操作功能,主要包括: * 多維數組的創建、操作和索引。 * 數組的切片、拼接和轉置。 * 數組的乘法、除法、求導、積分、對數等基本運算。 * 數組的逐元素操作、求平均值、中位數、眾數等統計量。 * 數組作為列表、元組等數據類型進 ...
  • 在Mac OS上安裝vs code的java開發環境. 按照vs code的官方說明安裝Java相關插件, 遇見下列問題並解決了. 安裝JDK環境 安裝Extension Pack for Java 插件後,vscode會提示你安裝一個java,我安裝提示安裝了java.後來才發現安裝的是jre,並 ...
  • 相信閱讀過上期文章,動手能力強的朋友們已經自己跑出來界面了。所以這期我要講的是交互部分,也就是對於滑鼠點擊事件的響應,包括計時計數對點擊事件以及一些狀態量的影響。 回憶下第一期介紹的掃雷規則和操作,游戲從開局到結束可能會涉及到哪些情況呢?我認為比較重要的就是明確什麼情況下游戲已經結束,結束代表的是勝 ...
  • # python multiprocessing庫使用記錄 需求是想並行調用形式化分析工具proverif,同時發起對多個query的分析(378個)。實驗室有40核心80線程的伺服器(雙cpu,至強gold 5218R*2)。 觀察到單個命令在分析時記憶體占用不大,且只使用單核心執行,因此考慮同時調 ...
  • 首先聲明,我不是小黑子,我不是小黑子! 作為一個ikun,時刻都在想著我們家姐姐! 這不上次用Python做了一個ikun飛機大戰,今天再給大家整活一手,Python tkinter開發一個專屬ikun音樂播放器,這樣就能時刻看到姐姐了。 咱們來看看效果 代碼實現 今天要做的就是上面的簡易音樂播放器 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...