天天看點

ARM64基礎10:GNU LD連結器介紹

連結器Linker:是一個程式,将一個或多個編譯器或彙編生成的目标檔案,及依賴庫,連結為一個可執行檔案。

GNU Linker采用AT&T連結腳本語言;

連結腳本檔案:包含ld程式連結的規則,其決定輸出可執行檔案的記憶體布局;

LD指令:arm64版本的連接配接器是aarch64-linux-gnu-ld

檢視指令參數:

aarch64-linux-gnu-ld --help      

LD指令的參數有很多,常用的如下:

$(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -Map xxx.map -o $(BUILD_DIR)/xxx.elf  $(OBJ_FILES)      

常用參數:

-T: 指定連接配接腳本;

-Map:輸出一個符号表檔案;

-o: 輸出最終可執行二進制檔案;

基本概念

輸入段(input section),輸出段(output section)

每個段包括name和大小;

段的屬性:

loadable:運作時會加載這些段内容到記憶體中;

allocatable:運作時不加載段的内容;

段的位址:

VMA(virtual memory address):虛拟位址,運作位址;

LMA(load memory address):加載位址

通常ROM的位址為加載位址,而RAM位址為VMA;

連結腳本指令

Entry(symbol): 設定程式的入口函數;

GNU-AS程式入口點有以下幾種:

a.使用-e參數;

b.使用ENTRY(symbol);

c.在.text的最開始的地方;

d.0位址;

INCLUDE filename:引入filename的連結腳本;

OUTPUT filename:輸出二進制檔案,類似指令行的"-o filename"

OUTPUT_FORMAT(bfd):輸出BFD格式;

OUTPUT_ARCH(bfdarch): 輸出處理器體系架構格式

OUTPUT_ARCH(aarch64)
ENTRY(_text)      

指派符号

符号可以像C語言一樣指派;

"."表示目前位置;

symbol = expression;
symbol += expression;
symbol -= expression;
symbol *= expression;
symbol /= expression;
symbol <<= expression;
symbol >>= expression;
symbol &= expression;
symbol |= expression;      

符号的引用

在C語言定義一個變量并初始化,編譯器會配置設定一個符号,且在記憶體中配置設定空間存儲;

在連結腳本中定義的變量,僅僅是在符号表裡定義這個符号,沒有配置設定存儲空間;

xxx.ld
start_of_ROM = .ROM;
end_of_ROM = .ROM + sizeof(.ROM)      

可以在每個段中設定一些符号,以友善C語言通路每個段的起始位址和結束位址;

C語言中,對連結腳本裡定義的符号引用
extern char start_of_ROM[],end_of_ROM[];
char buf[4096];
memcpy(buf, start_of_ROM, end_of_ROM - start_of_ROM);//引用的是位址      

SECTIONS指令

告訴連結器如何把輸入段(input sections)映射到輸出段(output sections),決定輸出程式的記憶體布局;

輸出section的描述符:

ARM64基礎10:GNU LD連結器介紹

LMA加載位址

每個段都有VMA和LMA;

在輸出段描述符中使用“AT”指定LMA, 如果沒有指定,通常LMA=VMA;

構造一個基于ROM的映像檔案,常常會設定輸出端的虛拟位址和加載位址不一樣;

SECTIONS
{
  .text 0x1000:{*(.text)_etext=.;}
  .mdata 0x2000:
  AT(ADDR(.text)+SIZEOF(.text))  //指定加載位址
  {
    _data=.;
    *(.data);
    _edata=.;  //_data和_edata隻是一個符号,用來記錄mdata段的VMA位址
  }
  .bss 0x3000;
  {
    _bstart=.;
    *(.bss)*(COMMON);
    -bend=.;
  }
}      

mdata段的加載位址和連結位址不一樣,是以程式初始化代碼需要把data段内容複制到SDRAM中的虛拟位址中;

ARM64基礎10:GNU LD連結器介紹

常見的内建函數(builtin functions)

ADDR(section):傳回前面已經定義過的段的VMA位址;

ALIGN(n): 傳回下一個與n位元組對齊的位址;

SIZEOF(section):傳回一個段的大小;

MAX/MIN(exp1,exp2):傳回較大/較小值;

TEXT_ROM = 0X90000;
SECTIONS
{
  /*
   * -->location, current addr
   *  0x80000 is entrance address
   */
  . = 0x80000,
  /*
   * first instruction
   **/
  _text_boot = .;
  .text.boot : { *(.text.boot) }  
  _etext_boot = .;

  /*
   * code segment
   */
  _text = .;
  .text : AT(TEXT_ROM)
  { *(.text) }
  _etext = .;

  /*
   * readonly data segment
   */
  _rodata = .;
  .rodata : AT(ADDR(.rodata))
  { *(.rodata) }
  _erodata = .;

  /*
   * data segment
   */
  _data = .;
  .data : { *(.data) }
  _edata = .;

  /*
   * bss segment
   * 8bytes algine
   */
  . = ALIGN(0x8);
  _bss = .;
  .bss : { *(.bss*) } 
  _ebss = .;


  /*
   * alloc a page memory, store page table
   * aligne 4096
   */
  . = ALIGN(4096);
  init_pg_dir = .;
  . +=4096;
}      
master:
/*
 * copy code segment from rom(LMA) TO ram(VMA)
 */
  adr x0, TEXT_ROM
  adr x1, _text
  adr x2, _etext
1:
  ldr x4, [x0], #8
  str x4, [x1], #8
  cmp      

繼續閱讀