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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【软件开发底层知识修炼】十三 链接器-如何写出不依赖C库函数的代码

發(fā)布時間:2023/12/10 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【软件开发底层知识修炼】十三 链接器-如何写出不依赖C库函数的代码 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文將綜合以下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ù)print:
  • void print(const char* s, int l) {asm volatile ("movl $4, %%eax\n" // sys_write系統(tǒng)函數(shù)相關(guān)"movl $1, %%ebx\n""movl %0, %%ecx\n""movl %1, %%edx\n""int $0x80 \n" //通過80H進(jìn)行系統(tǒng)調(diào)用:: "r"(s), "r"(l) // print的參數(shù): "eax", "ebx", "ecx", "edx" // 保留列表); }

    上述打印函數(shù)在【軟件開發(fā)底層知識修煉】十二 C/C++語言中內(nèi)嵌匯編語言(asm)這篇文章中一講非常詳細(xì)。

  • 退出函數(shù) exit
  • void exit(int code) {asm volatile ("movl $1, %%eax\n" //sys_exit"movl %0, %%ebx\n""int $0x80 \n":: "r"(code) //參數(shù): "eax", "ebx"); }

    上述退出函數(shù)也在【軟件開發(fā)底層知識修煉】十二 C/C++語言中內(nèi)嵌匯編語言(asm)這篇文章中一講非常詳細(xì)。

  • 鏈接腳本設(shè)計
    program.lds
  • 上述鏈接腳本中已經(jīng)注解的非常詳細(xì),當(dāng)然,還是需要參考【軟件開發(fā)底層知識修煉】十一 鏈接器-鏈接腳本這篇文章先學(xué)習(xí)以下鏈接腳本的語法與概念最好。

    那么上述程序設(shè)計基本上完成,下面我們給出完成的代碼:

    • program.c 源文件
    void print(const char* s, int l); void exit(int code);void program() {print("D.T.Software\n", 13);exit(0); }void print(const char* s, int l) {asm volatile ("movl $4, %%eax\n""movl $1, %%ebx\n""movl %0, %%ecx\n""movl %1, %%edx\n""int $0x80 \n":: "r"(s), "r"(l): "eax", "ebx", "ecx", "edx"); }void exit(int code) {asm volatile ("movl $1, %%eax\n""movl %0, %%ebx\n""int $0x80 \n":: "r"(code): "eax", "ebx"); }
    • program.lds 鏈接腳本
    ENTRY(program)SECTIONS {.text 0x08048000 + SIZEOF_HEADERS :{*(.text)*(.rodata)}/DISCARD/ :{*(*)} }
    • 當(dāng)然,為了編譯方便,我還給出了makefile文件,方便我們程序的編譯:
    CC := gcc LD := ld RM := rm -frTARGET := program.out SRC := $(TARGET:.out=.c) OBJ := $(TARGET:.out=.o) LDS := $(TARGET:.out=.lds).PHONY : rebuild clean all$(TARGET) : $(OBJ) $(LDS)$(LD) -static -T $(LDS) -o $@ $<@echo "Target File ==> $@"$(OBJ) : $(SRC)$(CC) -fno-builtin -o $@ -c $^rebuild : clean allall : $(TARGET)clean :$(RM) $(TARGET) $(OBJ)

    上述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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。