一:程序的誕生
平時(shí)使用gcc生成可執(zhí)行程序,gcc -g -Wall hello.c -o hello
整個(gè)過(guò)程涉及了預(yù)處理困曙、編譯传惠、匯編仿便、鏈接多個(gè)步驟体啰。
1. 預(yù)處理階段
將宏定義展開(kāi),將頭文件的內(nèi)容包含嗽仪,生成后綴為.i的預(yù)處理文件荒勇。
gcc/g++ -E a.c -o a.i
所以為什么全局變量不能在頭文件中定義,因?yàn)槎x全局變量的代碼會(huì)存在于所有include包含該頭文件的文件中闻坚。
一般的做法沽翔,將全局變量 使用extern 聲明在頭文件(eg:res.h)中,res.cpp中定義鲤氢,其他使用全局變量的文件包含該頭文件(res.h)
關(guān)于g++和gcc在這一階段的區(qū)別:g++ 區(qū)別于 gcc 會(huì)在部分頭文件和類(lèi)型定義前添加extern “C”搀擂,這個(gè)標(biāo)識(shí)符的作用把標(biāo)識(shí)符作用域的數(shù)據(jù)類(lèi)型采用gcc去編譯西潘。
2. 編譯階段:
對(duì)源代碼進(jìn)行語(yǔ)義分析卷玉,并優(yōu)化產(chǎn)生對(duì)應(yīng)的匯編代碼的過(guò)程。生成后綴.s的匯編文件
gcc/g++ -S a.c -o a.s
使用gcc 和 g++ 編譯結(jié)果對(duì)比圖:
對(duì)比發(fā)現(xiàn)函數(shù)的命名方式不一樣喷市,對(duì)于g++相种,因?yàn)閏++支持重載,所以編譯器會(huì)為每個(gè)函數(shù)重新更改名字品姓。
3. 匯編階段
將源碼翻譯成可執(zhí)行的指令寝并,并生成目標(biāo)文件。后綴為.o.
gcc/g++ -c a.s -o a.o
匯編階段時(shí)腹备,gcc/g++內(nèi)部都是調(diào)用as匯編命令衬潦,在這里兩者是沒(méi)有區(qū)別的。
4. 鏈接階段
將各個(gè)目標(biāo)文件包括庫(kù)文件植酥,鏈接成一個(gè)可執(zhí)行程序镀岛,這個(gè)過(guò)程涉及 地址和空間的分配弦牡、符號(hào)解析、重定位等等漂羊,在linux下驾锰,該工作由 GNU的鏈接器 ld 完成。
gcc/g++ -o a a.o
我們可以使用 -v 選項(xiàng)查看完整和詳細(xì)的gcc編譯過(guò)程走越。
5.這里有一篇《關(guān)于gcc和g++編譯器分別對(duì)c與c++文件影響》
原文鏈接:https://blog.csdn.net/qq_21792169/article/details/85097822
版權(quán)聲明:本文為CSDN博主「HeroKern」的原創(chuàng)文章椭豫,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明旨指。
二:程序的構(gòu)成
linux下可執(zhí)行程序大部分是elf格式文件赏酥,可以使用 readelf 查看
readelf -h test
readelf -S test
[21] .dynamic DYNAMIC 0000000000200dc8 00000dc8
00000000000001f0 0000000000000010 WA 6 0 8
[22] .got PROGBITS 0000000000200fb8 00000fb8
0000000000000048 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000201000 00001000
0000000000000010 0000000000000000 WA 0 0 8
[24] .bss NOBITS 0000000000201010 00001010
0000000000000008 0000000000000000 WA 0 0 1
[25] .comment PROGBITS 0000000000000000 00001010
0000000000000029 0000000000000001 MS 0 0 1
[26] .symtab SYMTAB 0000000000000000 00001040
0000000000000600 0000000000000018 27 43 8
[27] .strtab STRTAB 0000000000000000 00001640
0000000000000205 0000000000000000 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00001845
00000000000000fe 0000000000000000 0 0 1
可以看到比較熟悉的 data text bss等。
比較常見(jiàn)的段:
- text段:代碼段谆构,用于保存可執(zhí)行指令
- data段:初始化數(shù)據(jù)段今缚,保存有非0初始值的全局變量和靜態(tài)變量
- bss段:未初始化數(shù)據(jù)段,用于保存沒(méi)有初始化值或初值為0的全局變量和靜態(tài)變量低淡,當(dāng)程序加載時(shí)姓言,這些變量的值會(huì)被初始化為0。
- debug段:用于保存調(diào)試信息蔗蹋。
- dyamic段:用于保存動(dòng)態(tài)鏈接信息
- init段:用于保存進(jìn)程啟動(dòng)時(shí)的執(zhí)行程序何荚,當(dāng)進(jìn)程啟動(dòng)時(shí),系統(tǒng)會(huì)自動(dòng)執(zhí)行這部分代碼猪杭。
等等吧餐塘。。皂吮。
關(guān)于程序內(nèi)存分區(qū)
代碼段戒傻、數(shù)據(jù)段、BSS段蜂筹、堆區(qū)需纳、映射段、棧區(qū)艺挪、內(nèi)核空間
參考 https://blog.csdn.net/shayne000/article/details/88547187
關(guān)于nm和ldd
nm 可以查看可執(zhí)行程序或庫(kù)的符號(hào)信息
ldd 可以查看可執(zhí)行程序運(yùn)行時(shí)依賴(lài)的庫(kù)文件
參考 https://www.cnblogs.com/xiaomanon/p/4203671.html
三:關(guān)于ABI兼容
1.API和ABI的區(qū)別
參考:https://blog.csdn.net/xinghun_4/article/details/7905298
API 應(yīng)用程序接口不翩,是編程接口。編寫(xiě)“應(yīng)用程序”時(shí)候調(diào)用的函數(shù)之類(lèi)的東西麻裳。 定義了源代碼和庫(kù)之間的接口口蝠,因此同樣的代碼可以在支持這個(gè)API的任何系統(tǒng)中編譯。
ABI 應(yīng)用程序二進(jìn)制接口津坑,是二進(jìn)制接口妙蔗,除非你直接使用匯編語(yǔ)言,這種接口一般是不能直接拿來(lái)用的疆瑰。允許編譯好的目標(biāo)代碼在使用兼容ABI的系統(tǒng)中無(wú)需改動(dòng)就能運(yùn)行.
2. ABI兼容
以下整理自知乎上一個(gè)答者眉反。
作者:Aman
鏈接:https://www.zhihu.com/question/381069847/answer/1094118331
來(lái)源:知乎
著作權(quán)歸作者所有狞谱。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處禁漓。
詳細(xì)情況建議點(diǎn)擊上述鏈接直接查看原答者的回答
2.1什么是二進(jìn)制兼容性呢跟衅?
如1中API和ABI的說(shuō)明,假設(shè)你的應(yīng)用程序引用的一個(gè)庫(kù)某天更新了播歼,雖然 API 和調(diào)用方式基本沒(méi)變伶跷,但你需要重新編譯你的應(yīng)用程序才能使用這個(gè)庫(kù),那么一般說(shuō)這個(gè)庫(kù)是 Source compatible秘狞;反之叭莫,如果不需要重新編譯應(yīng)用程序就能使用新版本的庫(kù),那么說(shuō)這個(gè)庫(kù)跟它之前的版本是二進(jìn)制兼容的烁试。
2.2 怎么開(kāi)發(fā)二進(jìn)制兼容的程序呢
由于不同的 C++ 編譯器雇初、甚至不同版本的 C++ 編譯器的 Name mangling 算法可能有所不同,所以開(kāi)發(fā)二進(jìn)制兼容的庫(kù)的時(shí)候减响,一般使用 extern "C" 來(lái)抑制 Name mangling
#ifdef __cplusplus
extern "C" {
#endif
// your code here
#ifdef __cplusplus
}
#endif
在開(kāi)發(fā)二進(jìn)制兼容的庫(kù)的時(shí)候靖诗,一定要避免使用 STL,因?yàn)椴煌?C++ 編譯器支示、不同版本的 C++ 編譯器攜帶的 STL 不具備二進(jìn)制兼容性刊橘,甚至同一個(gè)版本的 C++ 編譯器用戶(hù)也可能使用不同的 STL 替代自帶的 STL∷毯瑁或者說(shuō)促绵,二進(jìn)制兼容的接口應(yīng)該只使用 int32、double 等基礎(chǔ)數(shù)據(jù)類(lèi)型嘴纺,使用確定的 struct 甚至完全不使用 struct败晴、只提供抽象的 handle,或者純抽象接口栽渴。
2.3 這里有一個(gè)ABI不兼容的問(wèn)題例子
參考 https://www.cnblogs.com/Keeping-Fit/p/14251144.html
四:指令集
參考 https://www.cnblogs.com/johnnyzen/p/13224632.html
指令集 | 被應(yīng)用的指令集架構(gòu) | 國(guó)產(chǎn)芯片 | |
---|---|---|---|
CISC | 復(fù)雜指令集 | x86 | 兆芯 amd64 |
RISC | 精簡(jiǎn)指令集 | mips | 龍芯 |
RISC | 精簡(jiǎn)指令集 | arm | 飛騰尖坤、華為鯤鵬 |
RISC | 精簡(jiǎn)指令集 | aarch64/arm64 | 飛騰 |