色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

linker script的原理及使用技巧

嵌入式IoT ? 來源:CSDN博客 ? 作者:CSDN博客 ? 2020-08-31 14:58 ? 次閱讀

1.概述

編譯器將編寫的C程序代碼進行翻譯,變成機器可以執行的程序,這個大致上可以分為四個步驟:預編譯、編譯、匯編、鏈接。

其中編譯和鏈接這兩個過程比較重要。編譯過程就是將源代碼通過程序翻譯后生成機器可以認識的機器語言。而鏈接就是將目標文件進行組合,最后生成在特定平臺上可以正常運行的可執行程序。

本文主要描述鏈接這個過程。由于匯編器生成的目標代碼(.o)文件不能被立即執行,因為里面一般都會包含其他的源文件中的符號、變量或者函數調用等等,要想處理好這些問題,就必須將程序進行鏈接。

2.靜態鏈接和動態鏈接

根據開發人員指定庫函數的鏈接方式,鏈接又分為動態鏈接和靜態鏈接兩種。

2.1 靜態鏈接

我們在進行嵌入式開發過程中時,往往接觸到比較多的就是靜態鏈接。前面說過,編譯器將源代碼編譯成一個一個的.o文件的目標文件,這些文件又會存在各種依賴關系,所以將各種.o文件匯集到一起。

這種方式編譯出來的程序,可以直接運行,不依賴于外部庫文件。

2.2 動態鏈接

當涉及到程序比較多的時候,如果每個程序都依賴于同樣的一個庫里面的函數,那么這個庫就是共享的。

2.3 兩種鏈接方式的對比

靜態鏈接方式,適合單應用程序,比如嵌入式rtos等等。這種將所有的目標文件都鏈接到一個可執行的文件中,所以執行效率很高。但是文件內存占用大。動態鏈接時,如果app1運行將libc加載到內存中,下次app2直接可以從內存中使用。這種方式可以讓每個程序的文件大小比較小,但是相對于的,執行效率相對比較低。

3.鏈接腳本

一般在進行gcc進行鏈接的時候,都會考慮到鏈接腳本(linker script),該文件一般以lds文件作為后綴名。該文件規定了將特定的section放到文件內,并且控制著輸出文件的布局。一般來說,自己編寫的鏈接腳本可以指定傳遞參數-T xxx.lds,其中xxx.lds則是自己編寫的鏈接腳本。

xxx.lds基本格式如下:

SECTIONS{sections-commandsections-command......}

那么什么是sections?每個目標文件都有一些列的段,比如代碼段、數據段、bss段等等。

3.1 鏈接腳本實例分析

如果沒有實際的東西,那么說起理論來將索然無味。下面就具體來看下面的一個鏈接腳本的布局。

一個最簡單的linker腳本文件如下:

SECTIONS{.=0x10000;/*(1)*/.text:{*(.text)}/*(2)*/.=0x800000;/*(3)*/.data:{*(.data)}/*(4)*/.bss:{*(.bss)}/*(5)*/}

下面來解釋一下上述的程序

(1).的定義是location counter,也就是把當前的程序指向0x10000,如果沒有這個地址,默認該符號的值為0。或者在gcc的鏈接選項中-Ttext 0x10000也是一樣的效果。

(2).text指向代碼段,其中*這個符號代表所有的輸入文件的.text section合并成的一個

(3).=0x800000將定位器的符號設置成0x800000

(4).data指向所有輸入文件的數據段,并且這個地址的起始為0x800000

(5).bss表示所有輸入文件的bss段

上述從一個最簡單的鏈接腳本分析了鏈接腳本的語法格式。

3.2 內存的分段鏈接

如果一塊內存在sram中,一塊內存在sdram中,這兩塊地址并不連續,那么需求是將代碼段(.text)段放在sram區,數據段(.data)與bss段放在ddr區,這時鏈接腳本該如何進行設計。

首先假設sram的空間地址為0x1000處開始的,可用空間為1M。ddr的地址空間為0x40000000,目前只用到2M。

首先可用在lds文件中做一個聲明

MEMORY{ram : org = 0x00001000, len = 1Mddr : org = 0x40000000, len = 2M}

然后鏈接腳本可用以如下的方式進行編寫

SECTIONS{ . = 0x00001000; . = ALIGN(4096); .text:{*(.text)}>ram.data:{*(.data)}>ddr.bss:{*(.bss)}>ddr}

只需要指定對應的鏈接段即可。

3.3 指定第一個文件的鏈接

有的時候,需要考慮到鏈接順序的問題,比如在有些處理器中,系統從一個固定的地址啟動,但這個地址一定最開始的時候會存放一個異常向量表。從異常向量表中跳轉到實際的入口函數處去執行。那么這該如何進行設計?

一般來說我們鏈接代碼段的時候,都是鏈接的.text section。但是,我們也可用指定該文件的代碼段。比如可以在第一個需要編譯的文件頭部加上

.section ".text.entrypoint"

這樣就會指定

SECTIONS{ . = 0x00001000; . = ALIGN(4096); .text: { KEEP(*(.text.entrypoint)) *(.text) }>ram.data:{*(.data)}>ddr.bss:{*(.bss)}>ddr}

其中keep相當于告訴編譯器,這部分不要被垃圾回收。

3.4 自己定義代碼段名字

有些時候,需要將特定的符號指定到特定的地址,這樣的好處就是可用通過地址訪問對應的函數。這個應用在rt-thread rtos操作系統應用的比較經典。

在很多時候,需要指定初始化的執行順序。比如驅動的初始化順序等等。實現這種功能有很多種實現方式,上中下策都可以,下策就是直接通過函數調用關系進行調用。中策就是采用回調函數的方式進行設計。上策就是利用linker script進行函數擴展。

直接調用的方式實現起來比較簡單,也比較好理解,直接調用對應的函數即可。

回調函數就是利用函數指針,當回調函數綁定了指針時,執行該回調函數檢查該函數是否綁定,然后選擇執行。這樣可用降低耦合性。

采用linker script方式時,相當于把函數的指針集合到一個.text的空間中。這樣執行的時候,只需要找到linker中對應的地址,轉換成函數即可,這種方式就很好擴展。

在rt-thread中,函數導出命令使用了這種技巧

/* board init routines will be called in board_init() function */#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1") /* pre/device/component/env/app init routines will be called in init_thread *//* components pre-initialization (pure software initilization) */#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2")/* device initialization */#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")/* components initialization (dfs, lwip, ...) */#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")/* environment initialization (mount disk, ...) */#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5")/* appliation initialization (rtgui application etc ...) */#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")

而INIT_EXPORT的實現如下:

#define INIT_EXPORT(fn, level) RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn

而在鏈接腳本中編寫如下:

. = ALIGN(4);__rt_init_start = .;KEEP(*(SORT(.rti_fn*)))__rt_init_end = .;. = ALIGN(4);

最后可用查看map文件,查看地址

*(SORT(.rti_fn*)) .rti_fn.0 0xffffffff802bd418 0x8 buildkernelsrccomponents.o 0xffffffff802bd418 __rt_init_rti_start .rti_fn.0.end 0xffffffff802bd420 0x8 buildkernelsrccomponents.o 0xffffffff802bd420 __rt_init_rti_board_start .rti_fn.1 0xffffffff802bd428 0x8 builddriversdrv_gpio.o 0xffffffff802bd428 __rt_init_loongson_pin_init .rti_fn.1.end 0xffffffff802bd430 0x8 buildkernelsrccomponents.o 0xffffffff802bd430 __rt_init_rti_board_end .rti_fn.2 0xffffffff802bd438 0x8 buildkernelcomponentsdfssrcdfs.o 0xffffffff802bd438 __rt_init_dfs_init .rti_fn.2 0xffffffff802bd440 0x8 buildkernelcomponents etlwip-2.0.2srcarchsys_arch.o 0xffffffff802bd440 __rt_init_lwip_system_init .rti_fn.3 0xffffffff802bd448 0x8 builddriversdrv_rtc.o 0xffffffff802bd448 __rt_init_rt_hw_rtc_init .rti_fn.3 0xffffffff802bd450 0x8 buildkernelcomponentsdriverssrcworkqueue.o 0xffffffff802bd450 __rt_init_rt_work_sys_workqueue_init .rti_fn.4 0xffffffff802bd458 0x8 builddrivers etsynopGMAC.o 0xffffffff802bd458 __rt_init_rt_hw_eth_init .rti_fn.4 0xffffffff802bd460 0x8 buildkernelcomponentsdfsfilesystemselmfatdfs_elm.o 0xffffffff802bd460 __rt_init_elm_init .rti_fn.4 0xffffffff802bd468 0x8 buildkernelcomponentslibccompilers ewliblibc.o 0xffffffff802bd468 __rt_init_libc_system_init .rti_fn.4 0xffffffff802bd470 0x8 buildkernelcomponents etsal_socketsrcsal_socket.o 0xffffffff802bd470 __rt_init_sal_init .rti_fn.6 0xffffffff802bd478 0x8 buildkernelcomponentsfinshshell.o 0xffffffff802bd478 __rt_init_finsh_system_init .rti_fn.6.end 0xffffffff802bd480 0x8 buildkernelsrccomponents.o 0xffffffff802bd480 __rt_init_rti_end 0xffffffff802bd488 __rt_init_end = . 0xffffffff802bd488 . = ALIGN (0x4) 0xffffffff802bd488 . = ALIGN (0x4)

實際上在執行的時候,實現如下

/** * RT-Thread Components Initialization for board */void rt_components_board_init(void){#if RT_DEBUG_INIT int result; const struct rt_init_desc *desc; for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++) { rt_kprintf("initialize %s", desc->fn_name); result = desc->fn(); rt_kprintf(":%d done ", result); }#else volatile const init_fn_t *fn_ptr; for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++) { (*fn_ptr)(); }#endif}

并不是訪問的具體的函數,而是從__rt_init_rti_board_start指向的指針開始,不停向下執行,直到__rt_init_rti_board_end結尾。這樣就不依賴于具體的函數的實現了。所以函數的擴展性非常好。

4.總結

以上介紹了linker script的原理,以及在實際使用過程中的幾個使用的技巧。這些都是在實際的項目中總結的來的,其實理解了linker script將可用完成很多有趣的使用技巧。只是平時我們并沒有特別關注這個文件的使用,也并沒有實際去編寫一個linker script完成一個工程的構建。關于linker script的語法和使用,還有很多可以自由發揮的地方。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linker
    +關注

    關注

    0

    文章

    3

    瀏覽量

    1663

原文標題:鏈接腳本linker script的妙用

文章出處:【微信號:Embeded_IoT,微信公眾號:嵌入式IoT】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    求助,關于PSOC4模擬EEPROM的checksum問題求解

    咨詢個問題,當我們用PSOC4的Em_EEPROM組件時,工程生成的Hex1文件的checksum是不包含了EEPROM的部分吧(通過Custom Linker Script調用cm0gcc.ld
    發表于 02-02 08:48

    Eclipse如何設置Linker文件路徑?

    電腦沒有F盤,可Eclispe 編譯提示找不到F盤的linker script file,暈了,找半天沒找到,哪里可以設置linker路徑呢?
    發表于 02-22 07:31

    esp-idf-tools-setup-offline-2.11.exe編譯提示Could NOT find Perl,這個Perl如何安裝?

    linker script D:/esp32/esp-idf/components/esp_rom/esp32s2/ld/esp32s2.rom.ld -- Adding linker sc
    發表于 06-20 07:24

    ESP32-S2使用IDF MASTER分支編譯出現/http-parser文件夾缺失怎么解決?

    \") -- App \"app_main\" version: a335f49-dirty -- Adding linker script /mnt/g/project
    發表于 06-21 09:32

    idf.py build報錯ninja failed with exit code 1如何解決?

    that the project does not need compatibility with older versions. -- Adding linker script /Users/yuyuan/esp
    發表于 06-24 06:20

    esp32s2添加IOT組件的配置出錯了怎么解決?

    script C:/Users/xuyux/Desktop/esp-idf/components/esp_rom/esp32s2/ld/esp32s2.rom.ld-- Adding linker
    發表于 06-28 07:39

    找不到鏈接器腳本PIC32MX570F512L

    在Microchip提供的AN1388源代碼中沒有PIC32 MX570F512L的鏈接器腳本? 以上來自于百度翻譯 以下為原文 hellowhy there isn't any linker
    發表于 09-11 14:59

    Harmony V2.05 Bootloader鏈接器腳本不起作用

    更容易,但我想不會。 以上來自于百度翻譯 以下為原文 Harmony V2.05.01MHCV2.05.02MplabX V4.05PIC32MX530F128H The Linker script
    發表于 11-14 16:21

    Bootloader錯誤:警告:鏈接描述文件未指定CRT0_STARTUP

    script error ...Warning: linker script did not specify CRT0_STARTUPThe error is because the lin
    發表于 04-23 12:28

    IDF-4.3.1編譯提示Could NOT find Perl,這個Perl如何安裝?

    : PERL_EXECUTABLE)-- App "smart-panel" version: v4.3.1-- Adding linker script D:/esp32
    發表于 02-20 06:22

    ESP-AT編譯失敗的原因?如何解決?

    : PERL_EXECUTABLE)-- Adding linker script D:/myWork/esp/esp-at/test-main/build/esp-idf/esp32/esp32_out.ld--
    發表于 03-08 08:22

    ESP32S2添加IOT組件的配置出錯的原因?如何解決?

    ;lvgl_example" version: 1-- Adding linker script C:/Users/xuyux/Desktop/esp-idf/components/esp_rom
    發表于 03-09 08:38

    在lpuart_edma_rb_transfer SDK示例中使用BOARD_SDRAM報錯怎么解決?

    > Settings > Tools Settings > MCU Linker > Managed Linker Script 中我將每個區域(堆、堆棧、.data
    發表于 03-15 07:01

    ESP-AT編譯失敗的原因?如何解決?

    linker script D:/myWork/esp/esp-at/test-main/build/esp-idf/esp32/esp32_out.ld -- Adding linker
    發表于 04-24 06:32

    D語言編寫單片(STM32F401cc)機應用需要用到的技巧 - 主入口函數

    D語言編寫單片機應用需要用到的技巧 - 主入口函數入口函數入口函數單片機一定會需要一個主入口函數,至于是那個主要看linker script中的定義,并不一定是你看到的main函數(多數情況下也
    發表于 11-29 21:06 ?13次下載
    D語言編寫單片(STM32F401cc)機應用需要用到的技巧 - 主入口函數
    主站蜘蛛池模板: 在教室伦流澡到高潮HNP视频 | 久久久午夜精品福利内容| 欧美丝袜女同| 18禁黄无遮挡禁游戏在线下载| 国产亚洲人成网站在线观看播放 | 九九99热久久精品在线6| 香蕉久久一区二区三区啪啪| 高干紧射H后入| 日本久久黄色| 超碰人热人人热人人看| 欧美亚洲国产免费高清视频| 97ganmeizi| 伦理片秋霞免费影院| 中文无码熟妇人妻AV在线| 久久国产精品福利影集| 伊人久综合| 毛片网站视频| MM131亚洲精品久久安然| 青青青视频在线| 儿子好妈妈的HD3中字抢劫| 日韩亚洲国产欧美免费观看| 二色AV天堂在线| 小草视频免费观看在线| 国产美女久久久久久久久久久| 性夜a爽黄爽| 久久国产亚洲精品AV麻豆| 在线观看免费精品国产| 猛烈抽插H1V1| 波多野结衣的AV一区二区三区| 日本69色视频在线观看| 贵妇局长的蕾丝乳罩| 小货SAO边洗澡边CAO你动漫| 精品久久免费视频| 999精品在线| 日日摸天天添天天添无码蜜臀| 国产精品成人网| 一级大乳奶| 欧洲老妇人bb| 国精产品一区一区三区有| 中文字幕无线观看不卡网站| 欧美高清另类video|