Binutils 新目标架构移植指南
虽然 binutils 项目已有一个 100 页的内部指南,但是撰写本文主要针对那些第一次想要开发/移植binutils的人。并且官方指南还存在以下限制:
- 它倾向于记录得详细一些,因此,单个函数描述得很清楚,但很难了解binutils的整体情况。
- 它并不完整,许多最有用的部分(例如,最终重定位的细节)尚未编写。
当前想把 binutils 移植到新架构的开发人员都是通过阅读源代码和查看其他架构是如何移植的来了解binutils是如何工作的。
我本人在将 binutils 移植到 Compact-RISC(又名 CR16)架构时也经历了这个过程,故本文旨在记录学习经验,以帮助他人,特别是那些希望将 binutils 工具移植到新target的人。
Binutils文件组织结构
binutils 源代码主体位于少数目录中,其中某些组件是在内部以及其他项目中使用的库。例如,BFD 库用于 GNU GDB 调试器。这些库有自己的顶级目录。主要目录如图1所示。
以下是有关这些目录的一些简短信息:
include/
包含跨主要组件信息的头文件。例如,由于主模拟器将 GDB (在gdb/
目录中) 链接到模拟器 (在sim/
目录中),因此,它的接口 header 在此处 (remote-sim.h
)。针对特定组件的其他header保存在该组件的目录中。bfd/
包含二进制文件描述符库。该库包含处理特定二进制文件格式的代码,例如 ELF、COFF、SREC 等。如果必须识别新的目标文件类型,则应在此目录添加支持它的代码。opcodes/
包含操作码库,有关于如何汇编和反汇编指令的信息。cpu/
包含名为 CGEN 的实用程序的源文件。该工具可用于自动为opcodes
库以及 GDB 使用的 SIM 模拟器生成 target-specific 源文件。binutils/
虽然这么命名,但这并不是 binutils 的主目录。相反,它是所有没有自己顶级源目录的 binutils 工具的目录,包括objcopy
、objdump
和readelf
等工具。gas/
包含 GNU 汇编器。 target-specific 汇编代码保存在config/子目录中。ld/
包含 GNU 链接器。 target-specific 链接器文件保存在子目录中。gprof/
包含 GNU 分析器。该程序没有任何 target-specific 代码。gold/
包含新的 GNU 链接器。这是一个正在创建的新链接器,用于替换 LD,目前它仍在开发中。elfccp/
包含elfcpp
,一个用于读写 ELF 信息的 C++ 库。目前仅由 GOLD 链接器使用。
此外,在 binutils 源代码版本的顶层还可以找到一些其他目录,它们在 binutils 构建过程中使用,但不是 binutils 项目的一部分。
intl/
包含来自gettext
的 GNU gettext 库。libiberty/
在 POSIX 和glibc
出现之前,这是一个提供一组标准函数的GNU项目。它存在于 binutils ,最有价值的就是其免费存储管理和参数解析功能。
主要功能区和数据结构
二进制文件描述
二进制文件描述 (BFD) 是一个包,无论目标文件格式如何,它都允许应用程序使用相同的例程来操作目标文件。只需创建新的 BFD 后端并将其添加到库中就可以支持新的目标文件格式。
BFD库后端创建了多个数据结构,这些数据结构描述了存储在特定类型目标文件中的数据。最后,为每个单独的架构定义了一个唯一的枚举常量(类型为enum bfd_architecture
)。该常量随后可被用于访问与特定架构相关的各种数据结构。
对于 Compact-RISC,16 位实现(可能是 COFF 或 ELF 二进制文件)的枚举常量为bfd_cr16_arch
。它可用于访问各种结构体,例如:
const bfd_arch_info_type bfd_cr16_arch =
{
16, /* 16 bits in a word. */
32, /* 32 bits in an address. */
8, /* 8 bits in a byte. */
bfd_arch_cr16, /* enum bfd_architecture arch. */
bfd_mach_cr16, /* Machine value, used to distinguish between cr16 variants. */
"cr16", /* Architecture name (short version). */
"cr16", /* Architecture name (long version). */
1, /* Section alignment power. */
TRUE, /* True if this is the default machine for the architecture. */
bfd_default_compatible, /* Function to call to determine if two different architectures are compatible. */
bfd_default_scan, /* Function to call to determine if a given string matches this architecture. */
NULL, /* Pointer to the next CR16 machine architecture. */
};
这个特定的结构体定义在bfd/cpu-<target>.c
文件中。
文件<file_format>-<target>.c
(例如,elf32-cr16.c
)用于为给定的文件格式和架构提供 target-specific 支持。它至少提供了以下信息:
- 将 BFD 重定位枚举映射到 target-specific 重定位类型的
reloc_map
数组。 - 具有 target-specific 重定位详细信息的数组
reloc_howto_type
。以下是来自 cr16 移植的数组 entry :
(R_CR16_NONE, /* Type. */
0, /* Rightshift. */
2, /* Size. */
32, /* Bitsize. */
FALSE, /* PC_relative */
0, /* Bitpos */
complain_overflow_dont, /* Complain_on_overflow */
bfd_elf_generic_reloc, /* Special_function */
"R_CR16_NONE", /* Name */
FALSE, /* Partial_inplace */
0, /* Src_mask */
0, /* Dst_mask */
FALSE), /* PCREL_offset */
- 通过使用与目标相关的配置来定义下面的宏:
#define TARGET_LITTLE_SYM
#define TARGET_LITTLE_NAME
#define ELF_ARCH
#define ELF_MACHINE_CODE
#define ELF_MAXPAGESIZE
#define elf_symbol_leading_char
#define elf_info_to_howto
#define elf_info_to_howto_rel
#define elf_backend_relocate_section
#define elf_backend_gc_mark_hook
#define elf_backend_gc_sweep_hook
#define elf_backend_can_gc_sections
#define elf_backend_rela_normal
#define elf_backend_check_relocs
#define elf_backend_final_write_processing
#define elf_backend_object_p
#define elf_backend_create_dynamic_sections
#define elf_backend_adjust_dynamic_symbol
#define elf_backend_size_dynamic_sections
#define elf_backend_omit_section_dynsym
#define elf_backend_finish_dynamic_sections
#define elf_backend_reloc_type_class
#define elf_backend_want_got_plt
#define elf_backend_plt_readonly
#define elf_backend_want_plt_sym
#define elf_backend_got_header_size
#define bfd_elf32_bfd_reloc_type_lookup
#define bfd_elf32_bfd_reloc_name_lookup
#define bfd_elf32_bfd_relax_section
#define bfd_elf32_bfd_get_relocated_section_contents
#define bfd_elf32_bfd_merge_private_bfd_data
#define bfd_elf32_bfd_link_hash_table_create
#define bfd_elf32_bfd_link_hash_table_free
此外,bfd/
目录中的archures.c
、config.bfd
、Makefile.am
和target.c
文件应该随着必要的 target-specific 更改进行更新。
opcodes
opcodes/
目录应该至少有两个与目标相关的文件——一个用于汇编目标指令,另一个用于反汇编它们。文件名是:
<target>-opc.c
和<target>-opc.h
(头文件是可选的)<target>-dis.c
和<target>-dis.h
(头文件是可选的)
<target>-dis.c
文件包括打印反汇编指令的代码,以及用于适当匹配操作码和操作数的代码。
opcodes 目录中的configure.in
、disassemble.c
和Makefile.am
文件也需要对其进行目标相关的更改。
include
include/
目录包含针对特定目标的头文件,通常位于特定目标文件的子目录中。opcode
信息通常保存在操作码子目录中的特定目标文件中。例如:
include/elf/cr16.h
include/opcode/cr16.h
binutils
该目录不需要任何新的目标特定文件。但configure.tgt
、Makefile.am
和readelf.c
文件应使用所需的任何目标特定信息进行更新。
gas
该gas/config/
子目录包含汇编器的特定目标文件。文件名是tc-<target>.c
和tc-<target>.h
。
上述文件应包括:
- Sizes of (i.e., macros defines):
- Registers
- Instructions (i.e., maximum size)
- Operands
- Operand error types
- Comment character used in assembly code
- Comment character used in assembly code line
- Line separator
- Defining the target-specific
- multi-character options, if any.
- Process machine-dependent command line options in the
md_parse_option
function - Include machine-dependent usage-output in the
md_show_usage
function
- A redefinition of the assemble directive using
md_pseudo_table
- Functions for getting registers along with type, size, and other information
md_begin
function used to initialise/set-up all the tables, etc, that are machine-dependent items of the assembler- Parse functions:
parse_insn
parse_operands
parse_operand
- Print functions:
print_insn
print_operand
print_operands
- Print functions:
- Function to assemble a single instruction
assemble_insn
md_assemble
is the first function called to assemble instruction
在gas/目录本级,需要修改configure.tgt
和Makefile.am文件以引用新文件,并添加对新目标的支持。
- Function to assemble a single instruction
ld
在scripttempl/<format><target>.sc
中,定义架构的默认链接描述文件。
在emulparams/<target><format>.em
中,定义默认的仿真脚本文件。它包含自定义链接器行为所需的任何函数。
在emulparams/<format><target>.sh
中,定义可用于修改默认链接描述文件的任何参数。
在ld/
目录本级,需要更新configure.tgt
文件以添加新的目标信息以及Makefile.am
文件中新的目标信息构建规则。
构建和测试
构建 binutils 工具
构建 binutils 工具需要按照以下步骤操作:
-
配置:使用目标和前缀选项运行配置脚本。例如:
src/configure --target=cr16-elf --prefix=/local/cr16-bintuils
其中,
--target
选项定义了要为其构建 binutils 工具的目标,--prefix
选项是指定 binutils 工具安装目录的位置。 -
构建:运行 make 为上述配置的目标构建 binutils 工具。例如:
make all make install
或者
make all install
注意: 可以使用该选项make -jN
通过使用主机 PC 上的所有处理器/CPU来快速构建工具。例如,如果你的主机 PC 有四个处理器,则可用make -j4
快速构建。
测试 binutils 工具
可以使用 binutils 测试套件来测试上述构建的工具(如gas
、binutls
和 ld
)。运行 binutils 测试套件需要安装DejaGNU软件包并设置 DejaGNU 环境变量。然后可以使用以下命令运行测试:
make check
上面的命令能同时运行binutils
,gas
和ld
测试套件。使用以下命令,可以分别运行binutils
、gas
和ld
测试套件:
make check-binutils
make check-gas
make check-ld
binutils 测试运行完成后,结果将总结于binutils/
目录的binutils.sum
文件中。binutils/binutils.log
文件中还将提供更详细的信息。对于gas
测试套件,结果位于gas/testsuite/gas.sum
和gas/testsuite/gas.log
文件中,对于ld
测试套件,结果位于ld/ld.sum
和ld/ld.log
文件中。
为了在主机和目标不同的环境中进行最全面的测试,DejaGNU 需要一些额外的配置。您可以通过设置 DejaGNU 环境变量来引用合适的配置文件,并在~/boards
目录中定义自定义板配置文件来实现此目的。这些配置文件可用于指定合适的模拟器以及运行测试时如何连接它。
文档
一些 binutils 子目录又包含doc/
子目录。这些文档以 texinfo 编写,可以将它们生成为 PDF、PostScript、HTML 或 info 文件。这些文档不是使用make all
或make doc
来自动构建的。创建文档需要改到单独的文档目录,并根据需要使用 make HTML
make PDF
、make ps
或make info
。有意思的主要文件是:
bfd/doc/bfd.texinfo
是BFD手册binutils/doc/binutils.texi
是 binutils 主要的用户指南ld/ld.texinfo
是链接器用户指南gas/doc/as.texinfo
是汇编程序用户指南
自动构建的例外是make install
。 这将为binutils/doc/
目录中的任一文档构建信息文件并将它们安装在install/
目录的info/
子目录中。