From: mengqinggang Date: Fri, 22 May 2026 01:38:49 +0000 (+0800) Subject: LoongArch: Fix relaxation alignment with ld -r (PR 33236) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8bf4b69718d406abc99dd9cdc4c9cef79d7344cf;p=thirdparty%2Fbinutils-gdb.git LoongArch: Fix relaxation alignment with ld -r (PR 33236) LoongArch has the same issue as RISC-V for PR 33236 [1]. Section alignment can't be adjusted for objects generated by ld -r. If previous sections are relaxed, the subsequent section maybe misaligned. To fix this, add an align section and an align relocation before each section when ld -r. And change the section alignment to 4 to disable the default section start address calculation. ld.lld has fixed this issue in the following two patches [2] [3]. [1] https://sourceware.org/bugzilla/show_bug.cgi?id=33236 [2] https://github.com/llvm/llvm-project/pull/151639 [3] https://github.com/llvm/llvm-project/pull/198147 --- diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c index b7af2041238..f4d8d32ff2f 100644 --- a/bfd/elfnn-loongarch.c +++ b/bfd/elfnn-loongarch.c @@ -7054,6 +7054,149 @@ elf_loongarch64_hash_symbol (struct elf_link_hash_entry *h) return _bfd_elf_hash_symbol (h); } +/* Write nops to align section. */ + +static bool +loongarch_build_align_nops (asection *align_section) +{ + bfd_byte *loc = align_section->contents; + for (bfd_size_type i = 0; i < (align_section->size / 4); i++) + { + bfd_putl32 (LARCH_NOP, loc); + loc += 4; + } + + return true; +} + +/* Emit an align relocation and a related undefined symbol. */ + +static bool +loongarch_add_align_relocs (asection *align_sec, bfd* align_bfd) +{ + elf_backend_data *bed = get_elf_backend_data (align_bfd); + + if (elf_section_data (align_sec)->relocs == NULL) + { + align_sec->reloc_count = 1; + align_sec->flags |= SEC_RELOC; + + Elf_Internal_Rela *rela = (Elf_Internal_Rela *) + bfd_malloc (sizeof (Elf_Internal_Rela)); + if (rela == NULL) + return false; + + rela->r_offset = 0; + rela->r_addend = align_sec->size; + rela->r_info = ELFNN_R_INFO (0, R_LARCH_ALIGN); + elf_section_data (align_sec)->relocs = rela; + + Elf_Internal_Shdr *rela_hdr = (Elf_Internal_Shdr *) + bfd_zalloc (align_bfd, sizeof (Elf_Internal_Shdr)); + if (rela_hdr == NULL) + return false; + + rela_hdr->sh_size = sizeof (Elf_Internal_Rela); + rela_hdr->sh_entsize = sizeof (Elf_Internal_Rela); + rela_hdr->sh_type = SHT_RELA; + rela_hdr->sh_addralign = (bfd_vma) 1 << bed->s->log_file_align; + elf_section_data (align_sec)->rela.hdr = rela_hdr; + } + + /* Used in loongarch_elf_relax_section for align relocations. */ + Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (align_bfd); + if (symtab_hdr->contents == NULL) + { + Elf_Internal_Sym *sym = (Elf_Internal_Sym *) + bfd_zmalloc (sizeof (Elf_Internal_Sym)); + if (sym == NULL) + return false; + + /* Match if (r_symndx < symtab_hdr->sh_info) branch in + loongarch_elf_relocate_section to avoid find link failed. */ + symtab_hdr->sh_info = 1; + symtab_hdr->contents = (unsigned char *) sym; + } + + return true; +} + +/* Section name for aligns is the associated section name plus this + string. */ +#define ALIGN_SUFFIX ".align" + +/* Determine and set the size of the align section for a final link. */ + +bool +elfNN_loongarch_size_aligns (bfd *output_bfd, + bfd *align_bfd, + struct bfd_link_info *info, + asection * (*add_align_section) + (const char *, asection *), + void (*layout_sections_again) (void)) +{ + bool need_laying_out = false; + for (bfd *input_bfd = info->input_bfds; input_bfd != NULL; + input_bfd = input_bfd->link.next) + { + asection *sec; + asection *align_sec; + + if (!is_loongarch_elf (input_bfd) + || (input_bfd->flags & BFD_LINKER_CREATED) != 0) + continue; + + for (sec = input_bfd->sections; sec != NULL; sec = sec->next) + { + /* If this section is not a code section, do nothing. */ + if ((sec->flags & SEC_CODE) == 0) + continue; + + /* If this section is a link-once section that will be + discarded, then don't create any stubs. */ + if (sec->output_section == NULL + || sec->output_section->owner != output_bfd) + continue; + + if (sec->alignment_power > 2) + { + char *s_name; + size_t namelen; + bfd_size_type len; + namelen = strlen (sec->name); + len = namelen + sizeof (ALIGN_SUFFIX); + s_name = bfd_alloc (align_bfd, len); + if (s_name == NULL) + return false; + memcpy (s_name, sec->name, namelen); + memcpy (s_name + namelen, ALIGN_SUFFIX, sizeof (ALIGN_SUFFIX)); + + align_sec = add_align_section (s_name, sec); + + if (align_sec == NULL) + return false; + + if (! loongarch_build_align_nops (align_sec)) + return false; + + if (! loongarch_add_align_relocs (align_sec, align_bfd)) + return false; + + /* Change the section alignment to 4 to disable the default + behavior of "dot += alignment_needed" in ldlang.c. */ + bfd_set_section_alignment (sec, 2); + + need_laying_out = true; + } + } + } + + if (need_laying_out) + layout_sections_again (); + + return true; +} + #define TARGET_LITTLE_SYM loongarch_elfNN_vec #define TARGET_LITTLE_NAME "elfNN-loongarch" #define ELF_ARCH bfd_arch_loongarch diff --git a/bfd/elfxx-loongarch.h b/bfd/elfxx-loongarch.h index 400b7377ccd..b295b7a9070 100644 --- a/bfd/elfxx-loongarch.h +++ b/bfd/elfxx-loongarch.h @@ -45,6 +45,16 @@ bfd_elf64_loongarch_set_data_segment_info (struct bfd_link_info *, int *); bfd_byte * loongarch_write_unsigned_leb128 (bfd_byte *, size_t, bfd_vma) ATTRIBUTE_HIDDEN; +extern bool elf64_loongarch_size_aligns + (bfd *, bfd *, struct bfd_link_info *, + struct bfd_section * (*) (const char *, struct bfd_section *), + void (*) (void)); + +extern bool elf32_loongarch_size_aligns + (bfd *, bfd *, struct bfd_link_info *, + struct bfd_section * (*) (const char *, struct bfd_section *), + void (*) (void)); + /* TRUE if this is a PLT reference to a local IFUNC. */ #define PLT_LOCAL_IFUNC_P(INFO, H) \ ((H)->dynindx == -1 \ diff --git a/ld/emultempl/loongarchelf.em b/ld/emultempl/loongarchelf.em index 713331d30fc..901186cd114 100644 --- a/ld/emultempl/loongarchelf.em +++ b/ld/emultempl/loongarchelf.em @@ -41,6 +41,10 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}' ' fragment <header.next) + { + switch (l->header.type) + { + case lang_constructors_statement_enum: + ret = hook_in_align (add, input_section, &constructor_list.head); + if (ret) + return ret; + break; + + case lang_output_section_statement_enum: + ret = hook_in_align (add, input_section, + &l->output_section_statement.children.head); + if (ret) + return ret; + break; + + case lang_wild_statement_enum: + ret = hook_in_align (add, input_section, + &l->wild_statement.children.head); + if (ret) + return ret; + break; + + case lang_group_statement_enum: + ret = hook_in_align (add, input_section, + &l->group_statement.children.head); + if (ret) + return ret; + break; + + case lang_input_section_enum: + if (l->input_section.section == input_section) + { + /* We've found our section. Insert the align immediately + before its associated input section. */ + *lp = add.head; + add.head->header.next = l; + return true; + } + break; + + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + + default: + FAIL (); + break; + } + } + + return false; +} + +/* Create a new align section, and arrange for it to be linked + immediately before INPUT_SECTION. */ + +static asection * +elf${ELFSIZE}_loongarch_add_align_section (const char *align_sec_name, + asection *input_section) +{ + flagword flags; + asection *align_sec; + asection *output_section; + lang_statement_list_type add_child; + lang_output_section_statement_type *os; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE + | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP); + align_sec = bfd_make_section_anyway_with_flags (align_file->the_bfd, + align_sec_name, flags); + if (align_sec == NULL) + goto err_ret; + + align_sec->veneer = 1; + bfd_set_section_alignment (align_sec, 2); + + output_section = input_section->output_section; + os = lang_output_section_get (output_section); + + lang_list_init (&add_child); + lang_add_section (&add_child, align_sec, NULL, NULL, os); + + if (add_child.head == NULL) + goto err_ret; + + align_sec->size = (1 << input_section->alignment_power) - 4; + align_sec->contents = bfd_alloc (align_file->the_bfd, align_sec->size); + if (align_sec->contents == NULL && align_sec->size != 0) + goto err_ret; + align_sec->alloced = 1; + + if (hook_in_align (add_child, input_section, &os->children.head)) + return align_sec; + + err_ret: + einfo (_("%X%P: can not make align section: %E\n")); + return NULL; +} + +static void +gldloongarch_layout_sections_again (void) +{ + /* If we have changed sizes of the align sections, then we need + to recalculate all the section offsets. */ + ldelf_map_segments (true); +} + static void gld${EMULATION_NAME}_after_allocation (void) { @@ -78,6 +210,21 @@ gld${EMULATION_NAME}_after_allocation (void) } } + /* If generating a relocatable output file, we have to add align + at the start of sections. */ + if (align_file != NULL && bfd_link_relocatable (&link_info)) + { + if (! elf${ELFSIZE}_loongarch_size_aligns (link_info.output_bfd, + align_file->the_bfd, + &link_info, + &elf${ELFSIZE}_loongarch_add_align_section, + &gldloongarch_layout_sections_again)) + { + einfo (_("%X%P: can not size align section: %E\n")); + return; + } + } + /* The program header size of executable file may increase. */ if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour && !bfd_link_relocatable (&link_info)) @@ -101,7 +248,40 @@ gld${EMULATION_NAME}_after_allocation (void) ldelf_map_segments (need_layout); } +/* This is called before the input files are opened. We create a new + fake input file to hold the align sections. */ + +static void +loongarch_elf_create_output_section_statements (void) +{ + if (! bfd_link_relocatable (&link_info)) + return; + + align_file = lang_add_input_file ("linker aligns", + lang_input_file_is_fake_enum, + NULL); + align_file->the_bfd = bfd_create ("linker aligns", + link_info.output_bfd); + if (align_file->the_bfd == NULL + || ! bfd_set_arch_mach (align_file->the_bfd, + bfd_get_arch (link_info.output_bfd), + bfd_get_mach (link_info.output_bfd))) + { + fatal (_("%P: can not create BFD: %E\n")); + return; + } + + align_file->the_bfd->flags |= BFD_LINKER_CREATED; + + Elf_Internal_Ehdr *ehdr = elf_elfheader (align_file->the_bfd); + elf_backend_data *bed = get_elf_backend_data (link_info.output_bfd); + ehdr->e_ident[EI_CLASS] = bed->s->elfclass; + + ldlang_add_file (align_file); +} + EOF LDEMUL_BEFORE_ALLOCATION=larch_elf_before_allocation LDEMUL_AFTER_ALLOCATION=gld${EMULATION_NAME}_after_allocation +LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=loongarch_elf_create_output_section_statements diff --git a/ld/testsuite/ld-loongarch-elf/relax-align-ld-r.d b/ld/testsuite/ld-loongarch-elf/relax-align-ld-r.d new file mode 100644 index 00000000000..a2d19734a59 --- /dev/null +++ b/ld/testsuite/ld-loongarch-elf/relax-align-ld-r.d @@ -0,0 +1,9 @@ +#as: +#ld: -r +#objdump: -Dr + +#... +.*R_LARCH_ALIGN.* +#... +.*R_LARCH_ALIGN.* +#... diff --git a/ld/testsuite/ld-loongarch-elf/relax-align-ld-r.s b/ld/testsuite/ld-loongarch-elf/relax-align-ld-r.s new file mode 100644 index 00000000000..19555a7148e --- /dev/null +++ b/ld/testsuite/ld-loongarch-elf/relax-align-ld-r.s @@ -0,0 +1,12 @@ +# Add an align section before text section when ld -r. +# There will be two R_LARCH_ALIGN relocations after ld -r. +.text + addi.d $a0, $a0, 1 + call36 func + .align 3 +func: + addi.d $a0, $a0, 1 + +# Only add a align section for code section. +.data + .4byte 0x1234 diff --git a/ld/testsuite/ld-loongarch-elf/relax.exp b/ld/testsuite/ld-loongarch-elf/relax.exp index 31787a93967..bb8429ab515 100644 --- a/ld/testsuite/ld-loongarch-elf/relax.exp +++ b/ld/testsuite/ld-loongarch-elf/relax.exp @@ -48,6 +48,7 @@ proc run_partial_linking_align_test {} { if [istarget loongarch*-*-*] { run_dump_test "relax-align-1" + run_dump_test "relax-align-ld-r" } if [istarget loongarch64-*-*] {