[PATCH v1] LoongArch: Fix relaxation alignment with ld -r (PR 33236)
mengqinggang
mengqinggang@loongson.cn
Mon Jun 8 06:24:01 GMT 2026
Hi Alan, this patch modifies |ld/ldlang.c|. Could you please help review
it? Thanks!
在 2026/5/29 15:43, mengqinggang 写道:
> 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 disabling 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
> ---
> bfd/elfnn-loongarch.c | 152 +++++++++++++++
> bfd/elfxx-loongarch.h | 10 +
> ld/emultempl/loongarchelf.em | 182 ++++++++++++++++++
> ld/ldlang.c | 8 +-
> .../ld-loongarch-elf/relax-align-ld-r.d | 9 +
> .../ld-loongarch-elf/relax-align-ld-r.s | 12 ++
> ld/testsuite/ld-loongarch-elf/relax.exp | 4 +
> 7 files changed, 376 insertions(+), 1 deletion(-)
> create mode 100644 ld/testsuite/ld-loongarch-elf/relax-align-ld-r.d
> create mode 100644 ld/testsuite/ld-loongarch-elf/relax-align-ld-r.s
>
> diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
> index 8f8ec3b0789..ddd321d896f 100644
> --- a/bfd/elfnn-loongarch.c
> +++ b/bfd/elfnn-loongarch.c
> @@ -7067,6 +7067,158 @@ 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 *section, asection *align_section)
> +{
> + if (section->output_section == NULL
> + || align_section->output_section == NULL)
> + {
> + _bfd_error_handler (_("Could not assign to an output section."));
> + return false;
> + }
> +
> + 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 *sec,
> + 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 = (1 << sec->alignment_power) - 4;
> + 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->sh_size = sizeof (Elf_Internal_Sym);
> +// symtab_hdr->sh_entsize = sizeof (Elf_Internal_Sym);
> +// symtab_hdr->sh_type = SHT_SYMTAB;
> +// symtab_hdr->sh_addralign = (bfd_vma) 1 << bed->s->log_file_align;
> + 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 (sec, align_sec))
> + return false;
> +
> + if (! loongarch_add_align_relocs (sec, align_sec, align_bfd))
> + return false;
> +
> + 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 5f9d4106629..bbb9c378781 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 <<EOF + +/* Fake input file for align. */ +static
> lang_input_statement_type *align_file; + static void
> larch_elf_before_allocation (void) { @@ -61,6 +65,136 @@
> larch_elf_before_allocation (void) link_info.relax_pass = 3; } +/*
> Traverse the linker tree to insert the align section + before input
> section. */ + +static bool +hook_in_align (lang_statement_list_type
> add, + asection *input_section, + lang_statement_union_type **lp) +{ +
> bool ret; + lang_statement_union_type *l; + + for (; (l = *lp) !=
> NULL; lp = &l->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. */
> + //*(add.tail) = l->header.next;
> + //l->header.next = add.head;
> + *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 +212,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 +250,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/ldlang.c b/ld/ldlang.c
> index 48dd33a49bb..8c3c287a3a0 100644
> --- a/ld/ldlang.c
> +++ b/ld/ldlang.c
> @@ -5769,6 +5769,10 @@ size_input_section
> {
> bfd_size_type alignment_needed;
>
> + bool synthesize_align = bfd_link_relocatable (&link_info)
> + && (i->flags & SEC_CODE) != 0
> + && bfd_get_arch (i->owner) == bfd_arch_loongarch;
> +
> /* Align this section first to the input sections requirement,
> then to the output section's requirement. If this alignment
> is greater than any seen before, then record it too. Perform
> @@ -5788,7 +5792,9 @@ size_input_section
>
> alignment_needed = align_power (dot, i->alignment_power) - dot;
>
> - if (alignment_needed != 0)
> + /* If synthesized ALIGN may be needed, add one align section
> + and one align relocation and disable the default handling. */
> + if (alignment_needed != 0 && ! synthesize_align)
> {
> insert_pad (this_ptr, fill, TO_SIZE (alignment_needed), o, dot);
> dot += alignment_needed;
> 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 7c32a65244c..066f1893042 100644
> --- a/ld/testsuite/ld-loongarch-elf/relax.exp
> +++ b/ld/testsuite/ld-loongarch-elf/relax.exp
> @@ -46,6 +46,10 @@ proc run_partial_linking_align_test {} {
> }
> }
>
> +if [istarget loongarch*-*-*] {
> + run_dump_test "relax-align-ld-r"
> +}
> +
> if [istarget loongarch64-*-*] {
> if [isbuild loongarch64-*-*] {
> run_dump_test "relax-align-ignore-start"
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://sourceware.org/pipermail/binutils/attachments/20260608/04208ccc/attachment-0001.htm>
More information about the Binutils
mailing list