From 4363d9007a7bb75f9b05f2c03e638c4b521c4ad4 Mon Sep 17 00:00:00 2001 From: asterich Date: Tue, 19 Sep 2023 23:32:44 +0800 Subject: [PATCH 01/12] add article: 20230919-elf2flt-elf2flt-src-analysis --- .../20230919-elf2flt-elf2flt-src-analysis.md | 354 ++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 articles/20230919-elf2flt-elf2flt-src-analysis.md diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md new file mode 100644 index 0000000..43241f4 --- /dev/null +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -0,0 +1,354 @@ +# 从源码看elf2flt原理 + +## 前言 + +`elf2flt` 是一个专门设计用于将 ELF 二进制格式转换为 FLAT 二进制格式的工具,目的是使得二进制文件能够在没有内存管理单元 (MMU) 的CPU上正常运行。虽然 elf2flt 的代码总量达到了约两千行,但是其中的大部分代码是用于支持不同的 CPU 架构的。在本文中,我们将专门针对 RISC-V 架构进行深入探讨。 + +## 目录结构 + +elf2flt 的目录结构如下所示: + +```bash +. +├── 2-1.md +├── elf2flt +│ ├── compress.c +│ ├── compress.h +│ ├── config.guess +│ ├── config.sub +│ ├── configure +│ ├── configure.ac +│ ├── e1-elf2flt.ld +│ ├── elf +│ │ ├── reloc-macros.h +│ │ └── riscv.h +│ ├── elf2flt.c +│ ├── elf2flt.ld.in +│ ├── filenames.h +│ ├── flat.h +│ ├── flthdr.c +│ ├── install-sh +│ ├── ld-elf2flt.c +│ ├── ld-elf2flt.in +│ ├── LICENSE.TXT +│ ├── Makefile.in +│ ├── README.md +│ ├── README_riscv64.md +│ ├── stubs.c +│ ├── stubs.h +│ ├── tests +│ │ ├── flthdr +│ │ │ ├── basic.good +│ │ │ ├── generate.c +│ │ │ ├── multi.good +│ │ │ └── test.sh +│ │ └── lib.sh +│ └── travis +│ ├── arches.sh +│ ├── lib.sh +│ └── main.sh +``` + +从目录结构我们可以看出,程序的主要入口是 `elf2flt.c`,这是一个大约有两千行 C 代码的文件。 + +## 文件解析 + +解析一个 ELF 文件的过程相对直接。主要的解析工作如下: + +1. 定义了一系列的变量用于存储关于输入文件和其他相关信息的细节。 +2. 初始化这些变量并准备进行文件解析。 +3. 使用 `bfd` 库来读取和解析输入文件的符号表和段信息。 +4. 根据段的属性(如代码段、数据段、bss段等)来确定段的位置和大小。 +5. 根据解析得到的信息,将这些段的内容读取到相应的内存区域中。 + +首先,我们直接看`main`函数,在函数前段的定义中,包含了对输入输出文件,操作流,操作,栈,符号表,各个数据段的大小和起止,数据段地址,重定位地址等做出了定义。 + +```c + int fd; + bfd *rel_bfd, *abs_bfd; + asection *s; + char *ofile=NULL, *pfile=NULL, *abs_file = NULL, *rel_file = NULL; + char *fname = NULL; + int opt; + int i; + int stack; + stream gf; + + asymbol **symbol_table; + long number_of_symbols; + + uint32_t data_len = 0; + uint32_t bss_len = 0; + uint32_t text_len = 0; + uint32_t reloc_len; + + uint32_t data_vma = ~0; + uint32_t bss_vma = ~0; + uint32_t text_vma = ~0; + + uint32_t text_offs; + + void *text; + void *data; + uint32_t *reloc; +``` + +在对栈空间进行分配后,进行常规的参数处理,通过 `bfd` 库对输入文件格式进行错误检查。之后,我们开始正式进行转换。 + +转换的第一步,是解析输入文件,获取符号表和段表信息。 + +```C + symbol_table = get_symbols(abs_bfd, &number_of_symbols); + + /* Group output sections into text, data, and bss, and calc their sizes. */ + for (s = abs_bfd->sections; s != NULL; s = s->next) { + uint32_t *vma, *len; + bfd_size_type sec_size; + bfd_vma sec_vma; + + if ((s->flags & SEC_CODE) || + ro_reloc_data_section_should_be_in_text(s)) { + vma = &text_vma; + len = &text_len; + } else if (s->flags & SEC_DATA) { + vma = &data_vma; + len = &data_len; + } else if (s->flags & SEC_ALLOC) { + vma = &bss_vma; + len = &bss_len; + } else + continue; + + sec_size = elf2flt_bfd_section_size(s); + sec_vma = elf2flt_bfd_section_vma(s); + + if (sec_vma < *vma) { + if (*len > 0) + *len += sec_vma - *vma; + else + *len = sec_size; + *vma = sec_vma; + } else if (sec_vma + sec_size > *vma + *len) + *len = sec_vma + sec_size - *vma; + } +``` + +这里的 `get_symbols` 函数的实现其实也是比较简单的,主要也是通过对 `bfd` 库的使用进行符号表的解析和填充。 + +而在 `sections` 的解析中,通过对节头元数据的判断来确定节的类型,并将其填入预先分配的地址中并获取其长度。 + +接着通过由 `text` 段长度进行的错误判断和由该长度进行的内存分配后,将特定类型的输入节(代码节和只读重定位数据节)读取到 `text` 段里面: + +```C + for (s = abs_bfd->sections; s != NULL; s = s->next) + if ((s->flags & SEC_CODE) || + ro_reloc_data_section_should_be_in_text(s)) + if (!bfd_get_section_contents(abs_bfd, s, + text + (s->vma - text_vma), 0, + elf2flt_bfd_section_size(s))) + { + fatal("read error section %s", s->name); + } +``` + +接着,将其他的数据节(除了上述提到的只读重定位数据节)都读到数据输出段内,将符号表放入bss段内,再进行一些必要的错误判断,初始的解析就完成了。 + +## 重定位处理 + +对于 RISC-V 架构,重定位的处理是关键的一步。简化的步骤如下: + +1. 判断哪些段需要进行重定位。 +2. 为需要重定位的段确定重定位的起始位置。 +3. 使用 `bfd` 库来解析输入文件的重定位条目。 +4. 根据这些条目和当前段的信息,更新段的内容以完成重定位。 + +我们具体看elf2flt。首先,从源码中相应的调用我们可以看见: + +```C + reloc = (uint32_t *) + output_relocs(abs_bfd, symbol_table, number_of_symbols, &reloc_len, + text, text_len, text_vma, data, data_len, data_vma, rel_bfd); +``` + +这里的重定位是调用了 `output_relocs` 这样的一个函数。这个函数是占据了两千行源码中的一千行,是实质意义上程序的核心。但实际上,是由于不同架构的不同,对于重定位的解析也不同,因此这样的一千行代码实际上真正会被我们用到的,也就是RISC-V的只有一小部分。 + +具体而言,我们只以RISC-V为例子来看这整个过程是怎么样的。 + +首先,我们需要检测我们的GOT表在字节上的大小。由于GOT表终止于 -1 ,所以这对我们来说不算困难。(这里的重定位和绝对地址都有这个终止记号) + +```C + if (pic_with_got && !use_resolved) { + uint32_t *lp = (uint32_t *)data; + /* Should call ntohl(*lp) here but is isn't going to matter */ + while (*lp != 0xffffffff) lp++; + got_size = ((unsigned char *)lp) - data; +``` + +接着,扫描每一个节,如果使用位置无关代码(PIC)或者是全局偏移表(GOT),则只针对可写数据节中进行重定位,否则也对 text 和只读数据进行重定位。 + +```C +if ((!pic_with_got || ALWAYS_RELOC_TEXT) && + ((a->flags & SEC_CODE) || + ro_reloc_data_section_should_be_in_text(a))) + sectionp = text + (a->vma - text_vma); +else if (a->flags & SEC_DATA) + sectionp = data + (a->vma - data_vma); +else + continue; +``` + +然后查找与当前处理的程序部分相关的二进制重定位文件中的信息,然后使用这些信息来构建重定位条目。 + +在进行一些检查排除了不需要进行重定位的情况后,正式开始进行重定位,这里针对不同的系统架构进行了不同的重定位解析,以RISC-V为例,如下: + +```C +#elif defined(TARGET_riscv64) + case R_RISCV_32_PCREL: + case R_RISCV_ADD32: + case R_RISCV_ADD64: + case R_RISCV_SUB32: + case R_RISCV_SUB64: + continue; + case R_RISCV_32: + case R_RISCV_64: + goto good_32bit_resolved_reloc; + default: + goto bad_resolved_reloc; +``` + +我们发现,只有R_RISCV_32、R_RISCV_64这两种重定位方式会进入到 `good_32bit_resolved_reloc` 这个标签中,这里的代码如下: + +```C +good_32bit_resolved_reloc: + if (bfd_big_endian (abs_bfd)) + sym_addr = + (r_mem[0] << 24) + + (r_mem[1] << 16) + + (r_mem[2] << 8) + + r_mem[3]; + else + sym_addr = + r_mem[0] + + (r_mem[1] << 8) + + (r_mem[2] << 16) + + (r_mem[3] << 24); + relocation_needed = 1; + update_text = 0; + break; +``` + +这里,我们可以看到,对于32位的重定位,我们将通过是否是大小端的判断,将其转换为一个32位的地址。然后,我们将其放入符号表中: + +```C +if (relocation_needed) { + if (verbose) + printf(" RELOC[%d]: offset=0x%"BFD_VMA_FMT"x symbol=%s%s " + "section=%s size=%d " + "fixup=0x%x (reloc=0x%"BFD_VMA_FMT"x)\n", + flat_reloc_count, + q->address, sym_name, addstr, + section_name, sym_reloc_size, + sym_addr, section_vma + q->address); + +#ifndef TARGET_bfin + flat_relocs = realloc(flat_relocs, + (flat_reloc_count + 1) * sizeof(uint32_t)); +#ifndef TARGET_e1 + flat_relocs[flat_reloc_count] = pflags | + (section_vma + q->address); + +#else + // ... +#endif + flat_reloc_count++; +#endif //TARGET_bfin + relocation_needed = 0; + pflags = 0; + } +``` + +这里可以看到,重定位地址由段地址加上相对偏移,再与`pflags`进行按位或操作完成。RISC-V没有用到pflags。 + +到此处,我们实际上已经知道各段大小。我们的GOT表放在.data段的最上面,因此由此可以确定重定位入口,到此重定位完成。 + +## header 构建 + +为了能够成功地加载和执行bFLT格式的二进制文件,我们需要构建一个bFLT header,这个 header 包含了关于二进制文件的所有必要信息。 + +```C + memcpy(hdr.magic,"bFLT",4); + hdr.rev = htonl(FLAT_VERSION); + hdr.entry = htonl(sizeof(hdr) + bfd_get_start_address(abs_bfd)); + hdr.data_start = htonl(sizeof(hdr) + text_offs + text_len); + hdr.data_end = htonl(sizeof(hdr) + text_offs + text_len +data_len); + hdr.bss_end = htonl(sizeof(hdr) + text_offs + text_len +data_len+bss_len); + hdr.stack_size = htonl(stack); /* FIXME */ + hdr.reloc_start = htonl(sizeof(hdr) + text_offs + text_len +data_len); + hdr.reloc_count = htonl(reloc_len); + hdr.flags = htonl(0 + | (load_to_ram || text_has_relocs ? FLAT_FLAG_RAM : 0) + | (ktrace ? FLAT_FLAG_KTRACE : 0) + | (pic_with_got ? FLAT_FLAG_GOTPIC : 0) + | (docompress ? (docompress == 2 ? FLAT_FLAG_GZDATA : FLAT_FLAG_GZIP) : 0) + ); + hdr.build_date = htonl((uint32_t)get_build_date()); + memset(hdr.filler, 0x00, sizeof(hdr.filler)); + + for (i=0; i Date: Tue, 19 Sep 2023 23:33:10 +0800 Subject: [PATCH 02/12] elf2flt-elf2flt-src-analysis.md: commit correct result of tinycorrect Signed-off-by: asterich --- .../20230919-elf2flt-elf2flt-src-analysis.md | 716 +++++++++--------- 1 file changed, 362 insertions(+), 354 deletions(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index 43241f4..4882700 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -1,354 +1,362 @@ -# 从源码看elf2flt原理 - -## 前言 - -`elf2flt` 是一个专门设计用于将 ELF 二进制格式转换为 FLAT 二进制格式的工具,目的是使得二进制文件能够在没有内存管理单元 (MMU) 的CPU上正常运行。虽然 elf2flt 的代码总量达到了约两千行,但是其中的大部分代码是用于支持不同的 CPU 架构的。在本文中,我们将专门针对 RISC-V 架构进行深入探讨。 - -## 目录结构 - -elf2flt 的目录结构如下所示: - -```bash -. -├── 2-1.md -├── elf2flt -│ ├── compress.c -│ ├── compress.h -│ ├── config.guess -│ ├── config.sub -│ ├── configure -│ ├── configure.ac -│ ├── e1-elf2flt.ld -│ ├── elf -│ │ ├── reloc-macros.h -│ │ └── riscv.h -│ ├── elf2flt.c -│ ├── elf2flt.ld.in -│ ├── filenames.h -│ ├── flat.h -│ ├── flthdr.c -│ ├── install-sh -│ ├── ld-elf2flt.c -│ ├── ld-elf2flt.in -│ ├── LICENSE.TXT -│ ├── Makefile.in -│ ├── README.md -│ ├── README_riscv64.md -│ ├── stubs.c -│ ├── stubs.h -│ ├── tests -│ │ ├── flthdr -│ │ │ ├── basic.good -│ │ │ ├── generate.c -│ │ │ ├── multi.good -│ │ │ └── test.sh -│ │ └── lib.sh -│ └── travis -│ ├── arches.sh -│ ├── lib.sh -│ └── main.sh -``` - -从目录结构我们可以看出,程序的主要入口是 `elf2flt.c`,这是一个大约有两千行 C 代码的文件。 - -## 文件解析 - -解析一个 ELF 文件的过程相对直接。主要的解析工作如下: - -1. 定义了一系列的变量用于存储关于输入文件和其他相关信息的细节。 -2. 初始化这些变量并准备进行文件解析。 -3. 使用 `bfd` 库来读取和解析输入文件的符号表和段信息。 -4. 根据段的属性(如代码段、数据段、bss段等)来确定段的位置和大小。 -5. 根据解析得到的信息,将这些段的内容读取到相应的内存区域中。 - -首先,我们直接看`main`函数,在函数前段的定义中,包含了对输入输出文件,操作流,操作,栈,符号表,各个数据段的大小和起止,数据段地址,重定位地址等做出了定义。 - -```c - int fd; - bfd *rel_bfd, *abs_bfd; - asection *s; - char *ofile=NULL, *pfile=NULL, *abs_file = NULL, *rel_file = NULL; - char *fname = NULL; - int opt; - int i; - int stack; - stream gf; - - asymbol **symbol_table; - long number_of_symbols; - - uint32_t data_len = 0; - uint32_t bss_len = 0; - uint32_t text_len = 0; - uint32_t reloc_len; - - uint32_t data_vma = ~0; - uint32_t bss_vma = ~0; - uint32_t text_vma = ~0; - - uint32_t text_offs; - - void *text; - void *data; - uint32_t *reloc; -``` - -在对栈空间进行分配后,进行常规的参数处理,通过 `bfd` 库对输入文件格式进行错误检查。之后,我们开始正式进行转换。 - -转换的第一步,是解析输入文件,获取符号表和段表信息。 - -```C - symbol_table = get_symbols(abs_bfd, &number_of_symbols); - - /* Group output sections into text, data, and bss, and calc their sizes. */ - for (s = abs_bfd->sections; s != NULL; s = s->next) { - uint32_t *vma, *len; - bfd_size_type sec_size; - bfd_vma sec_vma; - - if ((s->flags & SEC_CODE) || - ro_reloc_data_section_should_be_in_text(s)) { - vma = &text_vma; - len = &text_len; - } else if (s->flags & SEC_DATA) { - vma = &data_vma; - len = &data_len; - } else if (s->flags & SEC_ALLOC) { - vma = &bss_vma; - len = &bss_len; - } else - continue; - - sec_size = elf2flt_bfd_section_size(s); - sec_vma = elf2flt_bfd_section_vma(s); - - if (sec_vma < *vma) { - if (*len > 0) - *len += sec_vma - *vma; - else - *len = sec_size; - *vma = sec_vma; - } else if (sec_vma + sec_size > *vma + *len) - *len = sec_vma + sec_size - *vma; - } -``` - -这里的 `get_symbols` 函数的实现其实也是比较简单的,主要也是通过对 `bfd` 库的使用进行符号表的解析和填充。 - -而在 `sections` 的解析中,通过对节头元数据的判断来确定节的类型,并将其填入预先分配的地址中并获取其长度。 - -接着通过由 `text` 段长度进行的错误判断和由该长度进行的内存分配后,将特定类型的输入节(代码节和只读重定位数据节)读取到 `text` 段里面: - -```C - for (s = abs_bfd->sections; s != NULL; s = s->next) - if ((s->flags & SEC_CODE) || - ro_reloc_data_section_should_be_in_text(s)) - if (!bfd_get_section_contents(abs_bfd, s, - text + (s->vma - text_vma), 0, - elf2flt_bfd_section_size(s))) - { - fatal("read error section %s", s->name); - } -``` - -接着,将其他的数据节(除了上述提到的只读重定位数据节)都读到数据输出段内,将符号表放入bss段内,再进行一些必要的错误判断,初始的解析就完成了。 - -## 重定位处理 - -对于 RISC-V 架构,重定位的处理是关键的一步。简化的步骤如下: - -1. 判断哪些段需要进行重定位。 -2. 为需要重定位的段确定重定位的起始位置。 -3. 使用 `bfd` 库来解析输入文件的重定位条目。 -4. 根据这些条目和当前段的信息,更新段的内容以完成重定位。 - -我们具体看elf2flt。首先,从源码中相应的调用我们可以看见: - -```C - reloc = (uint32_t *) - output_relocs(abs_bfd, symbol_table, number_of_symbols, &reloc_len, - text, text_len, text_vma, data, data_len, data_vma, rel_bfd); -``` - -这里的重定位是调用了 `output_relocs` 这样的一个函数。这个函数是占据了两千行源码中的一千行,是实质意义上程序的核心。但实际上,是由于不同架构的不同,对于重定位的解析也不同,因此这样的一千行代码实际上真正会被我们用到的,也就是RISC-V的只有一小部分。 - -具体而言,我们只以RISC-V为例子来看这整个过程是怎么样的。 - -首先,我们需要检测我们的GOT表在字节上的大小。由于GOT表终止于 -1 ,所以这对我们来说不算困难。(这里的重定位和绝对地址都有这个终止记号) - -```C - if (pic_with_got && !use_resolved) { - uint32_t *lp = (uint32_t *)data; - /* Should call ntohl(*lp) here but is isn't going to matter */ - while (*lp != 0xffffffff) lp++; - got_size = ((unsigned char *)lp) - data; -``` - -接着,扫描每一个节,如果使用位置无关代码(PIC)或者是全局偏移表(GOT),则只针对可写数据节中进行重定位,否则也对 text 和只读数据进行重定位。 - -```C -if ((!pic_with_got || ALWAYS_RELOC_TEXT) && - ((a->flags & SEC_CODE) || - ro_reloc_data_section_should_be_in_text(a))) - sectionp = text + (a->vma - text_vma); -else if (a->flags & SEC_DATA) - sectionp = data + (a->vma - data_vma); -else - continue; -``` - -然后查找与当前处理的程序部分相关的二进制重定位文件中的信息,然后使用这些信息来构建重定位条目。 - -在进行一些检查排除了不需要进行重定位的情况后,正式开始进行重定位,这里针对不同的系统架构进行了不同的重定位解析,以RISC-V为例,如下: - -```C -#elif defined(TARGET_riscv64) - case R_RISCV_32_PCREL: - case R_RISCV_ADD32: - case R_RISCV_ADD64: - case R_RISCV_SUB32: - case R_RISCV_SUB64: - continue; - case R_RISCV_32: - case R_RISCV_64: - goto good_32bit_resolved_reloc; - default: - goto bad_resolved_reloc; -``` - -我们发现,只有R_RISCV_32、R_RISCV_64这两种重定位方式会进入到 `good_32bit_resolved_reloc` 这个标签中,这里的代码如下: - -```C -good_32bit_resolved_reloc: - if (bfd_big_endian (abs_bfd)) - sym_addr = - (r_mem[0] << 24) - + (r_mem[1] << 16) - + (r_mem[2] << 8) - + r_mem[3]; - else - sym_addr = - r_mem[0] - + (r_mem[1] << 8) - + (r_mem[2] << 16) - + (r_mem[3] << 24); - relocation_needed = 1; - update_text = 0; - break; -``` - -这里,我们可以看到,对于32位的重定位,我们将通过是否是大小端的判断,将其转换为一个32位的地址。然后,我们将其放入符号表中: - -```C -if (relocation_needed) { - if (verbose) - printf(" RELOC[%d]: offset=0x%"BFD_VMA_FMT"x symbol=%s%s " - "section=%s size=%d " - "fixup=0x%x (reloc=0x%"BFD_VMA_FMT"x)\n", - flat_reloc_count, - q->address, sym_name, addstr, - section_name, sym_reloc_size, - sym_addr, section_vma + q->address); - -#ifndef TARGET_bfin - flat_relocs = realloc(flat_relocs, - (flat_reloc_count + 1) * sizeof(uint32_t)); -#ifndef TARGET_e1 - flat_relocs[flat_reloc_count] = pflags | - (section_vma + q->address); - -#else - // ... -#endif - flat_reloc_count++; -#endif //TARGET_bfin - relocation_needed = 0; - pflags = 0; - } -``` - -这里可以看到,重定位地址由段地址加上相对偏移,再与`pflags`进行按位或操作完成。RISC-V没有用到pflags。 - -到此处,我们实际上已经知道各段大小。我们的GOT表放在.data段的最上面,因此由此可以确定重定位入口,到此重定位完成。 - -## header 构建 - -为了能够成功地加载和执行bFLT格式的二进制文件,我们需要构建一个bFLT header,这个 header 包含了关于二进制文件的所有必要信息。 - -```C - memcpy(hdr.magic,"bFLT",4); - hdr.rev = htonl(FLAT_VERSION); - hdr.entry = htonl(sizeof(hdr) + bfd_get_start_address(abs_bfd)); - hdr.data_start = htonl(sizeof(hdr) + text_offs + text_len); - hdr.data_end = htonl(sizeof(hdr) + text_offs + text_len +data_len); - hdr.bss_end = htonl(sizeof(hdr) + text_offs + text_len +data_len+bss_len); - hdr.stack_size = htonl(stack); /* FIXME */ - hdr.reloc_start = htonl(sizeof(hdr) + text_offs + text_len +data_len); - hdr.reloc_count = htonl(reloc_len); - hdr.flags = htonl(0 - | (load_to_ram || text_has_relocs ? FLAT_FLAG_RAM : 0) - | (ktrace ? FLAT_FLAG_KTRACE : 0) - | (pic_with_got ? FLAT_FLAG_GOTPIC : 0) - | (docompress ? (docompress == 2 ? FLAT_FLAG_GZDATA : FLAT_FLAG_GZIP) : 0) - ); - hdr.build_date = htonl((uint32_t)get_build_date()); - memset(hdr.filler, 0x00, sizeof(hdr.filler)); - - for (i=0; i Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc2 - [toc comments codeinline refs pangu]
+> Author: Odysseus <320873791@qq.com>
+> Date: 2023/09/19
+> Revisor: walimis <>
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Proposal: [为 ELF2FLT 完善独立编译与安装支持](https://gitee.com/tinylab/riscv-linux/issues/I79PO2)
+> Sponsor: PLCT Lab, ISCAS + +# 从源码看 elf2flt 原理 + +## 前言 + +`elf2flt` 是一个专门设计用于将 ELF 二进制格式转换为 FLAT 二进制格式的工具,目的是使得二进制文件能够在没有内存管理单元 (MMU) 的 CPU 上正常运行。虽然 elf2flt 的代码总量达到了约两千行,但是其中的大部分代码是用于支持不同的 CPU 架构的。在本文中,我们将专门针对 RISC-V 架构进行深入探讨。 + +## 目录结构 + +elf2flt 的目录结构如下所示: + +```bash +. +├── 2-1.md +├── elf2flt +│ ├── compress.c +│ ├── compress.h +│ ├── config.guess +│ ├── config.sub +│ ├── configure +│ ├── configure.ac +│ ├── e1-elf2flt.ld +│ ├── elf +│ │ ├── reloc-macros.h +│ │ └── riscv.h +│ ├── elf2flt.c +│ ├── elf2flt.ld.in +│ ├── filenames.h +│ ├── flat.h +│ ├── flthdr.c +│ ├── install-sh +│ ├── ld-elf2flt.c +│ ├── ld-elf2flt.in +│ ├── LICENSE.TXT +│ ├── Makefile.in +│ ├── README.md +│ ├── README_riscv64.md +│ ├── stubs.c +│ ├── stubs.h +│ ├── tests +│ │ ├── flthdr +│ │ │ ├── basic.good +│ │ │ ├── generate.c +│ │ │ ├── multi.good +│ │ │ └── test.sh +│ │ └── lib.sh +│ └── travis +│ ├── arches.sh +│ ├── lib.sh +│ └── main.sh +``` + +从目录结构我们可以看出,程序的主要入口是 `elf2flt.c`,这是一个大约有两千行 C 代码的文件。 + +## 文件解析 + +解析一个 ELF 文件的过程相对直接。主要的解析工作如下: + +1. 定义了一系列的变量用于存储关于输入文件和其他相关信息的细节。 +2. 初始化这些变量并准备进行文件解析。 +3. 使用 `bfd` 库来读取和解析输入文件的符号表和段信息。 +4. 根据段的属性(如代码段、数据段、bss 段等)来确定段的位置和大小。 +5. 根据解析得到的信息,将这些段的内容读取到相应的内存区域中。 + +首先,我们直接看 `main` 函数,在函数前段的定义中,包含了对输入输出文件,操作流,操作,栈,符号表,各个数据段的大小和起止,数据段地址,重定位地址等做出了定义。 + +```c + int fd; + bfd *rel_bfd, *abs_bfd; + asection *s; + char *ofile=NULL, *pfile=NULL, *abs_file = NULL, *rel_file = NULL; + char *fname = NULL; + int opt; + int i; + int stack; + stream gf; + + asymbol **symbol_table; + long number_of_symbols; + + uint32_t data_len = 0; + uint32_t bss_len = 0; + uint32_t text_len = 0; + uint32_t reloc_len; + + uint32_t data_vma = ~0; + uint32_t bss_vma = ~0; + uint32_t text_vma = ~0; + + uint32_t text_offs; + + void *text; + void *data; + uint32_t *reloc; +``` + +在对栈空间进行分配后,进行常规的参数处理,通过 `bfd` 库对输入文件格式进行错误检查。之后,我们开始正式进行转换。 + +转换的第一步,是解析输入文件,获取符号表和段表信息。 + +```C + symbol_table = get_symbols(abs_bfd, &number_of_symbols); + + /* Group output sections into text, data, and bss, and calc their sizes. */ + for (s = abs_bfd->sections; s != NULL; s = s->next) { + uint32_t *vma, *len; + bfd_size_type sec_size; + bfd_vma sec_vma; + + if ((s->flags & SEC_CODE) || + ro_reloc_data_section_should_be_in_text(s)) { + vma = &text_vma; + len = &text_len; + } else if (s->flags & SEC_DATA) { + vma = &data_vma; + len = &data_len; + } else if (s->flags & SEC_ALLOC) { + vma = &bss_vma; + len = &bss_len; + } else + continue; + + sec_size = elf2flt_bfd_section_size(s); + sec_vma = elf2flt_bfd_section_vma(s); + + if (sec_vma < *vma) { + if (*len > 0) + *len += sec_vma - *vma; + else + *len = sec_size; + *vma = sec_vma; + } else if (sec_vma + sec_size > *vma + *len) + *len = sec_vma + sec_size - *vma; + } +``` + +这里的 `get_symbols` 函数的实现其实也是比较简单的,主要也是通过对 `bfd` 库的使用进行符号表的解析和填充。 + +而在 `sections` 的解析中,通过对节头元数据的判断来确定节的类型,并将其填入预先分配的地址中并获取其长度。 + +接着通过由 `text` 段长度进行的错误判断和由该长度进行的内存分配后,将特定类型的输入节(代码节和只读重定位数据节)读取到 `text` 段里面: + +```C + for (s = abs_bfd->sections; s != NULL; s = s->next) + if ((s->flags & SEC_CODE) || + ro_reloc_data_section_should_be_in_text(s)) + if (!bfd_get_section_contents(abs_bfd, s, + text + (s->vma - text_vma), 0, + elf2flt_bfd_section_size(s))) + { + fatal("read error section %s", s->name); + } +``` + +接着,将其他的数据节(除了上述提到的只读重定位数据节)都读到数据输出段内,将符号表放入 bss 段内,再进行一些必要的错误判断,初始的解析就完成了。 + +## 重定位处理 + +对于 RISC-V 架构,重定位的处理是关键的一步。简化的步骤如下: + +1. 判断哪些段需要进行重定位。 +2. 为需要重定位的段确定重定位的起始位置。 +3. 使用 `bfd` 库来解析输入文件的重定位条目。 +4. 根据这些条目和当前段的信息,更新段的内容以完成重定位。 + +我们具体看 elf2flt。首先,从源码中相应的调用我们可以看见: + +```C + reloc = (uint32_t *) + output_relocs(abs_bfd, symbol_table, number_of_symbols, &reloc_len, + text, text_len, text_vma, data, data_len, data_vma, rel_bfd); +``` + +这里的重定位是调用了 `output_relocs` 这样的一个函数。这个函数是占据了两千行源码中的一千行,是实质意义上程序的核心。但实际上,是由于不同架构的不同,对于重定位的解析也不同,因此这样的一千行代码实际上真正会被我们用到的,也就是 RISC-V 的只有一小部分。 + +具体而言,我们只以 RISC-V 为例子来看这整个过程是怎么样的。 + +首先,我们需要检测我们的 GOT 表在字节上的大小。由于 GOT 表终止于 -1,所以这对我们来说不算困难。(这里的重定位和绝对地址都有这个终止记号) + +```C + if (pic_with_got && !use_resolved) { + uint32_t *lp = (uint32_t *)data; + /* Should call ntohl(*lp) here but is isn't going to matter */ + while (*lp != 0xffffffff) lp++; + got_size = ((unsigned char *)lp) - data; +``` + +接着,扫描每一个节,如果使用位置无关代码(PIC)或者是全局偏移表(GOT),则只针对可写数据节中进行重定位,否则也对 text 和只读数据进行重定位。 + +```C +if ((!pic_with_got || ALWAYS_RELOC_TEXT) && + ((a->flags & SEC_CODE) || + ro_reloc_data_section_should_be_in_text(a))) + sectionp = text + (a->vma - text_vma); +else if (a->flags & SEC_DATA) + sectionp = data + (a->vma - data_vma); +else + continue; +``` + +然后查找与当前处理的程序部分相关的二进制重定位文件中的信息,然后使用这些信息来构建重定位条目。 + +在进行一些检查排除了不需要进行重定位的情况后,正式开始进行重定位,这里针对不同的系统架构进行了不同的重定位解析,以 RISC-V 为例,如下: + +```C +#elif defined(TARGET_riscv64) + case R_RISCV_32_PCREL: + case R_RISCV_ADD32: + case R_RISCV_ADD64: + case R_RISCV_SUB32: + case R_RISCV_SUB64: + continue; + case R_RISCV_32: + case R_RISCV_64: + goto good_32bit_resolved_reloc; + default: + goto bad_resolved_reloc; +``` + +我们发现,只有 R_RISCV_32、R_RISCV_64 这两种重定位方式会进入到 `good_32bit_resolved_reloc` 这个标签中,这里的代码如下: + +```C +good_32bit_resolved_reloc: + if (bfd_big_endian (abs_bfd)) + sym_addr = + (r_mem[0] << 24) + + (r_mem[1] << 16) + + (r_mem[2] << 8) + + r_mem[3]; + else + sym_addr = + r_mem[0] + + (r_mem[1] << 8) + + (r_mem[2] << 16) + + (r_mem[3] << 24); + relocation_needed = 1; + update_text = 0; + break; +``` + +这里,我们可以看到,对于 32 位的重定位,我们将通过是否是大小端的判断,将其转换为一个 32 位的地址。然后,我们将其放入符号表中: + +```C +if (relocation_needed) { + if (verbose) + printf(" RELOC[%d]: offset=0x%"BFD_VMA_FMT"x symbol=%s%s " + "section=%s size=%d " + "fixup=0x%x (reloc=0x%"BFD_VMA_FMT"x)\n", + flat_reloc_count, + q->address, sym_name, addstr, + section_name, sym_reloc_size, + sym_addr, section_vma + q->address); + +#ifndef TARGET_bfin + flat_relocs = realloc(flat_relocs, + (flat_reloc_count + 1) * sizeof(uint32_t)); +#ifndef TARGET_e1 + flat_relocs[flat_reloc_count] = pflags | + (section_vma + q->address); + +#else + // ... +#endif + flat_reloc_count++; +#endif //TARGET_bfin + relocation_needed = 0; + pflags = 0; + } +``` + +这里可以看到,重定位地址由段地址加上相对偏移,再与 `pflags` 进行按位或操作完成。RISC-V 没有用到 pflags。 + +到此处,我们实际上已经知道各段大小。我们的 GOT 表放在.data 段的最上面,因此由此可以确定重定位入口,到此重定位完成。 + +## header 构建 + +为了能够成功地加载和执行 bFLT 格式的二进制文件,我们需要构建一个 bFLT header,这个 header 包含了关于二进制文件的所有必要信息。 + +```C + memcpy(hdr.magic,"bFLT",4); + hdr.rev = htonl(FLAT_VERSION); + hdr.entry = htonl(sizeof(hdr) + bfd_get_start_address(abs_bfd)); + hdr.data_start = htonl(sizeof(hdr) + text_offs + text_len); + hdr.data_end = htonl(sizeof(hdr) + text_offs + text_len +data_len); + hdr.bss_end = htonl(sizeof(hdr) + text_offs + text_len +data_len+bss_len); + hdr.stack_size = htonl(stack); /* FIXME */ + hdr.reloc_start = htonl(sizeof(hdr) + text_offs + text_len +data_len); + hdr.reloc_count = htonl(reloc_len); + hdr.flags = htonl(0 + | (load_to_ram || text_has_relocs ? FLAT_FLAG_RAM : 0) + | (ktrace ? FLAT_FLAG_KTRACE : 0) + | (pic_with_got ? FLAT_FLAG_GOTPIC : 0) + | (docompress ? (docompress == 2 ? FLAT_FLAG_GZDATA : FLAT_FLAG_GZIP) : 0) + ); + hdr.build_date = htonl((uint32_t)get_build_date()); + memset(hdr.filler, 0x00, sizeof(hdr.filler)); + + for (i=0; i Date: Wed, 20 Sep 2023 23:08:30 +0800 Subject: [PATCH 03/12] fix article: 20230919-elf2flt-elf2flt-src-analysis: some small fix --- .../20230919-elf2flt-elf2flt-src-analysis.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index 4882700..d2450b8 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -10,7 +10,7 @@ ## 前言 -`elf2flt` 是一个专门设计用于将 ELF 二进制格式转换为 FLAT 二进制格式的工具,目的是使得二进制文件能够在没有内存管理单元 (MMU) 的 CPU 上正常运行。虽然 elf2flt 的代码总量达到了约两千行,但是其中的大部分代码是用于支持不同的 CPU 架构的。在本文中,我们将专门针对 RISC-V 架构进行深入探讨。 +`elf2flt` 是一个专门设计用于将 ELF 二进制格式转换为 bFLT 二进制格式的工具,目的是使得二进制文件能够在没有内存管理单元 (MMU) 的 CPU 上正常运行。虽然 elf2flt 的代码总量达到了约两千行,但是其中的大部分代码是用于支持不同的 CPU 架构的。在本文中,我们将专门针对 RISC-V 架构进行深入探讨。 ## 目录结构 @@ -18,7 +18,6 @@ elf2flt 的目录结构如下所示: ```bash . -├── 2-1.md ├── elf2flt │ ├── compress.c │ ├── compress.h @@ -57,7 +56,7 @@ elf2flt 的目录结构如下所示: │ └── main.sh ``` -从目录结构我们可以看出,程序的主要入口是 `elf2flt.c`,这是一个大约有两千行 C 代码的文件。 +从目录结构我们可以看出,程序的主要入口是 `elf2flt.c`,这是一个大约有两千行 C 代码的文件。 `tests` 和 `travis` 目录都是作测试用途。 ## 文件解析 @@ -66,7 +65,7 @@ elf2flt 的目录结构如下所示: 1. 定义了一系列的变量用于存储关于输入文件和其他相关信息的细节。 2. 初始化这些变量并准备进行文件解析。 3. 使用 `bfd` 库来读取和解析输入文件的符号表和段信息。 -4. 根据段的属性(如代码段、数据段、bss 段等)来确定段的位置和大小。 +4. 根据段的属性(如代码段、数据段、`.bss` 段等)来确定段的位置和大小。 5. 根据解析得到的信息,将这些段的内容读取到相应的内存区域中。 首先,我们直接看 `main` 函数,在函数前段的定义中,包含了对输入输出文件,操作流,操作,栈,符号表,各个数据段的大小和起止,数据段地址,重定位地址等做出了定义。 @@ -145,7 +144,7 @@ elf2flt 的目录结构如下所示: 而在 `sections` 的解析中,通过对节头元数据的判断来确定节的类型,并将其填入预先分配的地址中并获取其长度。 -接着通过由 `text` 段长度进行的错误判断和由该长度进行的内存分配后,将特定类型的输入节(代码节和只读重定位数据节)读取到 `text` 段里面: +接着通过由 `.text` 段长度进行的错误判断和由该长度进行的内存分配后,将特定类型的输入节(代码节和只读重定位数据节)读取到 `.text` 段里面: ```C for (s = abs_bfd->sections; s != NULL; s = s->next) @@ -159,7 +158,7 @@ elf2flt 的目录结构如下所示: } ``` -接着,将其他的数据节(除了上述提到的只读重定位数据节)都读到数据输出段内,将符号表放入 bss 段内,再进行一些必要的错误判断,初始的解析就完成了。 +接着,将其他的数据节(除了上述提到的只读重定位数据节)都读到数据输出段内,将符号表放入 `.bss` 段内,再进行一些必要的错误判断,初始的解析就完成了。 ## 重定位处理 @@ -192,7 +191,7 @@ elf2flt 的目录结构如下所示: got_size = ((unsigned char *)lp) - data; ``` -接着,扫描每一个节,如果使用位置无关代码(PIC)或者是全局偏移表(GOT),则只针对可写数据节中进行重定位,否则也对 text 和只读数据进行重定位。 +接着,扫描每一个节,如果使用位置无关代码(PIC)或者是全局偏移表(GOT),则只针对可写数据节中进行重定位,否则也对 `.text` 段和只读数据进行重定位。 ```C if ((!pic_with_got || ALWAYS_RELOC_TEXT) && @@ -275,9 +274,9 @@ if (relocation_needed) { } ``` -这里可以看到,重定位地址由段地址加上相对偏移,再与 `pflags` 进行按位或操作完成。RISC-V 没有用到 pflags。 +这里可以看到,重定位地址由段地址加上相对偏移,再与 `pflags` 进行按位或操作完成。RISC-V 没有用到 `pflags` 。 -到此处,我们实际上已经知道各段大小。我们的 GOT 表放在.data 段的最上面,因此由此可以确定重定位入口,到此重定位完成。 +到此处,我们实际上已经知道各段大小。我们的 GOT 表放在 `.data` 段的最上面,因此由此可以确定重定位入口,到此重定位完成。 ## header 构建 @@ -353,7 +352,7 @@ if (relocation_needed) { ## 总结 -elf2flt 的代码虽然相对复杂,但其主要目标是为了处理 ELF 格式的文件,并将其转换为适用于没有 MMU 的 CPU 的 FLAT 格式。通过深入探讨其源代码,我们可以更好地理解这一转换过程的每个步骤,从而更好地使用和修改这一工具。 +elf2flt 的代码虽然相对复杂,但其主要目标是为了处理 ELF 格式的文件,并将其转换为适用于没有 MMU 的 CPU 的 bFLT 格式。通过深入探讨其源代码,我们可以更好地理解这一转换过程的每个步骤,从而更好地使用和修改这一工具。 ## 参考资料 -- Gitee From 92f8e6bb24898ca9a48a8649b49aed1ba9d02d06 Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:21:29 +0800 Subject: [PATCH 04/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.1 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index d2450b8..e1b139f 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -356,6 +356,6 @@ elf2flt 的代码虽然相对复杂,但其主要目标是为了处理 ELF 格 ## 参考资料 --. [https://gitee.com/tinylab/elf2flt][001] +- [https://gitee.com/tinylab/elf2flt][001] [001]: https://gitee.com/tinylab/elf2flt -- Gitee From cf38ae5b81afa827b6caac2654ab656bb67ecd4c Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:22:25 +0800 Subject: [PATCH 05/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.2 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index e1b139f..55b4196 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -60,7 +60,7 @@ elf2flt 的目录结构如下所示: ## 文件解析 -解析一个 ELF 文件的过程相对直接。主要的解析工作如下: +程序首先对 ELF 文件进行解析,其主要的解析过程如下: 1. 定义了一系列的变量用于存储关于输入文件和其他相关信息的细节。 2. 初始化这些变量并准备进行文件解析。 -- Gitee From cffd90dca69c478f217acec59c8e631cba1bcd13 Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:26:21 +0800 Subject: [PATCH 06/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.4 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index 55b4196..7ea594c 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -62,7 +62,7 @@ elf2flt 的目录结构如下所示: 程序首先对 ELF 文件进行解析,其主要的解析过程如下: -1. 定义了一系列的变量用于存储关于输入文件和其他相关信息的细节。 +1. 定义了一系列的变量用于存储输入文件和其他相关的信息。 2. 初始化这些变量并准备进行文件解析。 3. 使用 `bfd` 库来读取和解析输入文件的符号表和段信息。 4. 根据段的属性(如代码段、数据段、`.bss` 段等)来确定段的位置和大小。 -- Gitee From 9641f1b7a2443444cff64e9da7fc4d5ac11fa3ab Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:27:32 +0800 Subject: [PATCH 07/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.5 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index 7ea594c..cf7f33b 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -68,7 +68,7 @@ elf2flt 的目录结构如下所示: 4. 根据段的属性(如代码段、数据段、`.bss` 段等)来确定段的位置和大小。 5. 根据解析得到的信息,将这些段的内容读取到相应的内存区域中。 -首先,我们直接看 `main` 函数,在函数前段的定义中,包含了对输入输出文件,操作流,操作,栈,符号表,各个数据段的大小和起止,数据段地址,重定位地址等做出了定义。 +首先,我们来看 `main` 函数。在函数前段的定义中,包含了输入输出文件、操作流、操作、栈、符号表、各个数据段的大小和起止、数据段地址、重定位地址等变量。 ```c int fd; -- Gitee From 82022c6b08cbb1b45d59e5dda10a4ef4e06e8cff Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:28:58 +0800 Subject: [PATCH 08/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.6 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index cf7f33b..0ba4776 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -140,7 +140,7 @@ elf2flt 的目录结构如下所示: } ``` -这里的 `get_symbols` 函数的实现其实也是比较简单的,主要也是通过对 `bfd` 库的使用进行符号表的解析和填充。 +这里的 `get_symbols` 函数主要使用 `bfd` 库进行符号表的解析和填充。 而在 `sections` 的解析中,通过对节头元数据的判断来确定节的类型,并将其填入预先分配的地址中并获取其长度。 -- Gitee From 90ad3ec0c2aa3a0f58c897b41d96fb6fb5255a2f Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:29:56 +0800 Subject: [PATCH 09/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.7 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index 0ba4776..d29a38d 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -144,7 +144,7 @@ elf2flt 的目录结构如下所示: 而在 `sections` 的解析中,通过对节头元数据的判断来确定节的类型,并将其填入预先分配的地址中并获取其长度。 -接着通过由 `.text` 段长度进行的错误判断和由该长度进行的内存分配后,将特定类型的输入节(代码节和只读重定位数据节)读取到 `.text` 段里面: +接着处理 `.text` 段,如果段长度为 0 则出错;如果不为 0,分配对应长度的内存分配,然后将特定类型的输入节(代码节和只读重定位数据节)读取到 `.text` 段里面: ```C for (s = abs_bfd->sections; s != NULL; s = s->next) -- Gitee From c0d173b7325b33df893198abeee1420c84f2855c Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:31:26 +0800 Subject: [PATCH 10/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.8 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index d29a38d..2862ac7 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -177,7 +177,7 @@ elf2flt 的目录结构如下所示: text, text_len, text_vma, data, data_len, data_vma, rel_bfd); ``` -这里的重定位是调用了 `output_relocs` 这样的一个函数。这个函数是占据了两千行源码中的一千行,是实质意义上程序的核心。但实际上,是由于不同架构的不同,对于重定位的解析也不同,因此这样的一千行代码实际上真正会被我们用到的,也就是 RISC-V 的只有一小部分。 +重定位是通过调用 `output_relocs` 函数来完成的,这个函数占据了两千行中的一千行,是程序的核心代码。但实际上,大部分代码用来处理不同架构, RISC-V 相关的代码只占一小部分。 具体而言,我们只以 RISC-V 为例子来看这整个过程是怎么样的。 -- Gitee From 842d590c7488b17e0fdb85a00fb0767c4e62c90d Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:33:03 +0800 Subject: [PATCH 11/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.9 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index 2862ac7..dfb263c 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -179,7 +179,7 @@ elf2flt 的目录结构如下所示: 重定位是通过调用 `output_relocs` 函数来完成的,这个函数占据了两千行中的一千行,是程序的核心代码。但实际上,大部分代码用来处理不同架构, RISC-V 相关的代码只占一小部分。 -具体而言,我们只以 RISC-V 为例子来看这整个过程是怎么样的。 +这里我们只以 RISC-V 为例,来看整个过程是怎么实现的。 首先,我们需要检测我们的 GOT 表在字节上的大小。由于 GOT 表终止于 -1,所以这对我们来说不算困难。(这里的重定位和绝对地址都有这个终止记号) -- Gitee From 170e843eb9e8b11ce1d34389f0c37d550bf4e0a9 Mon Sep 17 00:00:00 2001 From: asterich Date: Sat, 30 Sep 2023 05:33:41 +0800 Subject: [PATCH 12/12] fix article 20230919-elf2flt-flt-analysis-0: pr comment No.10 Signed-off-by: asterich --- articles/20230919-elf2flt-elf2flt-src-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230919-elf2flt-elf2flt-src-analysis.md b/articles/20230919-elf2flt-elf2flt-src-analysis.md index dfb263c..14f4870 100644 --- a/articles/20230919-elf2flt-elf2flt-src-analysis.md +++ b/articles/20230919-elf2flt-elf2flt-src-analysis.md @@ -181,7 +181,7 @@ elf2flt 的目录结构如下所示: 这里我们只以 RISC-V 为例,来看整个过程是怎么实现的。 -首先,我们需要检测我们的 GOT 表在字节上的大小。由于 GOT 表终止于 -1,所以这对我们来说不算困难。(这里的重定位和绝对地址都有这个终止记号) +这里首先获取 GOT 表的大小。由于 GOT 表终止于 -1,所以这一步不算困难。(这里的重定位和绝对地址都有这个终止记号) ```C if (pic_with_got && !use_resolved) { -- Gitee