diff --git a/.gitignore b/.gitignore index 9c9badf3d683d47b0310293c949b8635592e7e9a..e517e41d8129ad5ba99d374dc6c11ab7013c1eb8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ target .idea/ vendor Cargo.lock +*.rto # vscode .vscode/* diff --git a/src/elfmerge/elf_link_common.c b/src/elfmerge/elf_link_common.c index 190ad6a3b6d174f58ac7cb18d5830f84a56c62bf..c6b9373a4f118d4664fa62276dde9ca0609781b3 100644 --- a/src/elfmerge/elf_link_common.c +++ b/src/elfmerge/elf_link_common.c @@ -123,6 +123,11 @@ static char *needed_sections[] = { ".symtab", ".strtab", ".shstrtab", + ".debug_info", + ".debug_line", + ".debug_str", + ".debug_line_str", + ".debug_abbrev", }; #define NEEDED_SECTIONS_LEN (sizeof(needed_sections) / sizeof(needed_sections[0])) @@ -590,9 +595,11 @@ static unsigned long _get_new_elf_addr(elf_link_t *elf_link, elf_file_t *src_ef, for (int i = 0; i < len; i++) { sec_rel = &sec_rels[i]; + // sec_rel is a section in the same file as src_ef if (sec_rel->src_ef != src_ef) { continue; } + // old addr is between source section if (addr < sec_rel->src_sec->sh_addr || addr > sec_rel->src_sec->sh_addr + sec_rel->src_sec->sh_size) { continue; } diff --git a/src/elfmerge/elf_link_elf.c b/src/elfmerge/elf_link_elf.c index 0e9a1d7939d04a445a62eba7fbc72b21ad83f508..a39fa1253bf4028fc58b9f44e34e07da49b87e21 100644 --- a/src/elfmerge/elf_link_elf.c +++ b/src/elfmerge/elf_link_elf.c @@ -367,7 +367,8 @@ static void write_sysboost_section(elf_link_t *elf_link) }*/ // main ELF and libc.so have .interp, need to ignore it -// .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_d .gnu.version_r .rela.dyn .rela.plt +// .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr +// .gnu.version .gnu.version_d .gnu.version_r .rela.dyn .rela.plt static elf_section_t hdr_segment_section_arr[] = { {".interp", merge_template_ef_section}, {".note.gnu.property", merge_template_ef_section}, @@ -584,6 +585,11 @@ static void modify_tls_segment(elf_link_t *elf_link) modify_segment(elf_link, out_ef->tls_Phdr, ".tdata", ".tbss"); } +static void write_debug_info(elf_link_t *elf_link) +{ + merge_debug_sections(elf_link); +} + // .tdata .init_array .fini_array .dynamic .got .got.plt .data .bss static void write_data(elf_link_t *elf_link) { @@ -1306,6 +1312,80 @@ static void modify_elf_header(elf_link_t *elf_link) elf_set_hugepage(elf_link); } +/* debug modify start */ + +#include +#include + +struct dwarf_unit_header +{ + uint32_t length; + uint16_t version; + uint8_t unit_type; + uint8_t pointer_size; + uint32_t abbrev_offset; +}; + +void check_unit_header(struct dwarf_unit_header *unit_header) +{ + /* + * 32-bit DWARF format's length must < 0xfffffff0, + * we only support 32-bit now. + */ + if (unit_header->length >= 0xfffffff0) + si_panic("64-bit DWARF format is not supported\n"); + + if (unit_header->version != 5) + si_panic("only support DWARF version 5\n"); + + if (unit_header->pointer_size != 8) + si_panic("only support 64-bit target machine\n"); +} + +void check_cu_header(struct dwarf_unit_header *cu_header) +{ + check_unit_header(cu_header); + + if (cu_header->unit_type != DW_UT_compile) + si_panic("current unit_header is not cu_header\n"); +} + +/* modify abbrev offset stored in .debug_info */ +void modify_debug_info_abbrev_offset(elf_link_t *elf_link) +{ + elf_file_t *ef; + uint32_t da_offset = 0; + void *di_base = elf_find_section_ptr_by_name(&elf_link->out_ef, ".debug_info"); + uint32_t cu_offset = 0; + + foreach_infile(elf_link, ef) { + Elf64_Shdr *di_sec = elf_find_section_by_name(ef, ".debug_info"); + Elf64_Shdr *da_sec = elf_find_section_by_name(ef, ".debug_abbrev"); + uint32_t in_ef_cu_offset = 0; + + while (in_ef_cu_offset < di_sec->sh_size) { + struct dwarf_unit_header *cu_header = di_base + cu_offset; + check_cu_header(cu_header); + cu_header->abbrev_offset += da_offset; + /* + * each cu have additional 4 bytes, + * because length doesn't count itself's space. + */ + cu_offset += cu_header->length + 4; + in_ef_cu_offset += cu_header->length + 4; + } + + da_offset += da_sec->sh_size; + } +} + +static void modify_debug(elf_link_t *elf_link) +{ + modify_debug_info_abbrev_offset(elf_link); +} + +/* debug modify end */ + // .init_array first func is frame_dummy, frame_dummy call register_tm_clones // .fini_array first func is __do_global_dtors_aux, __do_global_dtors_aux call deregister_tm_clones char *disabled_funcs[] = { @@ -1363,44 +1443,65 @@ static void do_special_adapts(elf_link_t *elf_link) // correct_stop_libc_atexit(elf_link); } -// merge per section -// .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .rela.dyn .rela.plt -// merge segment, keep offset in page -// .init .plt .text __libc_freeres_fn .fini -// merge segment, keep offset in page, split text and data -// .rodata .stapsdt.base .eh_frame_hdr .eh_frame .gcc_except_table -// merge per section, RW -> RO -// .tdata .init_array .fini_array .data.rel.ro .dynamic -// merge segment, keep offset in page, RW -> RO -// .got -// merge segment, keep offset in page -// .data .tm_clone_table __libc_subfreeres __libc_IO_vtables __libc_atexit .bss __libc_freeres_ptrs +/* + * There are 2 kinds of merge in elfmerge: + * 1. merge per section: + * merge same sections in each binary into one section. + * [.tdata a] [.init_array a] => new .tdata new .init_array + * [.tdata b] [.init_array b] => [.data a, .data b] [.init_array a, .init_array b] + * + * 2. merge segment: + * merge multiple sections in same segments in each binary all into one section. + * [.plt a, .plt b ] => new .text + * [.text a, .text b] => [.plt a, .text a, .plt b, .text b] + */ static void elf_link_write_sections(elf_link_t *elf_link) { - // .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_d .gnu.version_r .rela.dyn .rela.plt + /* + * merge per section for below sections: + * .interp .note.gnu.build-id .note.ABI-tag .gnu.hash + * .dynsym .dynstr .rela.dyn .rela.plt + */ write_first_LOAD_segment(elf_link); - // .init .plt .text __libc_freeres_fn .fini + /* + * merge segment for below sections: + * .init .plt .text __libc_freeres_fn .fini + * all into .text in new elf. + */ write_text(elf_link); - // .rodata .stapsdt.base .eh_frame_hdr .eh_frame .gcc_except_table + /* + * merge segment for below sections: + * .rodata .stapsdt.base .eh_frame_hdr .eh_frame .gcc_except_table + * all into .rodata in new elf. + */ write_rodata(elf_link); - // .tdata .tbss .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss + /* + * merge per section for below sections: + * .tdata .tbss .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss + */ write_data(elf_link); - // .dynamic + /* .dynamic (merge per section) */ modify_dynamic(elf_link); - // .symtab + /* .symtab (merge per section) */ write_symtab(elf_link); - // .strtab + /* .strtab (merge per section) */ write_strtab(elf_link); - // .shstrtab + /* .shstrtab (merge per section) */ write_shstrtab(elf_link); + /* + * merge per section for below sections: + * .debug_info .debug_line .debug_str .debug_line_str .debug_abbrev + */ + write_debug_info(elf_link); + /* * .comment is useless, it's used to hold comments about the generated ELF * (details such as compiler version and execution platform). @@ -1457,6 +1558,8 @@ int elf_link_write(elf_link_t *elf_link) // .rela.init .rela.text .rela.rodata .rela.tdata .rela.init_array .rela.data modify_local_call(elf_link); + modify_debug(elf_link); + // modify ELF header and write sections modify_elf_header(elf_link); diff --git a/src/elfmerge/elf_link_elf.h b/src/elfmerge/elf_link_elf.h index c025fa03534a3186137dd977b07c8b741ed46505..658de4d3dfa714a8ce3e8459359e945e8a7b8209 100644 --- a/src/elfmerge/elf_link_elf.h +++ b/src/elfmerge/elf_link_elf.h @@ -24,4 +24,7 @@ int elf_link_set_mode(elf_link_t *elf_link, unsigned int mode); elf_file_t *elf_link_add_infile(elf_link_t *elf_link, char *path); int elf_link_write(elf_link_t *elf_link); +#define foreach_infile(elf_link, ef) \ + for (ef = elf_link->in_efs; ef < elf_link->in_efs + elf_link->in_ef_nr; ef++) + #endif /* _LINK_ELF_H */ diff --git a/src/elfmerge/elf_read_elf.c b/src/elfmerge/elf_read_elf.c index 612400811e860f6c28e52b37d401c451177f449f..d27f1bf9373d5d0cc8766a5b47e69b60ee1367ba 100644 --- a/src/elfmerge/elf_read_elf.c +++ b/src/elfmerge/elf_read_elf.c @@ -36,6 +36,7 @@ #endif #define DEBUG_SEC_PRE_NAME ".debug_" +#define RELA_DEBUG_SEC_PRE_NAME ".rela.debug_" #define BUILD_ID_LEN 40 #define ELF_VERSION_NR_LOCAL 0 @@ -364,6 +365,15 @@ Elf64_Shdr *elf_find_section_by_name(elf_file_t *ef, const char *sec_name) return NULL; } +void *elf_find_section_ptr_by_name(elf_file_t *ef, const char *sec_name) +{ + Elf64_Shdr *sec = elf_find_section_by_name(ef, sec_name); + if (!sec) + return NULL; + + return ef->data + sec->sh_offset; +} + bool elf_is_relro_section(const elf_file_t *ef, const Elf64_Shdr *sechdr) { unsigned int start = ef->relro_Phdr->p_paddr; @@ -477,6 +487,48 @@ bool elf_is_debug_section(elf_file_t *ef, Elf64_Shdr *sechdr) return false; } +bool elf_is_rela_debug_section(elf_file_t *ef, Elf64_Shdr *sechdr) +{ + char *name = NULL; + + name = ef->shstrtab_data + sechdr->sh_name; + if (strncmp(name, RELA_DEBUG_SEC_PRE_NAME, sizeof(RELA_DEBUG_SEC_PRE_NAME) - 1) == 0) { + return true; + } + + return false; +} + +bool debug_info_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec) +{ + char *name = elf_get_section_name(ef, sec); + return strcmp(name, ".debug_info") == 0; +} + +bool debug_line_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec) +{ + char *name = elf_get_section_name(ef, sec); + return strcmp(name, ".debug_line") == 0; +} + +bool debug_str_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec) +{ + char *name = elf_get_section_name(ef, sec); + return strcmp(name, ".debug_str") == 0; +} + +bool debug_line_str_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec) +{ + char *name = elf_get_section_name(ef, sec); + return strcmp(name, ".debug_line_str") == 0; +} + +bool debug_abbrev_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec) +{ + char *name = elf_get_section_name(ef, sec); + return strcmp(name, ".debug_abbrev") == 0; +} + // text | rodata | got | data | bss bool elf_is_same_area(const elf_file_t *ef, const Elf64_Shdr *a, const Elf64_Shdr *b) { diff --git a/src/elfmerge/elf_read_elf.h b/src/elfmerge/elf_read_elf.h index 29a3426f51042008137b1bb096de4a38779a337d..98b747664df40c194d681b453d58c583a810861a 100644 --- a/src/elfmerge/elf_read_elf.h +++ b/src/elfmerge/elf_read_elf.h @@ -22,7 +22,10 @@ #define RELOCATION_ROOT_DIR "/usr/lib/relocation" typedef struct { - Elf64_Ehdr *hdr; + union { + Elf64_Ehdr *hdr; + void *data; + }; Elf64_Phdr *segments; Elf64_Shdr *sechdrs; @@ -341,6 +344,7 @@ static inline bool elf_is_version_sec(Elf64_Shdr *sec) Elf64_Shdr *elf_find_section_by_tls_offset(elf_file_t *ef, unsigned long obj_tls_offset); Elf64_Shdr *elf_find_section_by_name(elf_file_t *ef, const char *sec_name); +void *elf_find_section_ptr_by_name(elf_file_t *ef, const char *sec_name); Elf64_Shdr *elf_find_section_by_addr(elf_file_t *ef, unsigned long addr); typedef bool (*section_filter_func)(const elf_file_t *ef, const Elf64_Shdr *sec); bool elf_is_relro_section(const elf_file_t *ef, const Elf64_Shdr *sechdr); @@ -350,6 +354,12 @@ bool got_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec); bool rwdata_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec); bool bss_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec); bool elf_is_debug_section(elf_file_t *ef, Elf64_Shdr *sechdr); +bool elf_is_rela_debug_section(elf_file_t *ef, Elf64_Shdr *sechdr); +bool debug_info_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec); +bool debug_line_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec); +bool debug_str_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec); +bool debug_line_str_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec); +bool debug_abbrev_section_filter(const elf_file_t *ef, const Elf64_Shdr *sec); bool elf_is_same_area(const elf_file_t *ef, const Elf64_Shdr *a, const Elf64_Shdr *b); // ELF diff --git a/src/elfmerge/elf_write_elf.c b/src/elfmerge/elf_write_elf.c index 7d5e4ad07fc27d11877bd94179860471fe0d5623..9b9d6a7151065a8a9dd3ca2853afc343cc10bdc0 100644 --- a/src/elfmerge/elf_write_elf.c +++ b/src/elfmerge/elf_write_elf.c @@ -65,12 +65,21 @@ unsigned int elf_align_file_section(elf_link_t *elf_link, Elf64_Shdr *sec, bool return elf_link->next_file_offset; } +void print_memory(void *dest, size_t num_bytes) { + unsigned char *ptr = (unsigned char *)dest; + for (size_t i = 0; i < num_bytes; i++) { + printf("%02X ", ptr[i]); + } + printf("\n"); +} + void *write_elf_file(elf_link_t *elf_link, void *src, unsigned int len) { void *dest = ((void *)elf_link->out_ef.hdr) + elf_link->next_file_offset; (void)memcpy(dest, src, len); elf_link->next_file_offset += len; + // TODO debug should not change next_mem_addr? elf_link->next_mem_addr += len; return dest; @@ -396,13 +405,22 @@ static void append_section(elf_link_t *elf_link, Elf64_Shdr *dst_sec, elf_file_t if (dst_sec->sh_offset != 0 && sec->sh_type == SHT_NOBITS && !(sec->sh_flags & SHF_TLS)) { is_align_file_offset = false; } + // TODO clean code + char *name = elf_get_section_name(ef, sec); + if (strstr(name, "debug") != NULL) { + is_align_file_offset = false; + } // offset in PAGE inherit from in ELF elf_align_file_section(elf_link, sec, is_align_file_offset); // first in section to dst section if (dst_sec->sh_offset == 0) { dst_sec->sh_offset = elf_link->next_file_offset; - dst_sec->sh_addr = elf_link->next_mem_addr; + if (elf_is_debug_section(ef, dst_sec) || elf_is_rela_debug_section(ef, dst_sec)) { + dst_sec->sh_addr = 0; + } else { + dst_sec->sh_addr = elf_link->next_mem_addr; + } } write_elf_file_section(elf_link, ef, sec, dst_sec); @@ -487,10 +505,19 @@ static void merge_filter_sections(elf_link_t *elf_link, char *sec_name, section_ merge_filter_section(elf_link, dst_sec, ef, filter); } - dst_sec->sh_size = elf_link->next_mem_addr - dst_sec->sh_addr; + dst_sec->sh_size = elf_get_sh_size(elf_link, dst_sec); SI_LOG_DEBUG("section %-20s %08lx %08lx %06lx\n", sec_name, dst_sec->sh_addr, dst_sec->sh_offset, dst_sec->sh_size); } +void merge_debug_sections(elf_link_t *elf_link) +{ + merge_filter_sections(elf_link, ".debug_info", debug_info_section_filter); + merge_filter_sections(elf_link, ".debug_abbrev", debug_abbrev_section_filter); + merge_filter_sections(elf_link, ".debug_line", debug_line_section_filter); + merge_filter_sections(elf_link, ".debug_str", debug_str_section_filter); + merge_filter_sections(elf_link, ".debug_line_str", debug_line_str_section_filter); +} + void merge_text_sections(elf_link_t *elf_link) { merge_filter_sections(elf_link, ".text", text_section_filter); diff --git a/src/elfmerge/elf_write_elf.h b/src/elfmerge/elf_write_elf.h index 0d9e005188be98c75ada962ee6d959c920838e9a..97761cc6cdd4c8f5fe64d551695072a0002f99de 100644 --- a/src/elfmerge/elf_write_elf.h +++ b/src/elfmerge/elf_write_elf.h @@ -38,6 +38,7 @@ unsigned int elf_align_file_section(elf_link_t *elf_link, Elf64_Shdr *sec, bool // write section void copy_from_old_elf(elf_link_t *elf_link); +void merge_debug_sections(elf_link_t *elf_link); void merge_text_sections(elf_link_t *elf_link); void merge_rodata_sections(elf_link_t *elf_link); void merge_data_relro_sections(elf_link_t *elf_link); diff --git a/src/elfmerge/meson.build b/src/elfmerge/meson.build index 1e0d0a973a70b4f593c770eb42d5389391ea7fc3..b11a41e0a4c175ac68f9cc3744304bd347fccf3f 100644 --- a/src/elfmerge/meson.build +++ b/src/elfmerge/meson.build @@ -18,6 +18,9 @@ core_sources = files( cflags += ['-fpic', '-pie'] +includes += '/usr/src/debug/libdwarf-0.7.0-1.oe2309.aarch64/src/lib/libdwarf/' +includes += '/usr/include/libdwarf-0/' + executable( 'elfmerge', core_sources, install: true,