2011年(15)
分类: 嵌入式
2011-12-23 17:32:17
需要在cpu- 类型 字段名 说明 int bits_per_word; 字长 int bits_per_address; 地址长 int bits_per_byte; 字节长 enum bfd_architecture arch 枚举类型bfd_architecture,该类型由archures.c自动生成。生成的符号形如bfd_arch_cr16 unsigned long mach 型号(Machine value),用于区分CPU变种 const char *arch_name; 体系(Architecture)名字的缩写 const char *printable_name; 体系(Architecture)名字的全名 unsigned int section_align_power; 节对齐指数(Section alignment power) i386,mcore是3,arm是4 bfd_boolean the_default; 指出是否是该体系中的默认型号。如果是默认型号,应该处于链表的开头,以便通过*next访问其他型号。 const struct bfd_arch_info * (*compatible)(…) 判断一个两个体系是否兼容,可以引用bfd_default_compatible(…) bfd_boolean (*scan) (…); 判断一个字符串是否属于该体系,可以引用bfd_default_scan (…) const struct bfd_arch_info *next; 若该文件内包含多个该结构定义,则在这里指出
在`elfNN-CPU.c'中定义下列内容:
定义方法为使用宏定义,如#define XX 1
标识符 | 定义方法 |
TARGET_BIG_SYM | 必须提供给目标向量(target vector)一个独一无二长度C语言名字。这个名字应该同样地出现在下列文件中:targets.c, config.bfd, configure.in. |
TARGET_BIG_NAME | 定义为目标向量所使用的名字。用户会通过该名字指定目标文件格式。链接器脚本中通常也会使用它。 |
TARGET_LITTLE_SYM | 略 |
TARGET_LITTLE_NAME | 略 |
ELF_ARCH | BFD体系(bfd_architecture'枚举类型,通常是bfd_arch_CPU)。 |
ELF_MACHINE_CODE | ELF头中的e_machine。 |
ELF_MAXPAGESIZE | 定义为虚拟页的最大值。无内存管理硬件就设为1。 |
GAS
GAS中读写目标文件的逻辑全部调用BFD来实现,而自身专注于汇编指令的处理上面。
BFD对GAS的要求,即BFD对输入数据的要求:
a.GAS产生新的重定位类型。
b.GAS调用bfd_install_relocation。
2) 类型常量
在bfd-in2.h中添加新的重定位类型,即一组枚举常量。由于是填入到已有的结构,只有保证形式一致即可。
3) 全局常量和宏定义
在include/elf目录新建CPU.h定义在BFD之外使用的新的类型。
尤其需要定义下列宏:
`START_RELOC_NUMBERS'
`RELOC_NUMBER'
`FAKE_RELOC'
`EMPTY_RELOC'
`END_RELOC_NUMBERS'
仿照同类定义即可。
结构
Howto结构保存重定位参数信息,供多个函数使用。
首先,在
注:假定只有RELA重定位。
类型 | 字段名 | 说明 |
unsigned int | type; | 重定位类型内部标识 |
unsigned int | rightshift; | 重定位结果右移。 |
int | size; | 该值非一般观念上的大小。使用bfd_get_reloc_size来获取实际大小。 0->8 1->16 2->32 -2->32,relocation=-relocation -1->16,relocation=-relocation 4->64 |
unsigned int | bitsize | 重定位项的位数。溢出检查使用。 |
bfd_boolean | pc_relative | 指出重定位是否相对于addend地址。 |
unsigned int | bitpos | 重定位 |
enum complain_overflow | complain_on_overflow | 需要检查的溢出错误类型 |
bfd_reloc_status_type | (*special_function) (…) | 常规重定位之前调用 |
char* | name | 重定位名字 |
bfd_boolean | partial_inplace | 使用RELA应该设为FALSE。其他信息: When performing a partial link(ld –r) relocation will be modified. Values of TRUE should be looked on with suspicion. For relocs that aren't used in partial links (e.g. GOT stuff) it doesn't matter what this is set to. |
bfd_vma | src_mask | 计算前掩码 |
bfd_vma | dst_mask | 计算后掩码 |
bfd_boolean | pcrel_offset | PC相关的重定位 |
然后,实现下列函数:
函数名 | 输入 | 输出 | 实现提示 |
bfd_reloc_type_lookup | 重定位类型枚举值 | 重定位信息结构 | 从前面定义的howto数组中查找对应项并返回 |
bfd_reloc_name_lookup | 重定位类型名称 | 重定位信息结构 | |
elf_info_to_howto | arelent结构 Rela结构 | 重定位信息结构 | 使用ELFNN_R_TYPE宏从输入获取重定位类型,再返回前面定义的对应的howto结构指针。 |
检查下列函数的代码,确保他们能正确利用howto结构的数据:
bfd_install_relocation
bfd_elf_generic_reloc
bfd_perform_relocation
elf_backend_relocate_section
_bfd_final_link_relocate
_bfd_relocate_contents
应该通过详细分析所以函数的代码,来确定每一个重定位是否都能够被正确处理。若分析代码量太大,可以结合输入输出测试来检查。最后,如果确定某(些)函数功能不完善的,需要在elf32-cpu.c中重写对应的函数。
重定位elf_backend_relocate_section函数会被链接器调用,完成对所有的节的内容的重定位。
定义了该函数后,会自动把下列后端替换为ELF专用的默认函数:
接口 | 不定义elf_backend_relocate_section | 定义elf_backend_relocate_section |
bfd_elfNN_bfd_link_hash_table_create | _bfd_generic_link_hash_table_create | _bfd_elf_link_hash_table_create |
bfd_elfNN_bfd_link_add_symbols | _bfd_generic_link_add_symbols | bfd_elf_link_add_symbols |
bfd_elfNN_bfd_final_link | _bfd_generic_final_link | bfd_elf_final_link |
值得注意的是,不定义elf_backend_relocate_section,链接器会调用bfd_perform_relocation完成同样的工作。但若不使用ELF专用函数,可能缺失部分ELF格式特有的信息。
函数名:elf_backend_relocate_section
输入:节内容,重定位、符号相关信息。
功能:重定位节内容。
输出:重定位过后的节内容,若可再重定位就还要包括重定位信息。
返回值:FALSE表示错误,RUE表示成功,2表示成功且重定位结果应该保存。
函数主流程:
for 每一个重定位 do
If 重定位类型是未知的 then 输出异常,continue
If 是本地重定位 then
计算重定位结果
else
对全局符号重定位(RELOC_FOR_GLOBAL_SYMBOL)
end
if 该节被移除 then continue
if 可再重定位 then continue
特殊重定位预处理
最终连接重定位(_bfd_final_link_relocate)
根据返回值生成异常信息
end
实现提示:
_perform_relocation是实现同样功能的最通用函数,只不过不处理ELF特有信息,可以参考。
5. 链接时Relax典型的relax场景为:一个指令集会有多个间接跳转指令,区别在于跳转的距离。如果不知道实际的跳转距离,则只能采用调整距离最大的那条跳转指令。如果在汇编链接时发现跳转距离不大,就可以relax为一条距离较近的跳转指令,这样性能和代码体积可以优化。在汇编阶段,重定位接口bfd_relocate_section中,在其中的final_link_relocate部分,增加修改指令(relax代码优化)的功能。
在GNU Linker中,链接时的relax实现方法为:
在bfd_relax_section接口中(调用链接器的时候添加-relax参数),循环遍历重定位项,对目标重定位类型,适当修改指令。由于修改指令一般需要改变大小,所以,每次修改指令,也需要遍历其他重定位项,修改他们的信息,同时将其他数据一并移动。算法复杂度接近O(n^2),并且数据移动的I/O操作也是极为耗时的。