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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

lds链接脚本基础与例子分析

發布時間:2023/12/15 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 lds链接脚本基础与例子分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.基礎

(1)段

  • .data段包含初始值非0的全局變量(不管靜態還是非靜態)
  • .rodata段包含被const修飾的初始值非0的全局變量
  • .bss段包含初始值為0或未初始的全局變量(不管有沒有const修飾,也不管是靜態還是非靜態)局部變量保存在棧中
    :有的編譯器會將沒有初始化的變量保存在COMMON段,等到鏈接時再將其放入到bss段。
  • .text段保存代碼
  • (2)指定不同段的地址(不用鏈接腳本)

    編譯過程

    編譯: arm-linux-gcc -c -o led_on.o led_on.S #或led_on.c鏈接: arm-linux-ld -Ttext 0 led_on.o -o led_on.elf生成bin: arm-linux-objcopy -o binary -S led_on.elf led_on.bin 生成反匯編: arm-linux-objdump -D led_on.elf > led_on.dis

    arm-linux-objcopy
    復制一個目標文件的內容到另一個文件中,可以使用不同于源文件的格式來輸出目的文件,即可以進行格式轉換。常用arm-linux-objcopy將ELF格式的可執行文件轉換為bin二進制文件。

    arm-linux-objcopy -O binary -S elf_file bin_file-O:使用指定的格式來輸出文件-S:不從源文件中復制重定位信息和符號信息到目標文件中去

    arm-linux-objdump
    顯示二進制文件信息,常用來查看反匯編代碼。

    //ELF轉為反匯編 arm-linux-objdump -D elf_file>dis_file //二進制轉為反匯編 arm-linux-objdump -D -b binary -m arm bin_file > dis_file-D:反匯編所有段-b:指定目標碼格式,不是必須的。可通過arm-linux-objdump -i查看支持的目標碼格式-m machine:指定反匯編目標文件時所使用的架構,當待反匯編文件本身沒有描述架構信息的時候,這個選項很有用。

    arm-linux-ld

    -Ttext startaddr #直接指定代碼段地址 -Tdata startaddr #直接指定數據段地址 -Tbss startaddr #直接指定bss段地址

    例子

    arm-linux-gcc -c -o link.o link.s arm-linux-ld -Ttext 0x00000000 link.o -o link_elf_0x00000000//啟動后PC=0x00000000 arm-linux-ld -Ttext 0x30000000 link.o -o link_elf_0x30000000//啟動后PC=0x30000000
    • 只指定代碼段的地址,則數據段和bss段緊跟著代碼段存放

    (3)鏈接地址和加載地址

    • 鏈接地址是程序實際運行的地址(內存)
    • 加載地址指的是程序編譯后的存放地址(Flash)

    (4)鏈接腳本格式

    • 鏈接腳本由一系列命令組成,每個命令由一個關鍵字或一條對符號的賦值語句組成,命令間用分號分開。
    • 若文件名或格式名內包含分號,需用單引號引用起來

    (5)鏈接腳本的語法

    SECTIONS {...secname start ALIGN(align) (NOLOAD) : AT ( ldadr ){ contents } >region :phdr =fill... }
    • secname和contents是必須的,前者用來命名這個段,后者用來確定代碼中的什么部分放在這個段中。
    • start:段重定位地址,也稱為VMA,即運行地址。如果代碼中有位置相關的指令,程序在運行時,這個段必須放在這個地址上。
    • ALIGN(align):雖然start指定了運行地址,但是仍可以使用BLOCK(align)來指定對齊的要求一這個對齊的地址才是真正的運行地址。
    • (NOLOAD):用來告訴加載器,在運行時不用加載這個段。這個選項只有在有操作系統的情況下才有意義。
    • AT (ldadr):指定這個段在編譯出來的映象文件中的地址,稱為LMA,即加載地址。若不指定默認加載地址等于運行地址。通過這個選項,可以控制各段分別保存在輸出文件中不同的位置。
    • >region :phdr =fill:沒用到,不作介紹。

    (6)簡單腳本命令

    • ENTRY(SYMBOL) : 將符號SYMBOL的值設置成入口地址(執行的第一條指令的地址)。
      • 入口地址設置的方法還有(按優先級高低):
        • ld命令行的-e選項
        • 鏈接腳本的ENTRY(SYMBOL)命令
        • 如果定義了start符號,使用start符號值
        • 如果存在.text section, 使用其第一字節的位置值
        • 使用值0
    • INCLUDE filename:包含其他名為filename的鏈接腳本。腳本搜索路徑由-L選項指定。
    • . = ALIGN(4):代碼以4字節對齊
    • LOADADDR(.data):取data段的LMA
    • ADDR(.data):取data段的VMA

    (7)C語言相關
    對C語言符號地址的賦值
    在C文件內定義的全局變量可以在鏈接腳本內被賦值,此處賦值的意思是更改變量的地址。

    /* a.c */ #include <stdio.h> int a = 100; int main(void) {printf( "&a=0x%p ", &a );return 0; }/* a.lds */ a = 3;/* 不使用鏈接腳本 */ gcc -Wall -o a-without-lds a.c &a = 0x8049598/* 使用鏈接腳本 */ $ gcc -Wall -o a-with-lds a.c a.lds &a = 0x3

    將變量/函數放入指定段中
    __attribute__((section("section_name"))) :將作用的函數或數據放入指定名為"section_name"對應的段中。

    變量: const int descriptor[3] __attribute__ ((section ("descr"))) = { 1,2,3 }; long long rw[10] __attribute__ ((section ("RW"))); 函數: void Function_Attributes_section_0(void) __attribute__ ((section ("new_section"))); //聲明時指定段 void Function_Attributes_section_0(void) {static int aStatic =0;aStatic++; }

    鏈接腳本變量
    鏈接腳本中定義的變量可以在C語言中使用extern關鍵字聲明并使用

    2 鏈接腳本例子

    (1)例1:基礎鏈接腳本

    SECTIONS {outputa 0x10000 : //該section的VMA是0x10000{all.o //all.o文件的所有sectionfoo.o (.input1) //foo.o文件的所有(一個文件內可有多個同名section).input1 section}outputb : //該section的VMA是當前定位器符號的修調值(對齊后){foo.o (.input2)foo1.o (.input1)}outputc : //將非all.o、foo.o、foo1.o文件的. input1 section和.input2 section放入輸出outputc section內{*(.input1)*(.input2)} }

    (2)例2
    編譯時使用鏈接腳本:

    arm-linux-ld -Ttimer.lds -o timer_elf head.o init.o interrupt.o main.o

    timer.lds如下

    SECTIONS{. = 0X30000000;.text : {*(.text)}.rodata ALIGN(4) :{*(.rodata)}.data ALIGN(4) :{*(.data)}.bss ALIGN(4) :{*(.bss) *(COMMON)} }

    ①第2行設置運行地址為0x30000000。

    • .為定位器符號,不指定默認為0。存放了某個段后,定位器符號會往后移動這個段的大小長度,所以后面rodata段的地址為0X30000000+.text段大小(還要四字節對齊)

    ②第3行定義了一個名為.text的段,它的內容為*(.text), 表示所有輸入文件的代碼段。這些代碼段被集合在一起,起始運行地址為0x30000000。
    ③第4行定義了一個名為.rodata的段,在輸出文件timer_elf中,它緊挨著.text段存放。其中的ALIGN (4)表示起始運行地址為4字節對齊。假設前面.text段的地址范圍0x30000000~0x300003f1,則.rodata段的地址是4字節對齊后的0x300003f4。
    ④第5、6行的含義與第4行類似。
    (3)例3:輸出SECTION的LMA修改

    SECTIONS {.text 0x1000 : { *(.text) _etext = . ;}.mdata 0x2000 : AT ( ADDR (.text) + SIZEOF (.text) ){_data = . ;*(.data);_edata = . ;}.bss 0x3000 :{_bstart = . ;*(.bss) *(COMMON) ;_bend = . ;} } 程序中可定義上面定義的地址: extern char _etext, _data, _edata, _bstart, _bend;
    • .mdata 0x2000 : AT ( ADDR (.text) + SIZEOF (.text) ):生成的bin文件中mdata段緊跟著text段,但是加載到內存時,mdata段在0x2000處。(若LMA不緊接著上一個,生成的bin文件會很大,中間都是空洞,不方便燒寫)

    再來看一個鏈接腳本片段:

    .data 0x30000000 : AT(0x800) #不指定默認LMA等于VMA
    • 對應的bin文件的data段會燒寫在flash的800處,程序運行時,代碼中實現從0x800(加載地址)復制代碼到0x30000000(鏈接地址)

    總結

    以上是生活随笔為你收集整理的lds链接脚本基础与例子分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。