【软件开发底层知识修炼】十三 链接器-如何写出不依赖C库函数的代码
本文將綜合以下4篇文章,學(xué)習(xí)如何寫出不依賴libc庫的程序:
- 【軟件開發(fā)底層知識修煉】九 鏈接器-可重定位文件與可執(zhí)行文件
- 【軟件開發(fā)底層知識修煉】十 鏈接器-main函數(shù)不是第一個被執(zhí)行的函數(shù)
- 【軟件開發(fā)底層知識修煉】十一 鏈接器-鏈接腳本
- 【軟件開發(fā)底層知識修煉】十二 C/C++語言中內(nèi)嵌匯編語言(asm)
如果沒有看上面4篇文章的,建議先按照順序?qū)W習(xí)上述4篇文章,再來看這篇文章,不然有些地方會很突兀。
文章目錄
- 1 本篇文章的目的
- 2 解決方案設(shè)計
- 3 拓展
- 4、總結(jié)
1 本篇文章的目的
那么本篇文章的目的是什么呢?
- 我們想編寫一個體積非常小的可執(zhí)行程序。
- 通過makefile完成代碼編譯
- 運(yùn)行后在屏幕上打印“D.T.SoftWare”
但凡是學(xué)習(xí)過C語言基礎(chǔ)語法的人都能寫出來這個程序 ,這不就是一個"hello word " 程序么?就像下面這樣.
hello.c
#include <stdio.h>int main(){printf("D.T.SoftWare\n");return 0; }我們編譯上述hello.c,得到可執(zhí)行程序hello,可以看出hello的大小是:
竟然有7135字節(jié)!!!這其實是因為雖然我們只寫了個printf函數(shù),但是實際上在編譯鏈接的過程,鏈接器將一大堆庫函數(shù)都與我們的hello程序進(jìn)行鏈接,包括一些入口函數(shù)啊,進(jìn)程初始化函數(shù)以及printf這個庫函數(shù)等等,看起來只有一個printf庫函數(shù),但是實際上一與libc庫進(jìn)行鏈接,就會有一大堆東西(這些東西是啥嗎,請參看上述四篇文章)。
所以,我們的目的,并不是簡單的寫出上面的hello.c程序。我們想寫一個程序,進(jìn)行編譯鏈接后,體積達(dá)到最小?該如何做到?
我們的分析思路大概如下圖:
- 我們知道m(xù)ain函數(shù)執(zhí)行前還有一大堆的初始化函數(shù)需要調(diào)用,我們不依賴于libc庫進(jìn)行編譯鏈接,以及編寫自定義入口函數(shù)的鏈接腳本可以實現(xiàn)
- 不依賴libc庫打印字符串的話,就直接進(jìn)行系統(tǒng)調(diào)用,直接調(diào)用sys_write函數(shù),而不是調(diào)用printf函數(shù)
- 直接調(diào)用sys_write函數(shù)的方法的話就是使用內(nèi)嵌匯編語言進(jìn)行調(diào)用
學(xué)過我上面篇文章的朋友就一定會發(fā)現(xiàn)上述的幾點,都在那4篇文章中實現(xiàn)過。所以我們可以很輕松的進(jìn)行代碼的編寫。
2 解決方案設(shè)計
上面已經(jīng)給出了基本的程序設(shè)計思路,現(xiàn)在我們來給出更加具體的程序設(shè)計思路。
- 通過內(nèi)嵌匯編自定義打印函數(shù)和退出函數(shù)(具體來說就是使用INT 0X80指令)
- 通過編寫自定義的鏈接腳本來指定我們自己的入口函數(shù)(不依賴任何庫和GCC的任何內(nèi)置功能)
- 刪除可執(zhí)行程序的無用信息,比如無用的調(diào)試信息和段信息。這是通過鏈接腳本指定的
那么我們先來通過內(nèi)嵌匯編設(shè)計一下打印函數(shù)和退出函數(shù),這里要參考【軟件開發(fā)底層知識修煉】十二 C/C++語言中內(nèi)嵌匯編語言(asm)。
上述打印函數(shù)在【軟件開發(fā)底層知識修煉】十二 C/C++語言中內(nèi)嵌匯編語言(asm)這篇文章中一講非常詳細(xì)。
上述退出函數(shù)也在【軟件開發(fā)底層知識修煉】十二 C/C++語言中內(nèi)嵌匯編語言(asm)這篇文章中一講非常詳細(xì)。
program.lds
上述鏈接腳本中已經(jīng)注解的非常詳細(xì),當(dāng)然,還是需要參考【軟件開發(fā)底層知識修煉】十一 鏈接器-鏈接腳本這篇文章先學(xué)習(xí)以下鏈接腳本的語法與概念最好。
那么上述程序設(shè)計基本上完成,下面我們給出完成的代碼:
- program.c 源文件
- program.lds 鏈接腳本
- 當(dāng)然,為了編譯方便,我還給出了makefile文件,方便我們程序的編譯:
上述makefile可能大多數(shù)人看不懂,這個無所謂,它只是一種類似于腳本語言的語言,輔助我們編譯程序的,我們將上述三個文件:makefile program.lds program.c這三個文件放到linux系統(tǒng)下的同一個目錄下,然后輸入命令make即可完成代碼的編譯,生成可執(zhí)行文件。
- 就像下面這樣,我們隊我們的程序進(jìn)行編譯
- 運(yùn)行可執(zhí)行程序 ./program.out
很明顯,我們實現(xiàn)了我們最開始的功能。
-
我們看看我們寫的這個可執(zhí)行程序的大小:
-
上面的是hello的大小,下面的是我們自己的可執(zhí)行程序的大小,只有582字節(jié),遠(yuǎn)遠(yuǎn)小于hello的大小。這正是我們所希望看到的。
3 拓展
如果有詳細(xì)看上述的makefile文件,我們會發(fā)現(xiàn),在我們編譯我們的源文件的時候,使用了靜態(tài)鏈接?,F(xiàn)在在這里介紹一些鏈接時的一些選項:
-
ld 命令
- GNU的鏈接器,將目標(biāo)文件鏈接為可執(zhí)行文件
-
ld -static
- 表示ld使用靜態(tài)鏈接的方式來產(chǎn)生最終的可執(zhí)行程序,而不是默認(rèn)的動態(tài)鏈接。至于什么是靜態(tài)鏈接什么是動態(tài)鏈接,后面肯定會有文章詳細(xì)學(xué)習(xí)。
-
gcc -fno-builtin
- -fno-builtin 用于關(guān)閉GCC內(nèi)部函數(shù)功能
-
GCC提供了很多內(nèi)置函數(shù)(Built-in Function),它會把一些常用的C庫函數(shù)替換成編譯器內(nèi)置的函數(shù),以達(dá)到優(yōu)化程序的目的。在上述我們的makefile中就用到了這個選項,以防止編譯器的優(yōu)化
4、總結(jié)
-
對于資源受限制的嵌入式設(shè)備,需要考慮可執(zhí)行程序的大小(當(dāng)然目前各種設(shè)備內(nèi)存都足夠大,不必?fù)?dān)心這一點)
-
通過在C/C++語言中內(nèi)嵌匯編語言,可以避免使用庫函數(shù)而直接使用系統(tǒng)函數(shù)
-
可以通過如下方式,來控制可執(zhí)行程序的大小
- 最小化庫的使用(必要的時候,可以自己實現(xiàn)相關(guān)函數(shù))
- 自定義鏈接腳本,刪除無用的段信息
本文參考狄泰軟件學(xué)院相關(guān)課程
想學(xué)習(xí)的可以加狄泰軟件學(xué)院群,
群聊號碼:199546072
學(xué)習(xí)探討加個人(可以免費幫忙下載CSDN資源):
qq:1126137994
微信:liu1126137994
總結(jié)
以上是生活随笔為你收集整理的【软件开发底层知识修炼】十三 链接器-如何写出不依赖C库函数的代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C语言进阶深度学习记录】二十 结构体大
- 下一篇: 2014届华为校园招聘机试题2