Hexagon Linker Control Scripts(2)

240 篇文章 11 订阅


(2))

3 示例脚本

3.1 概述

本章介绍了一个链接器控制脚本的示例,并详细描述了它的所有重要元素。

3.2 目标配置

脚本指定的目标环境是 Qualcomm® MDM8200™ 芯片的内存映射,在以独立模式运行的 Hexagon 处理器模拟器上执行。

Hexagon 处理器是所有 Qualcomm MSM/MDM 芯片的组成部分。 该处理器通常配置为芯片上控制器 ARM 处理器的从属设备,该控制器控制 Hexagon 处理器复位,并负责配置与芯片相关的不同存储器。

MSM/MDM 芯片支持以下项目:

  • 外部存储器接口 (EBI)
  • 内部堆栈式存储器接口 (SMI)
  • TCM等片上存储器(内部SRAM)

EBI/SMI 接口支持多种存储设备,例如 DDR、SRAM、NAND/NOR Flash、LCD 等。

图 3-1 显示了 MDM8200 芯片的高级存储器映射。 芯片的链接描述文件将使用此内存映射作为参考,并将各种代码/数据部分放置在用户可用的内存部分中。

注意 
    在 MDM8200 内存配置中,Hexagon 处理器和 ARM 控制器共享相同的内存映射。  Hexagon 处理器使用虚拟内存,其虚拟到物理页面映射通常设置为 1:1(例如,虚拟地址 0x3C00 0000 对应于相同的物理地址)。 

    用户需要确保根据软件映像的映射正确设置页表。

3.3 脚本代码

本节列出了 MDM8200 示例链接描述文件的完整代码。 代码说明在以下部分中介绍。

1 		OUTPUT_FORMAT("elf32-littleqdsp6",
2 												"elf32-bigqdsp6",
3 												"elf32-littleqdsp6")
4 		OUTPUT_ARCH(qdsp6)
5 		ENTRY(start)
6
		SEARCH_DIR("/prj/dsp/qdsp6/austin/builds/hexbuild/allplat/hexframe/10844/Linux64/install/BT_20101
2150100_lnx64/gnu/qdsp6/lib");
7
8 		/*================================================================*/
9		 /* Configurable parameters */
10 	/*================================================================*/
11
12 	/* Image base address (virtual and physical).
13 	It must also match the .start address in the linker command line. */
14	 __image_addr_base__ = 0x03C00000;
15
16	 /* IMEM. We put some data buffer here. */
17	 __imem_locn__ = 0x58000000;
18
19 	SECTIONS
20 	{
21	 . = __image_addr_base__;
22	 . = ALIGN (DEFINED (TEXTALIGN)? (TEXTALIGN * 1K) : 4096);
23
24	 .start :
25	 {
26	 	KEEP (*(.start))
27	 } =0x00c0007f
28
29	 .init :
30	 {
31 		KEEP (*(.init))
32	 } =0x00c0007f
33
34	 .text : { *(.text) }
35
36	 .my_custom_section :
37	 {
38		 *(.my_custom_section)
39	 }
40
41	 .fini :
42	 {
43		 KEEP (*(.fini))
44	 } =0x00c0007f
45
46 	/* Constants */
47	 . = ALIGN (DEFINED (RODATAALIGN)? (RODATAALIGN * 1K) : 4096);
48	 .rodata :
49	 {
50		 *(.rodata*)
51	 }
52
53	 /* Data start. */
54	 /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */
56	 . = ALIGN(4096) + (. & (4096 - 1));
57	 . = ALIGN (DEFINED (DATAALIGN)? (DATAALIGN * 1K) : 4096);
58
59	 .ctors :
60	 {
61		 /* gcc uses crtbegin.o to find the start of
62		 the constructors, so we make sure it is
63		 first. Because this is a wildcard, it
64		 doesn't matter if the user does not
65		 actually link against crtbegin.o; the
66		 linker won't look for a file to match a
67		 wildcard. The wildcard also means that it
68		 doesn't matter which directory crtbegin.o
69		 is in. */
70	 	KEEP (*crtbegin.o(.ctors))
71	 	KEEP (*crtbegin?.o(.ctors))
72	 	/* We don't want to include the .ctor section from
73 			the crtend.o file until after the sorted ctors.
74		 	The .ctor section from the crtend file contains the
75			 end of ctors marker and it must be last */
76	 	KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o fini.o) .ctors))
77	 	KEEP (*(SORT(.ctors.*)))
78	 	KEEP (*(.ctors))
79	 }
80 	.dtors :
81 	{
82		 KEEP (*crtbegin.o(.dtors))
83		 KEEP (*crtbegin?.o(.dtors))
84		 KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o fini.o) .dtors))
85		 KEEP (*(SORT(.dtors.*)))
86		 KEEP (*(.dtors))
87	 }
88
89	 .data : { *(.data) }
90
91	 . = ALIGN (64K);
92	 __bss_start = .;
93	 .bss :
94	 {
95		 *(.bss*)
96		 *(COMMON)
97		 /* Align here to ensure that the .bss section occupies space up to _end. Align after .bss to ensure correct alignment even if the  .bss section disappears because there are no input sections. */
100 		. = ALIGN(. != 0 ? 64 : 1);
101 	}
102 	. = ALIGN(64);
103		_end = .;
104
105 	/* Small data start. */
106 	. = ALIGN (DEFINED (DATAALIGN)? (DATAALIGN * 1K) : 512K);
107 	. = ALIGN (64);
108 	.sdata :
109 	{
110 	PROVIDE (_SDA_BASE_ = .);
111 		*(.sdata*)
112 		*(.scommon*)
113 		*(.lit*)
114 		*(.gnu.linkonce*)
115 	}
116 	. = ALIGN (64);
117 	.sbss :
118 	{
119 		PROVIDE (__sbss_start = .);
120		 	PROVIDE (___sbss_start = .);
121 		*(.sbss*)
122 		*(.scommon .scommon.*)
123 		. = ALIGN (. != 0 ? 64 : 1);
124 		PROVIDE (__sbss_end = .);
125 		PROVIDE (___sbss_end = .);
126 	}
127
128 	/* place buffer at specific memory locn. */
129 	.data_at_specific_locn(__imem_locn__) :
130 	{
131 		my_data.o (.my_custom_data)
132 	}
133
134 	. = ALIGN(64);
135 	PROVIDE (end = .);
136
137 	.comment 0 : { *(.comment) }
138
139 	/* Group all remaining sections here */
140 	__misc_start__ = . ;
141 	.misc : { *(*) }
142
143 }

3.3.1 初始脚本行

示例脚本的最初几行描述了输出格式、目标代码库的搜索位置以及默认入口点的名称(在本例中为 start)。

3.3.2 SECTIONS 命令

脚本中最重要的命令是 SECTIONS 命令,它出现在 3.3 节示例脚本的第 19 行。 每个脚本都必须包含此命令 - 每个链接描述文件中都不需要出现其他脚本命令。 SECTIONS 命令指定如何合并这些部分,以及它们要放置的位置。

注意
    在多对象程序中,具有相同名称的部分(例如,.text)可能出现在多个输入文件中。  在这种情况下,链接器的默认行为是将节连续放置,并修补标签引用以反映新的节地址。

3.3.3 定位计数器

在 SECTIONS 命令之后的块中,“.” 字符代表位置计数器。 位置计数器表示将加载该部分的当前地址。

开始时位置计数器的默认值为零。 但是,用户可以通过以下两种方式之一覆盖默认位置计数器值:

  • 可以通过在链接描述文件中显式设置点 (.) 变量来设置位置计数器(参见第 3.3 节中示例脚本的第 21 行):
    . = __image_addr_base__;
  • 或者,可以通过 gcc(或 ld)将加载地址作为命令行参数传递来更改位置计数器的值:
    hexagon-gcc -Wl,-section-start,.start=0x1e000000 -Wl,-Tmy_linker_script.txt foo1.o foo2.o foo.o main_foo.o my_data.o -o main_foo

在上面的命令示例中,选项 -Wl,-section-start,.start=0x1e000000 被指定给 gcc,并且 gcc 将该选项传递给链接器,以便在指定的地址 0x1e000000 处设置节 .start。 可以在命令行上重复此选项以定位多个部分。

注意
    分号通常仅出于美观原因在链接描述文件中用作分隔符,否则将被忽略。  但是,它们在符号分配的末尾是必需的,如上所示或在 PHDRS 命令的末尾。

3.3.4 使用通配符放置部分

链接器从输入目标文件中收集所有相似的部分,并将它们组合成一个部分。 通配符 (*) 指示链接器从所有输入目标文件中收集相似部分:

.text : { *(.text ) }
.text : { *(.text.*) }
注意
    通配符模式只匹配在命令行上明确指定的文件。  链接器不会搜索目录来扩展通配符。

3.3.5 用户定义的部分

在 3.3 节的示例脚本的第 36 行中,为一组函数的放置指定了一个用户定义的节,如下所示:

.my_custom_section :
    {
        *(.my_custom_section)
    }

在本文档提供的示例代码中,源文件 foo.c、foo1.c 和 foo2.c 指定将某些函数放置在名为 my_custom_section 的用户定义部分中,在 C 源文件中使用以下语法:

void fooA (void) __attribute__ ((section (".my_custom_section")));

用户定义的部分通常用于在非标准内存配置中存储代码或数据,或施加定制的对齐限制。 例如,一个程序可以定义一个专用的数据段来访问内存映射的硬件设备。 或者用户可能希望将数据缓冲区与某些对齐限制对齐,并将其放置在具有所需对齐的新部分中。 3.3 节示例脚本的第 131 行显示了放置自定义数据部分的示例。

3.3.6 对齐

链接描述文件通常对代码和数据施加不同类型的对齐限制。 代码和数据不能共存于同一个 64 字节对齐区域。 程序员必须确保遵循此对齐限制; 否则,无法保证指令和数据缓存之间的一致性。

在 3.3 节的示例脚本的几个地方,ALIGN 指令用于将当前位置计数器设置为 64 字节边界:

. = ALIGN (64)

ALIGN 指令不会改变位置计数器的值——它只是对其进行算术运算。
用户定义的内存布局必须符合以下限制:

  • 部分必须是页面对齐的(对于 Hexagon,这通常是 4K 对齐的边界)
  • 应用程序中的部分不能在虚拟内存中重叠
  • 应用程序内部和应用程序之间的部分在物理内存中不得重叠

当链接器命令行选项 -gc-sections 时,KEEP 指令可用于保护特定部分免受垃圾收集。

.start :
{
    KEEP (*(.start))
} =0x00c0007f

符号“=0x00c0007f”指定要在间隙中写入的数据。
可以使用以下语法将缓冲区放置在给定的内存位置:

.data_at_specific_locn(__imem_locn__) :
{
    my_data.o (.my_custom_data)
}

这里 __imem_locn__ 指定了输出段的虚拟内存地址。

如果指定了地址,则将输出段地址设置为指定值。 如果既没有指定地址也没有指定区域,则在与输出节的对齐要求(定义为输出节中包含的任何输入节的最严格对齐)对齐后,地址将设置为位置计数器的当前值)。

例如,考虑放置数据的部分:
.data : { *(.data) }
此行将数据段的地址设置为与 .data 输入段的最严格对齐方式对齐的位置计数器的当前值。 为节指定地址会更改位置计数器的值,前提是该节不为空(忽略空节)。

数据部分应从远离文本的单独页面边界开始。 在 3.3 节示例脚本的第 56 行,这是通过使用对齐限制来实现的:

. = ALIGN(4096) + (. & (4096 - 1));
. = ALIGN (DEFINED (DATAALIGN)? (DATAALIGN * 1K) : 4096);

编写链接描述文件后,可以通过使用 binutils 工具(例如 readelf 或 objdump)检查输出 ELF 映像来静态验证脚本。 例如,要交叉检查链接描述文件是否将 fooA() 放置在 .my_custom_section 部分中,请在输出 ELF 图像上运行 objdump:

$ hexagon-objdump -t main_foo | grep fooA
03c0bc74 g F .my_custom_section 00000018 fooA

3.4 验证输出部分的位置

要验证链接描述文件是否正确放置了输出部分,请在输出 ELF 映像上运行 readelf 实用程序。

例如:

$> hexagon-readelf -e main_foo
ELF Header:
    Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
    Class: ELF32
    Data: 2's complement, little endian
    Version:1 (current)
    OS/ABI:UNIX - System V
    ABI Version:0
    Type: EXEC (Executable file)
    Machine:Qualcomm QDSP6
    Version:0x1
    Entry point address:0x3c00000<<<< image base address (line# 14
linker script)
    Start of program headers:52 (bytes into file)
    Start of section headers:73956 (bytes into file)
    Flags: 0x1, V2
    Size of this header:52 (bytes)
    Size of program headers:32 (bytes)
    Number of program headers:8
    Size of section headers:40 (bytes)
    Number of section headers:19
    Section header string table index:16
Section Headers:
    [Nr] NameTypeAddrOffSizeES FlgAl
    [ 0] NULL 0000000000000000000000 00
    [ 1] .startPROGBITS03c00000002000003cc000 WAX64
    [ 2] .initPROGBITS03c03cc0005cc000006000 AX32
    [ 3] .textPROGBITS03c04000006000007c7400 AX4096
    [ 4] .my_custom_sectio PROGBITS03c0bc7400dc7400003800 AX4
    [ 5] .fini PROGBITS03c0bcc000dcc000003400 AX32
    [ 6] .rodataPROGBITS03c0c00000e00000060000 A8
    [ 7] .ctorsPROGBITS03c0e00000f00000000c00 WA4
    [ 8] .dtorsPROGBITS03c0e00c00f00c00000c00 WA4
    [ 9] .dataPROGBITS03c0e01800f0180006a800 WA8
    [10] .bssNOBITS03c10000010000000c4000 WA8
    [11] .sdataPROGBITS03c8000001000000033800 Wap8
    [12] .sbssNOBITS03c8034001033800008000 Wap8
    [13] .data_at_specificPROGBITS5800000001100000100000 WA8
----
Program Headers:
    Type Offset VirtAddPhysAddr FileSiz MemSizFlgAlign

验证输出节位置的另一种方法是指示链接器生成映射文件。 映射文件可用于检查输出图像,方法是直接将选项 -Map 传递给链接器,或者将选项 -Wl,-Map, 传递给 gcc。

    有关链接描述文件语法的更多信息,请参见第 1.2 节。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值