91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

鏈接器的應用(八)

發布時間:2020-03-03 14:03:27 來源:網絡 閱讀:290 作者:上帝之子521 欄目:系統運維

        在上節博客中,我們介紹了鏈接器的相關概念。那么在本節,我們就繼續來看看鏈接器,看看它在工程實踐中的應用。我們在本節中做一個實驗,來模擬在嵌入式中的開發。

        實驗的目標:

                1、編寫一個“體積受限”的可執行程序;

                2、通過 makefile 完成代碼編譯;

                3、運行后在屏幕上打印“D.T.Software”

        那么這個目標看似很簡單,我們在初學編程時就直接打印經典的 hello world。其效果類似,經典的代碼是

#include <stdio.h>

int main()
{
    printf("D.T.Software");
    
    return 0;
}

        當然這個代碼也能運行,但是我們的要求是體積受限。什么是體積受限呢?我們都知道,在嵌入式的開發中,內存是很少的。因此節省內存成立必不可少的因素,因此我們必須要去掉庫文件所包含的一些額外的信息,只保留打印語句所需要代碼即可。那么我們來深度分析下,如何進行開發呢?無疑是直接用匯編代碼進行編寫,然后編寫鏈接腳本自定義入口地址了。思路如下

鏈接器的應用(八)

        由上可知,我們的解決方案可由以下幾部分完成:

                1、通過內嵌匯編自定義打印函數和退出函數(INT 80H);

                2、通過鏈接腳本自定義入口函數(不依賴于任何庫和 GCC 內置功能);

                3、刪除可執行程序中的無用信息(無用段信息,調試信息等)。

        打印函數設計如下

void print(const char* s, int l)
{
    asm volatile (
        "movl $4, %%eax\n"    // sysy_write
        "movl $1, %%ebx\n"
        "movl %0, %%ecx\n"
        "movl %1, %%edx\n"
        "int $0x80     \n"    // 80H Service
        :
        : "r"(s), "r"(1)      // parameter
        : "eax", "ebx", "ecx", "edx");
}

        退出函數設計如下

void exit(int code)
{
    asm volatile (
        "movl $1, %%eax\n"    // sys_exit
        "movl %0, %%ebx\n"
        "int $0x80     \n"    // 80H Service
        :
        : "r"(code)             // parameter
        : "eax", "ebx");
}

        鏈接腳本設計如下

ENTRY(program)    // 指定入口函數

SECTIONS
{
    .text 0x08048000 + SIZEOF_HEADRS :
    {
        *(.text)    // 將目標文件中的這兩個段合并進入到可執行程序中
        *(.rodata)
    }
    
    /DISCARD/ :
    {
        *(*)   // 放棄所有目標文件中除 .test 和 .rodata 之外的其他段
    }
}

        最后來介紹幾個命令:

            1、ld 命令:GNU 的鏈接器,將目標文件鏈接為可執行程序;GCC 編譯器集中的一員。

            2、ld -static:表示 ld 使用靜態鏈接方式來產生最終程序,而不是采用默認的動態鏈接方式。

            3、gcc -fno-builtion:用于關閉 GCC 內置函數的功能。

        在 GCC 中,它提供了很多內置函數(Built-in Function),-fno-builtion 選項會把一些常用的 C 庫函數替換成編譯器的內置函數,以達到優化的目的。


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"           // 調用系統寫函數,編號為 4
        "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"
    );
}


makefile 編寫如下

CC := gcc
LD := ld
RM := rm -rf

TARGET := 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 all

all : $(TARGET)

clean :
    $(RM) $(TARGET) $(OBJ)

        我們來運行看看結果如何呢?

鏈接器的應用(八)

        結果正是我們想要的,那么我們這么大費周章的來寫這么復雜的代碼,究竟意義何在呢?我們來看看它的內存大小,以及來編寫一個正常的 printf 打印語句的代碼,對比下兩個可執行文件的內存大小

鏈接器的應用(八)

        我們看到效果是一樣的,但是正常的 printf 打印和我們自定義的鏈接腳本的函數編寫所需的內存空間是十多倍的差異。也就是說,在嵌入式開發中,資源是很寶貴的這種做法是必須的。通過今天對鏈接腳本的實例分析,總結如下:1、對于資源受限的嵌入式設備,需要考慮可執行程序的文件大小;2、通過內嵌匯編直接使用系統服務能夠避開相關庫的使用;3、可以通過如下方法控制可執行程序的體積大小:a> 最小化庫的使用(必要情況下自己實現相關函數);b> 自定義鏈接腳本,刪除無用段信息。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

高碑店市| 孟津县| 盐池县| 萝北县| 成都市| 游戏| 渝北区| 梁平县| 冀州市| 阿荣旗| 和田市| 乃东县| 安溪县| 辽中县| 柳州市| 沛县| 彭州市| 扎赉特旗| 洛川县| 拜泉县| 南部县| 岫岩| 崇仁县| 南宁市| 仲巴县| 唐山市| 通州区| 镇坪县| 玉树县| 达日县| 申扎县| 遂平县| 龙井市| 寿光市| 青浦区| 黄山市| 澜沧| 镇平县| 高邮市| 吐鲁番市| 高密市|