- 浏览: 17640 次
最新评论
rtems 下linkcmd分析代码
2010年11月09日
/*
* GP32 Linker script
*
* Written by Philippe Simons
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
*
* http://www.rtems.com/license/LICENSE.
*
* $Id: linkcmds,v 1.1 2008/05/06 21:01:27 joel Exp $
*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm","elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start) ENTRY:用来指定入口地址。
/* SEARCH_DIR(/usr/local/rtems-arm-dev-tools/arm-rtem s/lib); */
MEMORY {
sdram : ORIGIN = 0x30000000, LENGTH = 64M
}
/*
* Declare some sizes.
*/
/* The base for SDRAM is set to umon's APPRAMBASE */
_sdram_base = DEFINED(_sdram_base) ? _sdram_base : 0x30000000;
_sdram_size = DEFINED(_sdram_size) ? _sdram_size : 64M;
_irq_stack_size = DEFINED(_irq_stack_size) ? _irq_stack_size : 0x40000;
_fiq_stack_size = DEFINED(_fiq_stack_size) ? _fiq_stack_size : 0x10000;
_abt_stack_size = DEFINED(_abt_stack_size) ? _abt_stack_size : 0x10000;
_svc_stack_size = DEFINED(_svc_stack_size) ? _svc_stack_size : 0x40000;
/* Do we need any of these for elf?
__DYNAMIC = 0; */
SECTIONS
{
.base :
{
arm_exception_table = .;
arm_reset_vect = .; /* 0x00 */
. += 4;
arm_undef_vect = .; /* 0x04 */
. += 4;
arm_swi_vect = .; /* 0x08 */
. += 4;
arm_iabrt_vect = .; /* 0x0c */
. += 4;
arm_dabrt_vect = .; /* 0x10 */
. += 4;
/* no vector here */
. += 4;
arm_irq_vect = .; /* 0x18 */
. += 4;
arm_fiq_vect = .; /* 0x1c */
. += 4;
/* FIXME: */
rtems_vector_table = .;
. += (8 * 4); /* 8 ARM interrupts */
bsp_vector_table = .;
. += (32 * 4); /* 32 S3C2440 interrupts */
. = ALIGN (0x100);
} > sdram
.text :
{
_axf_text_start = . ;
*(EXCLUDE_FILE (*text.iwram*) .text)
*(.text.*)
*(.stub)
/*
* Special FreeBSD sysctl sections.
*/
. = ALIGN (16);
__start_set_sysctl_set = .;
*(set_sysctl_*);
__stop_set_sysctl_set = ABSOLUTE(.);
*(set_domain_*);
*(set_pseudo_*);
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
*(.gnu.linkonce.t*)
*(.glue_7)
*(.glue_7t)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} > sdram
__text_end = . ;
.init :
{
*(.init)
} > sdram /*=0*/
.jcr :
{
*(.jcr)
} > sdram
.fini :
{
*(.fini)
} > sdram /*=0*/
.rodata :
{
*(.rodata)
*all.rodata*(*)
*(.roda)
*(.rodata.*)
*(.gnu.linkonce.r*)
SORT(CONSTRUCTORS)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} > sdram = 0xff
.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 matter if the user does not actually link against
crtbegin.o; the linker won't look for a file to match a
wildcard. The wildcard also means that it doesn't matter which
directory crtbegin.o is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >sdram = 0
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >sdram = 0
.eh_frame :
{
KEEP (*(.eh_frame))
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >sdram = 0
.gcc_except_table :
{
*(.gcc_except_table)
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >sdram = 0
_axf_ro_end = . ;
.data ALIGN(4) :
{
_axf_data_start = ABSOLUTE(.);
*(.data)
*(.data.*)
*(.gnu.linkonce.d*)
CONSTRUCTORS
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
} >sdram = 0xff
__data_end = . ;
.bss ALIGN(4):
{
_axf_bss_start = ABSOLUTE(.);
_clear_start = .;
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(64);
_clear_end = .;
. = ALIGN (256);
_abt_stack = .;
. += _abt_stack_size;
. = ALIGN (256);
_irq_stack = .;
. += _irq_stack_size;
. = ALIGN (256);
_fiq_stack = .;
. += _fiq_stack_size;
. = ALIGN (256);
_svc_stack = .;
. += _svc_stack_size;
/*
* Ideally, the MMU's translation table would be in SRAM. But we
* don't have any. If we don't use more regions than TLB entries (64),
* the lookup will only happen once for each region.
*/
. = ALIGN (16 * 1024);
_ttbl_base = .;
. += (16 * 1024);
. = ALIGN (1024);
_bss_free_start = .;
} > sdram
_axf_bss_end = . ;
_end = . ;
__end__ = . ;
PROVIDE (end = _end);
/* Debugging stuff follows? */
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/*.stack 0x80000 : { _stack = .; *(.stack) }*/
/* These must appear regardless of . */
}
以前的经验,链接脚本是嵌入式开发,单片机开发相当重要的一个东西。它完成的工作是做PC机软件的同志们不用关心的,但是也是很复杂的一项工作。总结来看链接脚本要告诉连接器
1:输出什么
2:输入是什么,那么obj文件
3:要用什么库,库放在什么地方
4:内存分布地址
5:提供启动代码一些全局地址变量
一般来说链接脚本需要搞清楚这几样事情后才能编写,那arm-gcc-ld的脚本也一定要实现这些功能。对于大多数的链接器来说,对于简单的项目不需要脚本,只是使用命令参数就可以完成了。
MEMORY:
它是用来补充SECTIONS命令的,用来描述目标CPU中可用的内存区域。它是可选的,如果没有这个命令,LD会认为SECTIONS描述的相邻的内存块之间有足够可用的内存。其实很容易理解但是却很少用(我没用过,嘿嘿),在SECTIONS中每个段的分布都没有考虑ARM能够寻址的地址中,ROM,RAM,FLASH是不是连续的。如果不是连续的怎么办?MEMORY就是设置各个区的起始位置,大小,属性的命令,在一个脚本中只能有一个。
举一个例子:
如果你的板子有两段存储,而且很遗憾的是不是连续的,一段是从0x0开始,大小为256K,另一段是从0x40000000开始的大小为4M,你可以在脚本中写入如下的代码来描述你的板子的内存信息。 很显然下面的一句用了简略标签,这并不重要,重要的是怎样使用它,不过在那之前还是想再仔细研究下MEMORY命令的细节。
MEMORY命令的语法是:
MEMORY
{
name (attr) : ORIGIN = origin, LENGTH = len
...
}
name:一个用户定义的名字,Linker将在内部使用它,所以别把它和SECTIONS里用到的文件名,段名等搞重复了,它要求是独一无二的。
attr :如同它的名字一样,这是内存段的属性描述。
`R' Read-only sections.
`W' Read/write sections.
`X' Sections containing executable code.
`A' Allocated sections.
`I' Initialized sections.
`L' Same as I.
`!' Invert the sense of any of the following attributes.
别怪我懒,确实不想再打一遍这个的翻译,而且很久没用英文的俺翻译的估计也不好。总体来说,它是属性就行了。
ORIGIN:这是起始地址
LENGTH:段长
由此可见上面那段实例显示ROM和RAM的明确位置,而且还显示了他们的只能,一个存代码,一个除了存代码什么都可以。
接着就是老问题了,怎么用这个。如果仅仅是规定我的板子有什么特点又不用的话那就是脱了裤子放屁,多此一举。这个问题留在SECTIONS命令中回顾。
SECTIONS:
它是脚本文件中最重要的元素,不可缺省。它的作用就是用来描述输出文件的布局。
SECTIONS命令的语法:
SECTIONS
{
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
这么多的参数中,只有secname和contents是必须的,其他都是可选的参数。也就说它的最简单的格式就是:
SECTIONS
{
...
secname : {
contents
}
...
}
但是注意:secname前后的两个空格是必须的,否则就是不合法输入。
secname定义了段名,其实最开始就忽略了一个重要的因素,arm-gcc-ld脚本需要描述输入和输出,而表面上一看却看不出来什么是输入什么事输入,其实secname和contents就是描述这两个信息的参数。secname是输出文件的段,即输出文件有哪些段,而contents就是描述输出文件的这个段从哪些文件里抽取而来。明确这个了就不难理解为什么SECTIONS命令什么都可以不要就是不能没有这两个参数了。
secname:定义段,但是别以为定义的段一定要是教科书上写的.data,.text这些科班的必须品,你甚至可以创建一个段来放一个美女的图片。
contents:它的语法开始复杂起来了,但是你可以简单的把输入文件写到代码中:
.data : { main.o led.o}
但是结果被列的目标文件中所有的代码都被链接到.data中去了,显然不大符合我们的要求啊。那么还有一种写法:
.data : {
main.o(.data)
main.o(.text) // 也可以这样写 main.o(.data .text)或者main.o(.data , .text)
led.o(.data)
}
这个写法让只有被选中的文件的特殊段被链接到输出文件的.data段了。当然,我们似乎还有更好的写法:
.data : {
*(.data)
}
这样的话,所有目标文件的.data段都被连接到了输出文件中了(这似乎是最常用的方法)。
核心的部分讲完了,开始回顾前面说到了的那些参数:
start:强制链接地址。也许没有讲清楚的是,在SECTIONS中,各个段是按次序排列的,前一个段用到什么地方下一个段接着用,而start就是强迫链接器将当前的段连接到指定的地址中。
.data 0x400000000 : { ..... }
BLOCK(align):说实话,没看懂。只知道用的时候用的比较多的是ALIGN(4)这样的标记,表示排列地址的时候按4的倍数排列,这样做的理由很简单,系统会快。
AT(addr):实现存放地址和加载地址不一致的功能,AT表示在文件中存放的位置,而在内存里呢,按照普通方式存储。至于用处,目前在下不知。
>region:好戏来了,这个region就是前面说的MEMORY命令定义的位置信息。表明当前section所放置的mem有什么特点,如果不符合会怎么样呢?不晓得嘛。
其他略了吧,累了,主要是没中文资料(屁话,有了还用我刻薄吗),其实有点日文资料也行啊,英文我比较苦手)。
注释:
和C语言一样的哦,/**/
其它:
其实ARM-GCC-LD脚本还真的和别的不一样,它的功能要强一些,从manual看,它有三大功能:
1:设置入口函数
2:定义一个变量并赋值
3:描述输入输出文件的链接规则
其实上面的介绍是功能3,1和2都没有讲过。对于arm-gcc-ld脚本来说设置入口函数和定义变量可以在SECTIONS命令大括号里,也可以在外面。语法是:
ENTRY(symbol)
这个symbol应该是某个函数,或者是汇编代码里的一个入口。然而,其实ARM-GCC-LD有很多种方式定义入口,所以当你看到你的脚本里没有这句话而板子运行的很正常的时候别大吃一斤。
1:在连接的时候使用-e参数。
2:在脚本里使用ENTRY
3:如果定义过start这个入口(如果你在汇编里如果本身就有这个名字叫start的入口,那么不用特别的声明也可以)
4:SECTION中.text的第一个入口函数
5:地址为0的指令
其实看了这个我们可以理解是ARM对入口的一个选择优先级,1和2是一样的,显示的指明入口,这也是推荐的方法,没人会觉得程序员故弄玄虚是什么好事情。3是连接器的智能吧,而4和5就是无奈的选择了,程序员没干好的事情CPU只要猜着来处理了,有.text段的话就从它开始执行吧,连.text都没有的就从0x00000000开始执行,至于执行到哪里去了,火星人知道。
关于定义变量,其实一般的脚本都会有的,目的只有一个,给汇编启动代码提地址信息。比如说,一段需要清零的区域在脚本里定义了,而脚本自己不是变形金刚,他不能主动给你清零的,需要你自己的启动代码来清零,清零的代码当然在汇编的启动代码里,它怎么知道需要清零的内存在什么地方?就靠脚本里定义的变量了。没错,事情就是这样的,那也就说一定会有一个提取地址的方法将地址赋给变量了哦,yes!就是小小的一个点"."。 RAM_START = .; 定义了一个RAM_START变量,地址是当前的地址,什么是当前的地址啊?就是链接器在连接的时候根据前面的段排列后的当前位置。当然也可以设置当前位置的值,不过最好不要小于前面排列需要的最小内存。
. = 0x00000000
定义当前地址为0x0。
常用的基本上就这么多了,看一个实例吧: 9 .data : { *(.data) }
10 .rodata : { *(.rodata) }
11 Image_ZI_Base = .;
12 .bss : { *(.bss) }
13 Image_ZI_Limit = .;
21 .debug_info 0 : { *(.debug_info) }
22 .debug_line 0 : { *(.debug_line) }
23 .debug_abbrev 0 : { *(.debug_abbrev)}
24 .debug_frame 0 : { *(.debug_frame) }
25}
18PROVIDE (__stack = .);
19 end = .;
20 _end = .;14 __bss_start__ = .;
15 __bss_end__ = .;
16 __EH_FRAME_BEGIN__ = .;
17 __EH_FRAME_END__ = .;
.bss { *(.bss) *(COMMON) }
这个例子中将所有输入文件的所有通用符号放入输出.bss section内。可以看到COMMOM section的使用方法跟其他section的使用方法是一样的。
有些目标文件格式把通用符号分成几类。例如,在MIPS elf目标文件格式中,把通用符号分成standard common symbols(标准通用符号)和small common symbols(微通用符号,不知道这么译对不对?),此时连接器认为所有standard common symbols在COMMON section内,而small common symbols在.scommon section内。
在一些以前的连接脚本内可以看见[COMMON],相当于*(COMMON),不建议继续使用这种陈旧的方式。
输入section和垃圾回收:
在连接命令行内使用了选项--gc-sections后,连接器可能将某些它认为没用的section过滤掉,此时就有必要强制连接器保留一些特定的 section,可用KEEP()关键字达此目的。如KEEP(*(.text))或KEEP(SORT(*)(.text))
最后看个简单的输入section相关例子:
SECTIONS {
outputa 0x10000 :
{
all.o
foo.o (.input1)
}
outputb :
{
foo.o (.input2)
foo1.o (.input1)
}
outputc :
{
*(.input1)
*(.input2)
}
}
本例中,将all.o文件的所有section和foo.o文件的所有(一个文件内可以有多个同名section).input1 section依次放入输出outputa section内,该section的VMA是0x10000;将foo.o文件的所有.input2 section和foo1.o文件的所有.input1 section依次放入输出outputb section内,该section的VMA是当前定位器符号的修调值(对齐后);将其他文件(非all.o、foo.o、foo1.o)文件的. input1 section和.input2 section放入输出outputc section内。
发表评论
-
MTD NAND Analysis 1
2012-01-20 08:14 722MTD NAND Analysis 1 2011年05月06 ... -
内存映射对于大文件的使用
2012-01-20 08:14 666内存映射对于大文件的使用 2011年04月28日 平时很 ... -
连傻瓜都能看懂的基于代码注入的线程守护技术
2012-01-20 08:14 1660连傻瓜都能看懂的基于 ... -
mmap内存映射操作之一
2012-01-20 08:14 1081mmap内存映射操作之一 2011年06月09日 本博客 ... -
S7-300 PLC的串口无线通讯在火电厂污水处理中的应用
2012-01-19 13:31 686S7-300 PLC的串口无线通讯 ... -
IIS32位和64位切换
2012-01-19 13:31 869IIS32位和64位切换 2011年12月22日 X64 ... -
两平台下的自动登录telnet设置
2012-01-19 13:30 645两平台下的自动登录telnet设置 2011年12月28日 ... -
延时批处理
2012-01-19 13:30 797延时批处理 2012年01月12日 举个例子,我们要延时 ... -
ActionScript垃圾回收
2012-01-17 03:20 964ActionScript垃圾回收 2011年10月26日 ... -
计算机故障大全(二)
2012-01-17 03:20 642计算机故障大全(二) 2010年06月02日 硬盘 ... -
琢石成器――windows环境下32位汇编语言程序设计(第三版)笔记
2012-01-17 03:20 2085琢石成器――windows环境下32位汇编语言程序设计(第三版 ... -
WinCE驱动开发问题精华集锦
2012-01-17 03:20 599WinCE驱动开发问题精华 ... -
钢蹦爸爸的自白
2012-01-16 01:54 668钢蹦爸爸的自白 2009年10月24日 怀着复杂的心情 ... -
爱韩庚的100个理由
2012-01-16 01:54 634爱韩庚的100个理由 2009 ... -
火车上的随想
2012-01-16 01:54 553火车上的随想 2011年02 ... -
jbpm3通过processDefinition.createProcessInstance()为什么能保存ProcessInstance之迷
2012-01-11 01:53 710jbpm3通过processDefinition.create ... -
Android自定义VIEW取得XML的自定义值
2012-01-11 01:53 674Android自定义VIEW取得XML的自定义值 2011年 ... -
常用的正则表达式--Java
2012-01-11 01:53 715常用的正则表达式--Java 2011年08月01日 是 ... -
图片传输问题,IO操作
2012-01-11 01:52 590图片传输问题,IO操作 2011年08月01日 原题: ... -
jsp编码过滤器的配置和使用-用心做程序-iteye技术网站
2012-01-11 01:52 693jsp编码过滤器的配置和使用-用心做程序-iteye技术网站 ...
相关推荐
### rtems移植代码分析之sparc-leon2 #### 概述 RTEMS(实时嵌入式微内核系统)是一种开源实时操作系统,广泛应用于嵌入式领域,特别是对于实时性和可靠性要求极高的场景。本文将深入分析RTEMS在针对SPARC处理器...
这里我们选择在`/opt/rtems`作为交叉编译工具的目标目录,以及在`/usr/rtems`下创建几个子目录,如`download`、`binutils-build`、`gcc-build`和`gdb-build`,用于存放下载的文件和构建过程中的中间代码。...
在32位ARM处理器环境下,开发者需要确保arm-rtems5-gcc.exe正确配置了交叉编译选项,以便生成能够在目标硬件上运行的代码。这涉及到设置正确的架构、目标板模型和库路径等。 使用RTEMS工具链进行开发时,开发者通常...
安装完成后,你会在`/opt`目录下看到一个新的`rtems4.11`子目录,其中包含了用于ARM-RTEMS4.11的工具链。 4. **获取RTEMS源代码**: 使用Fedora 18自带的`git`工具,从RTEMS的Git仓库克隆源代码到`/opt/rtems_src/...
最后,"RTEMS消息管理机制的剖析和验证_谭琦.pdf"对RTEMS的消息传递机制进行了深入分析和验证,这对于理解和优化实时系统中的并发控制和通信效率至关重要。 总的来说,这个资料包提供了一个全面的RTEMS学习路径,从...
9. **实时性能分析**:RTEMS支持实时性能分析工具,如gprof和OProfile,用于分析任务执行时间和资源消耗,帮助优化系统性能。 10. **移植与升级**:RTEMS支持多种处理器架构,如ARM、PowerPC、MIPS等,具备良好的可...
RTEMS经历了多个版本的迭代更新,例如4.8版本中就包括了任务优先级立刻生效、64位纳秒时间戳、优化的代码配置性、最小配置的内核和执行程序elf的缩减、启动时间的优化、任务遍历API的增加、对NIOS II和Blackfin...
在本项目中,RTEMS被移植到IAR编辑器下,这意味着开发者可以利用IAR的强大学习资源和调试工具,如EWRU32,进行高效的代码编写和问题定位。 IAR Workbench是知名的嵌入式开发工具链,以其稳定性和高效性著称。通过...
RTEMS 例子源代码,可以直接编译使用
C语言的API设计使得代码更加高效和可读,同时也方便了与其他C语言项目集成。 RTEMS的实时性能是其另一个重要优势。通过精细的调度策略和低开销的系统调用,RTEMS能确保任务的严格时序,这对于需要精确时间响应的...
- **介绍**:此章节概述了RTEMS源代码的结构及其如何被组织。了解这一点对于理解和维护RTEMS代码至关重要。源代码通常按照功能模块进行分类,以便于管理和扩展。 - **细节**:RTEMS源代码主要分为几个关键部分: - ...
9. **代码分析**:在文档中,可能会深入探讨RTEMS的源代码结构和设计,这对于理解RTEMS的内部工作原理以及进行定制和扩展非常有价值。 通过这个“RTEMS文档连载”,无论是初学者还是经验丰富的开发者,都可以深化对...
RTEMS(Real-Time Executive for Multi-processor System)是一款开放源代码的嵌入式实时操作系统,主要特征是简洁的内核结构、灵活的资源管理、强大的多任务能力和高性能的实时响应能力。RTEMS支持多处理器架构、...
- **i386/pc386**:对于i386/pc386 BSP,可使用qemu进行基本测试、代码覆盖分析、网络应用测试以及图形化应用测试。 值得注意的是,目前的CentOS镜像中尚未包含Eclipse及其RTEMS插件。若需要移除虚拟机中的某些包,...
这些示例代码可以帮助开发者了解如何在RTEMS环境下集成和使用SQLite。 6. **可执行程序与bin程序**:在成功编译和测试后,会产生运行在RTEMS上的SQLite可执行程序和bin文件。bin文件通常是已经链接好的二进制程序,...
这种设计使得在具体应用场景下,RTEMS能够将所有系统模块编译成静态连接库,与用户编写的程序静态链接,以生成运行在目标环境的镜像文件。RTEMS注重实时性,不支持虚拟内存管理,整个系统内核和应用程序共享同一内存...
RTEMS, 即: 实时多处理器系统(Real Time Executive for Multiprocessor Systems),是一个开源的无版税实时嵌入操作系统RTOS。 它最早用于美国国防系统,早期的名称为实时导弹系统(Real Time Executive for Missile ...
RTEMS实时操作系统源代码