---
# System prepended metadata

title: 連結器和執行檔資料
tags: [C 語言筆記]

---

---
title: 連結器和執行檔資料
tags: C 語言筆記
---
## 連結器和執行檔資料

### 嵌入一個二進位檔案到執行檔
* 範例一
    * 利用 ```$uname -a > blob``` 來產生一個 text 檔案
    * 然後可以先 ```$uname -a | wc -c ``` 看一下檔案大小
    * 之後再利用 ```$ld -s -r -b binary -o blob.o blob ``` 把 blob 轉成 binary file blob.o 輸出
    * 可以用 ```$objdump -t blob.o``` 觀察輸出
    ```
    blob.o:     file format elf64-x86-64

    SYMBOL TABLE:
    0000000000000000 l    d  .data	0000000000000000 .data
    0000000000000070 g       .data	0000000000000000 _binary_blob_end
    0000000000000000 g       .data	0000000000000000 _binary_blob_start
    0000000000000070 g       *ABS*	0000000000000000 _binary_blob_size
    ```
    * 然後用一個測試檔案 test.c，其中 extern 代表這個參數不是在這個 .c 檔裡面宣告的
    ```c=
    #include <stdio.h>
    int main(void) {
        extern void *_binary_blob_start, *_binary_blob_end;
        void *start = &_binary_blob_start, *end = &_binary_blob_end;
        printf("Data: %p..%p (%zu bytes)\n",
               start, end, end - start);         
        return 0;
    }
    ```
    * 進行編譯 ```$gcc test.c blob.o -o test```，後執行可得
    ```Data: 0x559914241010..0x559914241080 (112 bytes)```
    * 也可以用 ```$strings test``` 來看 _binary_blob_end、_binary_blob_end 有沒有在裡面

### ld script 基本術語
* linker: 將多個 input files 輸出為一個 output file，每個檔案則稱做 object file (目的檔)，輸出檔則常稱做 executable (可執行檔)，當然也能稱為 object file
* Symbols: 將名稱對應到一個記憶體位址的 symbol 稱為 defined symbol，名稱沒有對應到記憶體位址的稱為 undefined symbol，例如 int a ，a 就是一個 defined symbol。
* Symbol Visibility: Symbols 通常透過 static and extern 來調整作用範圍，因此分成全域變數、靜態變數或是函數的名稱。一般來說，如果把單獨的c編譯成object file時:
    * defined symbol 為該檔案內的 global variable, static varible 和funciton
    * undefined symbol 為該檔案內的 extern variable 和外部 funciton
* Section:
    * section包含:
        * section 的名稱
        * section 的內容
        * section 的大小
    * 狀態可以分為 LOADABLE 和 ALLOCATABLE:
        * LOADABLE: 執行時該 section 是否需要被載入到記憶體
        * ALLOCATABLE: 先保留記憶體的一塊空間讓程式執行時使用，如.bss
        * 非 LOADABLE 或是 ALLOCATABLE 的 section，通常是裝載著 debugging information
    * Section 記憶體位址: Output section 如果被載入記憶體，會存放兩種記憶體位址 
        * VMA (Virtual Memory Address): 當 output file 運作時，section 會得到這個 address。放在 RAM
        * LMA (Load Memory Address): 當 section 被載入 (loaded) 時，會放置到這個 address。放在 ROM/Flash
        * 通常時候 VMA = LMA
        * 可以使用objdump -h看VMA/LMA資訊
* Linker script 範例
    * .text 放程式碼、.data 放初始化變數、.bss 放未初始化全域變數
    ```c=
    SECTIONS
    {
      . = 0x10000;
      .text : { *(.text) }
      . = 0x8000000;
      .data : { *(.data) }
      .bss : { *(.bss) }
    }
    ```
    * ```.``` 表示 location counter
    * 第三行先將 location counter 移到 0x10000
    * 第四行 把 所有 輸入 object 檔案 的程式機械碼中 ({ *(.text) }) 存放到 輸出object檔案 的 .text 區塊中
    * 後面就以此類推
* 常用的功能
    * ENTRY: 在 linker script 中可以指定 entry point，也就是程式執行時第一個執行的指令開始位置，ex: ```ENTRY(_start)```
    * MEMORY: Linker 預設會取用全部的記憶體，我們可以用 MEMORY 指令指定記憶體大小，ORIGIN 代表起始位置，LENGTH 為長度
    ```c=
    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
      RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
    }
    ```
    * KEEP: 當有開啟 link-time garbage collection 的時候，有時候我們希望某個區段不要被回收掉，這時候就可以使用 KEEP，使用方式為將 input section wildcard entry 放入 KEEP 中，ex: ```KEEP(*(.init))```
    * 接下來SECTION部分，就能用 > 符號把資料寫到指定的位置
    ```c=
    SECTIONS
    {
        .text :
        {
        KEEP(*(.isr_vector))
        *(.text)
        } >FLASH
    }
    ```
    [10分鐘讀懂-linker-scripts](https://blog.louie.lu/2016/11/06/10%E5%88%86%E9%90%98%E8%AE%80%E6%87%82-linker-scripts/)
    [Linker Script初探](http://wen00072.github.io/blog/2014/03/14/study-on-the-linker-script/#assign)
    [linker-script-簡單教學](https://evshary.com/2018/06/02/linker-script-%E7%B0%A1%E5%96%AE%E6%95%99%E5%AD%B8/)
    [GNU ld的linker script簡介](https://www.slideshare.net/zzz00072/gnu-ldlinker-script)

    
