国产精品久久久久影院,成人午夜福利视频,国产精品久久久久高潮,国产精品 欧美 亚洲 制服,国产精品白浆无码流出

歷史上的今天

今天是:2025年07月22日(星期二)

2018年07月22日 | S3c2440代碼重定位詳解

發(fā)布者:RadiantGlow 來源: eefocus關(guān)鍵字:S3c2440  代碼重定位 手機看文章 掃描二維碼
隨時隨地手機看文章

第001節(jié)段的概念重定位的引入

S3C2440的CPU可以直接給SDRAM發(fā)送命令、給Nor Flash發(fā)送命令、給4K的片上SDRAM發(fā)送命令,但是不能直接給Nand Flsh發(fā)送命令

假如把程序燒寫到Nand Flsh上,即向Nand Flsh燒入* bin* 文件,CPU是無法從Nand Flsh中取代碼執(zhí)行的。

為什還可以使用NAND啟動?

  • 上電后,Nand啟動硬件會自動把Nand Flsh前4K復(fù)制到SRAM;

  • CPU從0地址運行SRAM;

如果我的程序大于4K怎么辦?
前4K的代碼需要把整個程序讀出來放到SDRAM(即代碼重定位)。

如果從Nor Flash啟動,會出現(xiàn)什么問題?

將撥動開關(guān)撥到Nor Flash啟動時,此時CPU認為的 0地址 在Nor Flash上面,片內(nèi)內(nèi)存SRAM的基地址就變成了0x40000000(Nand啟動時片內(nèi)內(nèi)存SRAM的基地址基地址是0), 
由于Nor Flash特性:可以像內(nèi)存一樣讀,但不能像內(nèi)存直接寫,因此需要把全局變量和靜態(tài)變量重定位 放到SDRAM里。 
這里寫圖片描述

例如執(zhí)行如下幾條匯編指令

 MOV R0, #0

 LDR R1, [R0] @讀有效

 STR R1, [R0] @寫無效


當程序中含有需要寫的全局變量或靜態(tài)變量時,假如是在Nand Flash可以正常操作,如果是在Nor Flash,修改無效。因此我們需要把全局變量和靜態(tài)變量重定位 放到SDRAM



#include "s3c2440_soc.h"

#include "uart.h"

#include "init.h"


char g_Char = 'A';  //定義一個全局變量

const char g_Char2 = 'B'; //定義固定的全局變量

int g_A = 0;

int g_B;


int main(void)

{

    uart0_init();


    while (1)

    {

        putchar(g_Char); /*讓g_Char輸出*/

        g_Char++;         /* nor啟動時, 此代碼無效 */

        delay(1000000);

    }



    return 0;

}


編譯運行查看是否有效果


查看sdram.dis文件 發(fā)現(xiàn)data數(shù)據(jù)段放在了0x00008474這個地址導(dǎo)致 程序太大


在makefile中加入這么一句話


 arm-linux-ld -Ttext 0 ** -Tdata 0x700 ** start.o led.o uart.o init.o main.o -o sdram.elf


16進制的700就是十進制的2048 

這時我們的bin文件就變?yōu)?049


燒寫程序:


燒寫在NORFlash 和 燒寫在NANDFlash觀察這兩種的效果。


設(shè)置成NANDFlash啟動沒有問題 顯示ABCDE…


設(shè)置成NORFlash啟動顯示AAA…


對于NOR啟動時g_Char++; /* nor啟動時, 此代碼無效 */


Disassembly of section .data:

00000700 <__data_start>:

 700:   Address 0x700 is out of bounds.  //數(shù)據(jù)段

Disassembly of section .rodata:

                            //放在只讀數(shù)據(jù)段內(nèi)

00000474 :         //const char g_Char2 = 'B';

 474:   Address 0x474 is out of bounds.


Disassembly of section .bss:    //bss段


00000804 :             //int g_A = 0;


 804:   00000000    andeq   r0, r0, r0


00000808 :             //int g_B;

 808:   00000000    andeq   r0, r0, r0

Disassembly of section .comment:


一個程序里面有


.text 代碼段

.data 數(shù)據(jù)段

rodata 只讀數(shù)據(jù)段(const全局變量)

bss段 (初始值為0,無初始值的全局變量)

commen 注釋

其中bss段和commen 注釋不保存在bin文件中。


第002節(jié)_鏈接腳本的引入與簡單測試


前面程序運行,發(fā)現(xiàn)從Nand Flash啟動和從Nor Flash啟動表現(xiàn)是不一樣的。


設(shè)置成Nand Flash啟動沒有問題 顯示ABCDE…


設(shè)置成NOor Flash啟動則顯示AAA…


這是什么原因呢?

  • 假如現(xiàn)在是Nor啟動: 
    這里寫圖片描述

Nor Flash就被認為是0地址,g_Char被放在0x700后面。CPU上電后從0地址開始執(zhí)行,它能讀取Nor Flash上的代碼,打印出A,當進行g(shù)_Char++的時候,寫操作操作無效,下次讀取的數(shù)據(jù)仍然是A。

  • 假如現(xiàn)在是Nor啟動: 
    這里寫圖片描述 
    上電后,Nand Flash前4K代碼就被自動的復(fù)制到SRAM里面,SRAM是CPU認為的0地址。CPU上電后從0地址開始執(zhí)行,它讀取SRAM上的代碼,并g_Char++修改變量,下次讀取的數(shù)據(jù)就依次增加了。

為了解決Nor Flash里面的變量不能寫的問題,我們把變量所在的數(shù)據(jù)段放在SDRAM里面,看行不行。 

修改Makefile 指定數(shù)據(jù)段為0x30000000 -Tdata 0x30000000:


 arm-linux-ld -Ttext 0 -Tdata 0x30000000  start.o led.o uart.o init.o main.o -o sdram.elf

這樣的話編譯出來的bin文件 從0地址 到 0x30000000地址 文件大小有700多MB,代碼段和數(shù)據(jù)段直接有間隔,稱之為黑洞 
這里寫圖片描述

解決黑洞有兩個辦法:


第一個方法 

把數(shù)據(jù)段的g_Char和代碼段靠在一起;

燒寫在Nor Flash上面;

運行時把g_char(全局變量)復(fù)制到SDRAM,即0x3000000位置(重定位);

第二個方法 

讓文件直接從0x30000000開始,全局變量在0x3……;

燒寫Nor Flash上 0地址處;

運行會把整個代碼段數(shù)據(jù)段(整個程序)從0地址復(fù)制到SDRAM的0x30000000(重定位);

這兩個方法的區(qū)別是前者只重定位了數(shù)據(jù)段,后者重定位了數(shù)據(jù)段和代碼段。


參考文檔 

[http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html Using LD, the GNU linker]


第一種辦法如何實現(xiàn) 

修改Makefile的代碼段地址,使用鏈接腳本sdram.lds指定。


 #arm-linux-ld -Ttext 0 -Tdata 0x30000000  start.o led.o uart.o init.o main.o -o sdram.elf

 arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf


鏈接腳本的語法:


SECTIONS {

...

secname start BLOCK(align) (NOLOAD) : AT ( ldadr )

  { contents } >region :phdr =fill

...

}


我們需要依次排列 代碼段、只讀數(shù)據(jù)段、數(shù)據(jù)段、.bss段、.common。


其中數(shù)據(jù)段放在0x700,但運行時在0x3000000:


SECTIONS {

   .text   0  : { *(.text) }//所有文件的.text

   .rodata  : { *(.rodata) } //只讀數(shù)據(jù)段

   .data 0x30000000 : AT(0x700) { *(.data) } //放在0x700,但運行時在0x3000000

   .bss  : { *(.bss) *(.COMMON) }//所有文件的bss段,所有文件的.COMMON段

}


重新編譯后燒寫bin文件,發(fā)現(xiàn)啟動后顯示亂碼。原因是我們從0x30000000處獲取g_Char,但在這之前,并沒有在0x30000000處準備好數(shù)據(jù)。因此需要重定位數(shù)據(jù)段,將0x700的數(shù)據(jù)移動到0x30000000處,在start.S加入:


 bl sdram_init


 /* 重定位data段 */

 mov r1, #0x700 

 ldr r0, [r1]

 mov r1, #0x30000000

 str r0, [r1]


 bl main


上面的這種方法,只能復(fù)制0x700處的一位數(shù)據(jù),不太通用,下面寫一個更加通用的復(fù)制方法:


鏈接腳本修改如下:


SECTIONS {

   .text   0  : { *(.text) }

   .rodata  : { *(.rodata) }

   .data 0x30000000 : AT(0x700) 

   { 

      data_load_addr = LOADADDR(.data);

      data_start = . ;//等于當前位置

      *(.data)  //等于數(shù)據(jù)段的大小

      data_end = . ;//等于當前位置

   }

   .bss  : { *(.bss) *(.COMMON) }

}


修改start.S


    bl sdram_init   


    /* 重定位data段 */

    ldr r1, =data_load_addr  /* data段在bin文件中的地址, 加載地址 */

    ldr r2, =data_start      /* data段在重定位地址, 運行時的地址 */

    ldr r3, =data_end        /* data段結(jié)束地址 */


cpy:

    ldrb r4, [r1] //從r1讀到r4

    strb r4, [r2] //r4存放到r2

    add r1, r1, #1 //r1+1

    add r2, r2, #1 //r2+1

    cmp r2, r3 //r2 r3比較

    bne cpy //如果不等則繼續(xù)拷貝


    bl main


第003節(jié)_鏈接腳本的解析


鏈接腳本的語法


SECTIONS {

...

secname start BLOCK(align) (NOLOAD) : AT ( ldadr )

  { contents } >region :phdr =fill

...

}


解釋:


 secname  :段名

 start  :起始地址:運行時的地址(runtime addr);重定位地址(relocate addr)

 AT ( ldadr ) :可有可無(load addr:加載地址) 不寫時LoadAddr = runtime addr

 { contents } 的內(nèi)容: 

 start.o //內(nèi)容為start.o文件

 *(.text)所有的代碼段文件

 start.o *(.text)文件


elf文件格式


1 鏈接得到elf文件,含有地址信息(load addr)


2 使用加載器


:: 2.1 對于裸板是JTAG調(diào)試工具


:: 2.2 對于APP,加載器也是APP 把elf文件解析讀入內(nèi)存的加載地址


3 運行程序


4 如果loadaddr != runtimeaddr程序本身要重定位


核心程序運行時應(yīng)該位于 runtimeaddr(reloate addr)或者鏈接地址


bin文件


1 elf生成bin文件 


2 硬件機制啟動


3 如果bin文件所在位置 不等于runtimeaddr ,程序本身實現(xiàn)重定位


bin文件/elf文件都不保存bss段 這些都是初始值為0 或者沒有初始化的全局變量


程序運行時把bss段對應(yīng)的空間清零


做個實驗,把全局變量g_A以16進制打印出來


/* 0xABCDEF12 */

void printHex(unsigned int val)

{

    int i;

    unsigned char arr[8];


    /* 先取出每一位的值 */

    for (i = 0; i < 8; i++)

    {

        arr[i] = val & 0xf;

        val >>= 4;   /* arr[0] = 2, arr[1] = 1, arr[2] = 0xF */

    }


    /* 打印 */

    puts("0x");

    for (i = 7; i >=0; i--)

    {

        if (arr[i] >= 0 && arr[i] <= 9)

            putchar(arr[i] + '0');

        else if(arr[i] >= 0xA && arr[i] <= 0xF)

            putchar(arr[i] - 0xA + 'A');

    }

}



//打印初始值為0的變量

int g_A = 0;

int g_B;


int main(void)

{

    uart0_init();


    puts("\n\rg_A = ");

    printHex(g_A);

    puts("\n\r");


上述代碼,沒有清理bss段 g_A等于莫名奇妙的值 并不等于0 所以需要清理bss段


修改lds鏈接文件


SECTIONS {

   .text   0  : { *(.text) }

   .rodata  : { *(.rodata) }

   .data 0x30000000 : AT(0x700) 

   { 

      data_load_addr = LOADADDR(.data);

      data_start = . ;

      *(.data) 

      data_end = . ;

   }


   bss_start = .; //bss開始地址是當前位置

   .bss  : { *(.bss) *(.COMMON) }

   bss_end = .; //bss結(jié)束地址也是當前位置

}


修改start.s,清除bss段


/* 清除BSS段 */

ldr r1, =bss_start

ldr r2, =bss_end

mov r3, #0

clean:

    strb r3, [r1]

    add r1, r1, #1

    cmp r1, r2

    bne clean


    bl main

halt:


現(xiàn)在的代碼全局變量就是為0,通過幾行代碼,就可以少幾十個甚至上千個全局變量的存儲空間。


第004節(jié)_拷貝代碼和鏈接腳本的改進


本節(jié)進行拷貝代碼的改進和鏈接腳本的改進。 

前面重定位時,需要ldrb命令從的Nor Flash讀取1字節(jié)數(shù)據(jù),再用strb命令將1字節(jié)數(shù)據(jù)寫到SDRAM里面。


cpy:

    ldrb r4, [r1] /*首先從flash讀出一個字節(jié)*/ 

    strb r4, [r2] /*讓后把數(shù)據(jù)寫到SDRAM*/

    add r1, r1, #1

    add r2, r2, #1

    cmp r2, r3

    bne cpy


JZ2440上的Nor Flash是16位,SDRAM是32位。 

假設(shè)現(xiàn)在需要復(fù)制16byte數(shù)據(jù), 

采用ldrb命令每次只能加載1byte,因此CPU需要發(fā)出16次命令,內(nèi)存控制器每次收到命令后,訪問硬件Nor Flash,因此需要訪問硬件16次; 

同理,訪問SDRAM時,CPU需要執(zhí)行strb 16次,內(nèi)存控制器每次收到命令后,訪問硬件SDRAM,也要16次,這樣總共訪問32次。


現(xiàn)在對其進行改進,使用ldr從Nor Flash中讀,ldr命令每次加載4字節(jié)數(shù)據(jù),因此CPU只需執(zhí)行4次,但由于Nor Flash是16位的,內(nèi)存控制器每次收到CPU命令后,需要拆分成兩次訪問,因此需要訪問硬件8次; 

使用str寫SDRAM,CPU只需執(zhí)行4次,內(nèi)存控制器每次收到命令后,直接硬件訪問32位的SDRAM,因此這里只需要4次,這樣總共訪問只需要12次。 

在整個操作中,花費時間最長的就是硬件訪問,改進后代碼,減少了硬件訪問的次數(shù),極大的提高了效率。
這里寫圖片描述

根據(jù)上面原理修改代碼,修改start.S:


cpy:

    ldr r4, [r1]

    str r4, [r2]

    add r1, r1, #4 //r1加4

    add r2, r2, #4 //r2加4

    cmp r2, r3 //如果r2 =< r3繼續(xù)拷貝

    ble cpy


/* 清除BSS段 */ 

    ldr r1, =bss_start

    ldr r2, =bss_end

    mov r3, #0

clean:

    str r3, [r1]

    add r1, r1, #4

    cmp r1, r2 //如果r1 =< r2則繼續(xù)拷貝

    ble clean


    bl main


然后編譯燒寫,發(fā)現(xiàn)啟動后沒有輸出字符。修改主程序,嘗試以整數(shù)格式輸出字符,發(fā)現(xiàn)輸出的數(shù)從0開始,應(yīng)該是 

全局變量被破壞了。


屏蔽掉start.S里面的清理命令,測試是否是清除bss段是清除了全局變量。


clean:

    //str r3, [r1] //注釋掉此句話,str不僅把bss段清除,把全局變量這些也清除了

    add r1, r1, #4

    cmp r1, r2

    ble clean


    bl main


屏蔽后,正常輸出,鎖定了問題大致位置。查看反匯編文件,原來是沒有向4取整。 

修改鏈接腳本讓bss段,使用ALIGN(4)向4取整。


SECTIONS {

   .text   0  : { *(.text) }

   .rodata  : { *(.rodata) }

   .data 0x30000000 : AT(0x700) 

   { 

      data_load_addr = LOADADDR(.data);

      . = ALIGN(4);

      data_start = . ;

      *(.data) 

      data_end = . ;

   }


   . = ALIGN(4);//讓當前地址向4對齊

   bss_start = .;

   .bss  : { *(.bss) *(.COMMON) }

   bss_end = .;

}


現(xiàn)在重新編譯燒寫,測試結(jié)果正常。 

再次查看反匯編文件,發(fā)現(xiàn)現(xiàn)在bss段以4字節(jié)對齊,清理bss段也是正常的。


Disassembly of section .bss:


30000004 :

30000004:   00000000    andeq   r0, r0, r0


30000008 :

30000008:   00000000    andeq   r0, r0, r0

Disassembly of section .comment:


同樣的問題也會出在代碼重定位這里,如何保證data段起始地址也是向4對齊呢? 

也是使用ALIGN(4)向4取整。


SECTIONS

{

    . = 0x30000000;


    . = ALIGN(4);

    .text      :

    {

      *(.text)

    }


    . = ALIGN(4);

    .rodata : { *(.rodata) }


    . = ALIGN(4);

    .data : { *(.data) }


    . = ALIGN(4);

    __bss_start = .;

    .bss : { *(.bss) *(.COMMON) }

    _end = .;

}


Uboot是裸機的集大成者,可以參考uboot鏈接腳本也是類似的。


第005節(jié)_代碼重定位與位置無關(guān)碼


一個程序,由代碼段、只讀數(shù)據(jù)段、數(shù)據(jù)段、bss段等組成。 

程序一開始可以燒在Nor Flash上面,運行時代碼段仍可以在Nor Flash運行,但對于數(shù)據(jù)段,就必須把數(shù)據(jù)段移到SDRAM中,因為只要在SDRAM里面,數(shù)據(jù)段的變量才能被寫操作,把程序從一個位置移動到另一個位置,把這個過程就稱為重定位。 

前面的例子,我們只是重定位了數(shù)據(jù)段,這里我們再嘗試重定位整個代碼。


先梳理下把整個程序復(fù)制到SDRAM需要哪些技術(shù)細節(jié):

1. 把程序從Flash復(fù)制到運行地址,鏈接腳本中就要指定運行地址為SDRAM地址;

2. 編譯鏈接生成的bin文件,需要在SDRAM地址上運行,但上電后卻必須先在0地址運行,這就要求重定位之前的代碼與位置無關(guān)(是位置無關(guān)碼);


參考Uboot修改鏈接腳本:


SECTIONS

{

    . = 0x30000000;


    . = ALIGN(4);

    .text      :

    {

      *(.text)

    }


    . = ALIGN(4);

    .rodata : { *(.rodata) }


    . = ALIGN(4);

    .data : { *(.data) }


    . = ALIGN(4);

    __bss_start = .;

    .bss : { *(.bss) *(.COMMON) }

    _end = .;

}


現(xiàn)在我們寫的這個鏈接腳本,稱為一體式鏈接腳本,對比前面的分體式鏈接腳本區(qū)別在于代碼段和數(shù)據(jù)段的存放位置是否是分開的。


例如現(xiàn)在的一體式鏈接腳本的代碼段后面依次就是只讀數(shù)據(jù)段、數(shù)據(jù)段、bss段,都是連續(xù)在一起的。 

分體式鏈接腳本則是代碼段、只讀數(shù)據(jù)段,中間相關(guān)很遠之后才是數(shù)據(jù)段、bss段。


我們以后的代碼更多的采用一體式鏈接腳本,原因如下:

1. 分體式鏈接腳本適合單片機,單片機自帶有flash,不需要再將代碼復(fù)制到內(nèi)存占用空間。而我們的嵌入式系統(tǒng)內(nèi)存非常大,沒必要節(jié)省這點空間,并且有些嵌入式系統(tǒng)沒有Nor Flash等可以直接運行代碼的Flash,就需要從Nand Flash或者SD卡復(fù)制整個代碼到內(nèi)存;

2. JTAG等調(diào)試器一般只支持一體式鏈接腳本;


修改start.S段



    /* 重定位text, rodata, data段整個程序 */

    mov r1, #0

    ldr r2, =_start         /* 第1條指令運行時的地址 */

    ldr r3, =__bss_start    /* bss段的起始地址 */


cpy:

    ldr r4, [r1]

    str r4, [r2]

    add r1, r1, #4

    add r2, r2, #4

    cmp r2, r3

    ble cpy



    /* 清除BSS段 */

    ldr r1, =__bss_start

    ldr r2, =_end

    mov r3, #0

clean:

    str r3, [r1]

    add r1, r1, #4

    cmp r1, r2

    ble clean


    bl main  


halt:

    b halt


將修改后的代碼重新編譯燒寫在Nor Flash上,上電運行。 

對本代碼的啟動情況進行分析:
這里寫圖片描述

在生成的bin文件里,代碼保存的位置是0x30000000。隨后燒寫到NOR Flash的0地址,但代碼的結(jié)構(gòu)沒有變化。之后再重定位到SDRAM。

查看反匯編:

3000005c:   eb000106    bl  30000478  

30000060:   e3a01000    mov r1, #0  ; 0x0
30000064:   e59f204c    ldr r2, [pc, #76]   ; 300000b8 <.text+0xb8>
30000068:   e59f304c    ldr r3, [pc, #76]   ; 300000bc <.text+0xbc>123456

這里的bl 30000478不是跳轉(zhuǎn)到30000478,這個時候sdram并未初始化; 
為了驗證,我們做另一個實驗,修改連接腳本sdram.lds, 鏈接地址改為0x32000478,編譯,查看反匯編:

3000005c:   eb000106    bl  30000478  

30000060:   e3a01000    mov r1, #0  ; 0x0
30000064:   e59f204c    ldr r2, [pc, #76]   ; 300000b8 <.text+0xb8>
30000068:   e59f304c    ldr r3, [pc, #76]   ; 300000bc <.text+0xbc>123456

可以看到現(xiàn)在變成了bl 30000478,但兩個的機器碼eb000106都是一樣的,機器碼一樣,執(zhí)行的內(nèi)容肯定都是一樣的。 
因此這里并不是跳轉(zhuǎn)到顯示的地址,而是跳轉(zhuǎn)到: pc + offset,這個由鏈接器決定。

假設(shè)程序從0x30000000執(zhí)行,當前指令地址:0x3000005c ,那么就是跳到0x30000478;如果程序從0運行,當前指令地址:0x5c 調(diào)到:0x00000478

跳轉(zhuǎn)到某個地址并不是由bl指令所決定,而是由當前pc值決定。反匯編顯示這個值只是為了方便讀代碼。

重點: 
反匯編文件里, B或BL 某個值,只是起到方便查看的作用,并不是真的跳轉(zhuǎn)。

怎么寫位置無關(guān)碼?

  • 使用相對跳轉(zhuǎn)命令 b或bl;

  • 重定位之前,不可使用絕對地址,不可訪問全局變量/靜態(tài)變量,也不可訪問有初始值的數(shù)組(因為初始值放在rodata里,使用絕對地址來訪問);

  • 重定位之后,使用ldr pc = xxx,跳轉(zhuǎn)到/runtime地址;

寫位置無關(guān)碼,其實就是不使用絕對地址,判斷有沒有使用絕對地址,除了前面的幾個規(guī)則,最根本的辦法看反匯編。

因此,前面的例子程序使用bl命令相對跳轉(zhuǎn),程序仍在NOR/sram執(zhí)行,要想讓main函數(shù)在SDRAM執(zhí)行,需要修改代碼:

 //bl main  /*bl相對跳轉(zhuǎn),程序仍在NOR/sram執(zhí)行*/
 ldr pc, =main/*絕對跳轉(zhuǎn),跳到SDRAM*/123

第006節(jié)重定位清除BSS段的C函數(shù)實現(xiàn)

在前面,我們使用匯編程序來實現(xiàn)了重定位和清bss段,本節(jié)我們將使用C語言,實現(xiàn)重定位和清除bss段。

1.打開start.S把原來的匯編代碼刪除改為調(diào)用C函數(shù)

    /* 重定位text, rodata, data段整個程序 */
    mov r1, #0
    ldr r2, =_start         /* 第1條指令運行時的地址 */
    ldr r3, =__bss_start    /* bss段的起始地址 */cpy:
    ldr r4, [r1]
    str r4, [r2]
    add r1, r1, #4
    add r2, r2, #4
    cmp r2, r3
    ble cpy    /* 清除BSS段 */
    ldr r1, =__bss_start
    ldr r2, =_end
    mov r3, #0clean:
    str r3, [r1]
    add r1, r1, #4
    cmp r1, r2
    ble clean1234567891011121314151617181920212223

改為

    /* 重定位text, rodata, data段整個程序 */
    mov r0, #0
    ldr r1, =_start         /* 第1條指令運行時的地址 */
    ldr r2, =__bss_start    /* bss段的起始地址 */
    sub r2, r2, r1          /*長度*/


    bl copy2sdram  /* src, dest, len */

    /* 清除BSS段 */
    ldr r0, =__bss_start
    ldr r1, =_end

    bl clean_bss  /* start, end */123456789101112131415
  1. 在init.c 實現(xiàn)如上兩個C函數(shù)

void copy2sdram(volatile unsigned int *src, volatile unsigned int *dest, unsigned int len)  /* src, dest, len */{    unsigned int i = 0;    while (i < len)
    {
        *dest++ = *src++;
        i += 4;
    }
}void clean_bss(volatile unsigned int *start, volatile unsigned int *end)  /* start, end */{    while (start <= end)
    {
        *start++ = 0;
    }
}12345678910111213141516171819202122

匯編中,為C語言傳入的參數(shù),依次就是R1、R2、R3。 
編譯,燒寫運行沒有問題。

我們假設(shè)不想?yún)R編傳入?yún)?shù),而是C語言直接取參數(shù)。

  1. 修改start.S 跳轉(zhuǎn)到C函數(shù)不需要任何參數(shù)

    bl sdram_init    //bl sdram_init2     /* 用到有初始值的數(shù)組, 不是位置無關(guān)碼 */

    /* 重定位text, rodata, data段整個程序 */
    bl copy2sdram    /* 清除BSS段 */
    bl clean_bss123456789
  1. 修改鏈接腳本,讓__code_start 等于當前地址,也就是這里的0x30000000

SECTIONS
{
    . = 0x30000000;

    __code_start = .; //定義__code_start地址位當前地址

    . = ALIGN(4);
    .text      :
    {
      *(.text)
    }

    . = ALIGN(4);
    .rodata : { *(.rodata) }

    . = ALIGN(4);
    .data : { *(.data) }

    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss) *(.COMMON) }
    _end = .;
}1234567891011121314151617181920212223

3.修改init.c 用函數(shù)來獲取參數(shù)

void copy2sdram(void)
{    /* 要從lds文件中獲得 __code_start, __bss_start
     * 然后從0地址把數(shù)據(jù)復(fù)制到__code_start
     */

    extern int __code_start, __bss_start;//聲明外部變量

    volatile unsigned int *dest = (volatile unsigned int *)&__code_start;    volatile unsigned int *end = (volatile unsigned int *)&__bss_start;    volatile unsigned int *src = (volatile unsigned int *)0;    while (dest < end)
    {
        *dest++ = *src++;
    }
}void clean_bss(void)
{    /* 要從lds文件中獲得 __bss_start, _end
     */
    extern int _end, __bss_start;    volatile unsigned int *start = (volatile unsigned int *)&__bss_start;    volatile unsigned int *end = (volatile unsigned int *)&_end;    while (start <= end)
    {
        *start++ = 0;
    }
}1234567891011121314151617181920212223242526272829303132333435

編譯燒寫運行 ,沒有問題。

總結(jié): 
C函數(shù)怎么使用lds文件總的變量abc?

  • 在C函數(shù)中聲明改變量為extern外部變量類型,比如:extern int abc;

  • 使用時,要取址,比如:int *p = &abc;//p的只即為lds文件中abc的值

匯編文件中可以直接使用外部鏈接腳本中的變量,但C函數(shù)中要加上取址符號。 
解釋一下原因: 
C函數(shù)中,定義一個全局變量int g_i;,程序中必然有4字節(jié)的空間留出來給這個變量g_i。

假如我們的lds文件中有很多變量

lds{
    a1 = ;
    a2 = ;
    a3 = ;
    ...
}

如果我們C程序只用到幾個變量,完全沒必要全部存儲lds里面的所有變量,C程序是不保存lds中的變量的。 
對于萬一要用到的變量,編譯程序時,有一個symbol table符號表:
這里寫圖片描述

如何使用symbol table符號表?

  • 對于常規(guī)變量g_i,得到里面的值,使用&g_i得到addr;

  • 為了保持代碼的一致,對于lds中的a1,使用&a1得到里面的值;

這只是一個編譯器的小技巧,不用深究。

結(jié)論:

  • C程序中不保存lds文件中的變量,lds再大也不影響;

  • 借助symbol table保存lds的變量,使用時加上”&”得到它的值,鏈接腳本的變量要在C程序中聲明為外部變量,任何類型都可以;


關(guān)鍵字:S3c2440  代碼重定位 引用地址:S3c2440代碼重定位詳解

上一篇:s3c2440中斷程序(燒錄到NORFlash,運行在SDRAM中)
下一篇:存儲控制器與外設(shè)之間的關(guān)系

推薦閱讀

據(jù)國際機器人聯(lián)合會(R)預(yù)測,到2020年,將有170萬個新工業(yè)機器人應(yīng)用到世界各地的工廠中。 工業(yè)機器人是實現(xiàn)柔性自動化的基礎(chǔ)設(shè)施,越來越靈活的機器人產(chǎn)品滿足工廠的需求,有利于加快企業(yè)對市場的響應(yīng)速度。2017年全球機器人市場保持著強勁的增長趨勢,報告顯示,亞洲澳大利亞地區(qū)的機器人安裝量增加了21%,美洲機器人供應(yīng)增長16%,歐洲增長8%。 1、...
隨著人工智能技術(shù)的發(fā)展,我國服務(wù)機器人市場也迎來了爆發(fā)式增長,據(jù)《中國服務(wù)機器人行業(yè)發(fā)展前景與投資戰(zhàn)略規(guī)劃分析報告》統(tǒng)計數(shù)據(jù)顯示,2013-2018年整體處于飛速增長階段,2018年中國服務(wù)機器人市場規(guī)模有望達到18.4億美元,同比增長約45.3%,高于全球服務(wù)機器人市場增速。服務(wù)機器人市場的增速必然帶動其相關(guān)核心技術(shù)增長,移動機器人底盤作為服務(wù)...
7月22日消息,realme預(yù)告全新系列新機即將發(fā)布?! 」俜綇娬{(diào),realme全新系列新機沒有套娃,這次絕對會套進你的心。  當前型號為RMX2121的realme新機通過工信部入網(wǎng)許可,它有可能就是即將登場的全新系列新品?! 淖C件照來看,它采用的是挖孔屏,背部為矩陣相機。機身尺寸為160.8x75.2x8.5mm,支持雙模5G,電池容量為4400mAh?! 『蛂ealm...
2021年7月22日,致力于亞太地區(qū)市場的領(lǐng)先半導(dǎo)體元器件分銷商---大聯(lián)大控股宣布,其旗下品佳推出基于恩智浦(NXP)TEA2206的240W 5G open frame解決方案。 圖示1-大聯(lián)大品佳推出基于NXP產(chǎn)品的5G open frame解決方案的展示板圖當前,系統(tǒng)對于電源設(shè)計要求正在變得愈發(fā)苛刻。隨著能源法規(guī)不斷完善,針對效率的要求不斷提高,在電源已做到極致的情況下,...

史海拾趣

問答坊 | AI 解惑

想要課件。。謝謝

老師把I2C總線串行擴展技術(shù)的課件,我看看,還有程序。。。。。謝謝…

查看全部問答∨

電子大賽D題請進

本帖最后由 paulhyde 于 2014-9-15 09:08 編輯 39914530  …

查看全部問答∨

怎樣用c語言進行RAM的讀寫操作?

比如說我要在00H到10H單元依次寫入一段數(shù)據(jù)然后再讀取出來,這個在匯編容易實現(xiàn),但是C語言怎樣實現(xiàn)呢?…

查看全部問答∨

用鐵通網(wǎng)和大局域網(wǎng)做遠程監(jiān)控的親身經(jīng)歷

轉(zhuǎn)自:中安網(wǎng) 論壇 深圳小姚 以上兩個圖的IP地址(121.34.95.55)分別從路由器上和IP138.com得到,如一至說明你本地的IP地址是公網(wǎng)IP地址,是可以做端口應(yīng)射和域名綁定的,大家可以試一下,很簡單。 關(guān)于用鐵通網(wǎng)和大局域網(wǎng)做遠程監(jiān)控的經(jīng) ...…

查看全部問答∨

請教PXA3XX GPIO中斷的一個問題

新接觸PXA303 CPU,在使用中發(fā)現(xiàn)GPIO1中斷檢測不到, 1. 通過配置GPIO1輸出方波,示波器可以檢測到,說明硬件連接應(yīng)該沒有問題,但是配置為輸入則檢測不到高低電平 2. 配置別的GPIO,可以檢測到中斷,會運行相應(yīng)的中斷處理函數(shù),只有配置GPIO1時檢測不 ...…

查看全部問答∨

誰有帶媒體播放器模塊的WINCE模擬器的?

現(xiàn)在急需這種模擬器,版本越高越好。哪位大俠幫個忙,提供個網(wǎng)址或發(fā)個到郵箱,不勝感激!yccwt@qq.com…

查看全部問答∨

arm 匯編問題

MACRO     MTC15   $cpureg, $cp15reg     mcr p15, 0, $cpureg, $cp15reg, c0, 0     MEND 這段匯編什么意思? MTC15 是干什么的?…

查看全部問答∨

求基于TMS320F2812的圖形點陣液晶屏及鍵盤控制的原理圖 pcb 軟件流程及代碼

求基于TMS320F2812的圖形點陣液晶屏及鍵盤控制的原理圖 pcb 軟件流程及代碼 用DSP控制液晶屏和鍵盤,模擬簡單功能示波器界面。能顯示波形、電壓值、頻率;能通過鍵盤調(diào)節(jié)顯示的波形,電壓幅值以及頻率。原理圖 pcb 軟件流程及代碼  郵箱 ...…

查看全部問答∨

大家使用什么仿真器和編譯器來開發(fā)LM3S2793?

我現(xiàn)在用的仿真器是JLINK V8(山寨),編譯器KEIL4.12,KEIL4.22,KEIL4.54,IAR6.3.0.1,IAR6.4(都試過),老是出現(xiàn)各種各樣的問題,最主要的問題是燒寫能燒進去,就是不能正常仿真。PS:需要使用片內(nèi)FLASH進行編程。     用KEIL的話, ...…

查看全部問答∨
小廣播
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機文章

 
EEWorld訂閱號

 
EEWorld服務(wù)號

 
汽車開發(fā)圈

 
機器人開發(fā)圈

電子工程世界版權(quán)所有 京ICP證060456號 京ICP備10001474號-1 電信業(yè)務(wù)審批[2006]字第258號函 京公網(wǎng)安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved