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