lds链接脚本基础与例子分析
1.基礎
(1)段
注:有的編譯器會將沒有初始化的變量保存在COMMON段,等到鏈接時再將其放入到bss段。
(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.disarm-linux-objcopy
復制一個目標文件的內容到另一個文件中,可以使用不同于源文件的格式來輸出目的文件,即可以進行格式轉換。常用arm-linux-objcopy將ELF格式的可執行文件轉換為bin二進制文件。
arm-linux-objdump
顯示二進制文件信息,常用來查看反匯編代碼。
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文件內定義的全局變量可以在鏈接腳本內被賦值,此處賦值的意思是更改變量的地址。
將變量/函數放入指定段中
__attribute__((section("section_name"))) :將作用的函數或數據放入指定名為"section_name"對應的段中。
鏈接腳本變量
鏈接腳本中定義的變量可以在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
編譯時使用鏈接腳本:
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修改
- .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链接脚本基础与例子分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5700新手学堂,疑难杂症问题解决荟萃
- 下一篇: 什么是SSL数字证书