×
单片机 > 单片机程序设计 > 详情

S3C2440 代码重定位实验(二)

发布时间:2024-05-20 发布时间:
|

重定位和链接脚本

经过上一篇文章的讲述,我们了解了无论是从Nor启动还是从Nand启动,都存在代码的重定位问题。我们接下来就要解决程序加载时的代码重定位问题。我们再回顾一下上一篇文章中所使用的Makefile文件,如下所示:


CROSS_COMPILE_PREFIX=arm-linux-

CFLAGS=-g

TARGET=relocate


$(TARGET).bin : CRT0.o uart.o myprintf.o Ctype.o sdram.o util.o $(TARGET).o main.o

$(CROSS_COMPILE_PREFIX)ld -Ttext 0x0000000 -Tdata 0xE40 -o $(TARGET).elf $^

$(CROSS_COMPILE_PREFIX)objcopy -O binary -S $(TARGET).elf $@

$(CROSS_COMPILE_PREFIX)objdump -D $(TARGET).elf > $(TARGET).dis


CRT0.o : CRT0.S

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -c -o $@ $^

myprintf.o : myprintf.c stdarg.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $

uart.o : uart.c stdarg.h s3c2440_soc.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $

$(TARGET).o : $(TARGET).c

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<

main.o : main.c

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<

Ctype.o : Ctype.c Ctype.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<

util.o : util.c util.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<

sdram.o : sdram.c sdram.h

$(CROSS_COMPILE_PREFIX)gcc $(CFLAGS) -o $@ -c $<


clean:

rm -f *.elf *.dis *.bin *.o

从Makefile中可以看出,我们在链接时,指定了代码段的地址为0x0开始,数据段的地址从0xE40开始。


当我们使用Nor Flash启动时,0xE40对应的Nor Flash的地址空间,所以直接修改数据段的全局变量会失败。对此,我们的第一反应就是需要将数据段指定到SDRAM中,这样程序就应该可以正常运行了。为此,我们将数据段的地址指定为0x30000000,即SDRAM的起始地址,重新编译,结果如下图所示:

我们发现,编译出来的BIN文件竟然有769MB,其大小已经远远超出了存储器可以容纳的大小,这样显然是无法烧写到Nor Flash或者是Nand Flash中的。为什么会这样呢?我们可以将BIN文件的大小805306369转换成十六进制,即0x30000001,而查看反汇编文件可知数据段确实是在0x30000000地址处,占据1个字节的大小:

所以BIN文件的结尾地址是0x30000001就得到了解释。这表明如果我们像上面那样指定数据段的偏移地址,那么我们编译得到的文件就会这么大。在BIN文件的代码段和数据段之间显然存在着一个巨大的黑洞。显然,这样做是错误的,需要进一步改进。怎么解决呢?有两个思路。


思路一:只重定位数据段

我们仍然按照上面的方法,指定代码段在0x0处,而数据段仍在0x30000000处,但是让BIN文件中代码段和数据段挨在一起,去掉之前存在的黑洞。这样就可以让BIN文件恢复到4KB大小之内。在程序加载时,需要我们把BIN文件中数据段部分拷贝到指定的链接地址上去。


思路二:重定位整个程序

我们指定代码段时就设置为0x30000000处,其余部分依次偏移,这样BIN文件的大小就和原来完全一样。这样,在加载时,我们需要把整个程序都进行重定位,全部拷贝至SDRAM中。


无论时方法一,还是方法二,在链接时都涉及到更细致的操作,单靠链接命令行参数已经无法满足要求,需要引入一个非常有用的工具——链接脚本。


链接脚本

通常情况下,所有链接过程都有一个链接脚本来予以控制。而用来书写链接脚本的语言就是链接命令语言。链接脚本的主要目的就是用来描述给定的输入目标文件中的所有的段是怎样映射到单一的输出目标文件中的,与此同时,链接脚本还控制着输出目标文件在内存中的布局。绝大多数情况下,这两点就是链接脚本所负责的全部工作了。但是,链接脚本还可以使用下文所描述的命令来指导链接器进行其他一些操作。


链接器总会使用一个链接脚本。如果我们不指定一个的话,链接器会使用默认的链接脚本。关于这个内置的默认链接脚本,我们可以使用–verbose命令行选项来查看。同时一些命令行选项参数会影响默认的链接脚本的行为。


macrofun@BIgBrotherU:~$ arm-linux-ld --verbose

GNU ld (Sourcery G++ Lite 2008q3-72) 2.18.50.20080215

Supported emulations:

armelf_linux_eabi

armelfb_linux_eabi

using internal linker script:

==================================================

/* Script for -z combreloc: combine and sort reloc sections */

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",

"elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY(_start)

SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");

SECTIONS

{

/* Read-only sections, merged into text segment: */

PROVIDE (__executable_start = 0x00008000); . = 0x00008000 + SIZEOF_HEADERS;

.interp : { *(.interp) }

.note.gnu.build-id : { *(.note.gnu.build-id) }

.hash : { *(.hash) }

.gnu.hash : { *(.gnu.hash) }

.dynsym : { *(.dynsym) }

.dynstr : { *(.dynstr) }

.gnu.version : { *(.gnu.version) }

.gnu.version_d : { *(.gnu.version_d) }

.gnu.version_r : { *(.gnu.version_r) }

.rel.dyn :

{

*(.rel.init)

*(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)

*(.rel.fini)

*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)

*(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)

*(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)

*(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)

*(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)

*(.rel.ctors)

*(.rel.dtors)

*(.rel.got)

*(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)

}

.rela.dyn :

{

*(.rela.init)

*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)

*(.rela.fini)

*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)

*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)

*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)

*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)

*(.rela.ctors)

*(.rela.dtors)

*(.rela.got)

*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)

}

.rel.plt : { *(.rel.plt) }

.rela.plt : { *(.rela.plt) }

.init :

{

KEEP (*(.init))

} =0

.plt : { *(.plt) }

.text :

{

*(.text .stub .text.* .gnu.linkonce.t.*)

/* .gnu.warning sections are handled specially by elf32.em. */

*(.gnu.warning)

*(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)

} =0

.fini :

{

KEEP (*(.fini))

} =0

PROVIDE (__etext = .);

PROVIDE (_etext = .);

PROVIDE (etext = .);

.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }

.rodata1 : { *(.rodata1) }

.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) }

__exidx_start = .;

.ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }

__exidx_end = .;

.eh_frame_hdr : { *(.eh_frame_hdr) }

.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }

.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }

/* Adjust the address for the data segment. We want to adjust up to

the same address within the page on the next page up. */

. = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));

/* Exception handling */

.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }

.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }

/* Thread Local Storage sections */

.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }

.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }

.preinit_array :

{

PROVIDE_HIDDEN (__preinit_array_start = .);

KEEP (*(.preinit_array))

PROVIDE_HIDDEN (__preinit_array_end = .);

}

.init_array :

{

PROVIDE_HIDDEN (__init_array_start = .);

KEEP (*(SORT(.init_array.*)))

KEEP (*(.init_array))

PROVIDE_HIDDEN (__init_array_end = .);

}

.fini_array :

{

PROVIDE_HIDDEN (__fini_array_start = .);

KEEP (*(.fini_array))

KEEP (*(SORT(.fini_array.*)))

PROVIDE_HIDDEN (__fini_array_end = .);

}

.ctors :

{

/* gcc uses crtbegin.o to find the start of

the constructors, so we make sure it is

first. Because this is a wildcard, it

doesn't matte


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
单片机制作超级流水灯