X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=ld%2Fldelfgen.c;h=58b37c65bc92bcb765570436a7893d04908bf40c;hb=1034d824d8981cd02f0961153acf5121dcc3d55a;hp=21739b19ca1c8b40cc8fe654329b371da7bc9c05;hpb=b3adc24a0713411ab38a21dc894dd40dbc5c8f4f;p=binutils-gdb.git diff --git a/ld/ldelfgen.c b/ld/ldelfgen.c index 21739b19ca1..58b37c65bc9 100644 --- a/ld/ldelfgen.c +++ b/ld/ldelfgen.c @@ -1,5 +1,5 @@ /* Emulation code used by all ELF targets. - Copyright (C) 1991-2020 Free Software Foundation, Inc. + Copyright (C) 1991-2022 Free Software Foundation, Inc. This file is part of the GNU Binutils. @@ -27,20 +27,273 @@ #include "ldmisc.h" #include "ldexp.h" #include "ldlang.h" +#include "ldctor.h" #include "elf-bfd.h" +#include "elf/internal.h" #include "ldelfgen.h" +/* Info attached to an output_section_statement about input sections, + used when sorting SHF_LINK_ORDER sections. */ + +struct os_sections +{ + /* Size allocated for isec. */ + unsigned int alloc; + /* Used entries in isec. */ + unsigned int count; + /* How many are SHF_LINK_ORDER. */ + unsigned int ordered; + /* Input sections attached to this output section. */ + struct os_sections_input { + lang_input_section_type *is; + unsigned int idx; + } isec[1]; +}; + +/* Add IS to data kept for OS. */ + +static bool +add_link_order_input_section (lang_input_section_type *is, + lang_output_section_statement_type *os) +{ + struct os_sections *os_info = os->data; + asection *s; + + if (os_info == NULL) + { + os_info = xmalloc (sizeof (*os_info) + 63 * sizeof (*os_info->isec)); + os_info->alloc = 64; + os_info->count = 0; + os_info->ordered = 0; + os->data = os_info; + } + if (os_info->count == os_info->alloc) + { + size_t want; + os_info->alloc *= 2; + want = sizeof (*os_info) + (os_info->alloc - 1) * sizeof (*os_info->isec); + os_info = xrealloc (os_info, want); + os->data = os_info; + } + os_info->isec[os_info->count].is = is; + os_info->isec[os_info->count].idx = os_info->count; + os_info->count++; + s = is->section; + if (bfd_get_flavour (s->owner) == bfd_target_elf_flavour + && (s->flags & SEC_LINKER_CREATED) == 0 + && elf_linked_to_section (s) != NULL) + os_info->ordered++; + return false; +} + +/* Run over the linker's statement list, extracting info about input + sections attached to each output section. */ + +static bool +link_order_scan (lang_statement_union_type *u, + lang_output_section_statement_type *os) +{ + asection *s; + bool ret = false; + + for (; u != NULL; u = u->header.next) + { + switch (u->header.type) + { + case lang_wild_statement_enum: + if (link_order_scan (u->wild_statement.children.head, os)) + ret = true; + break; + case lang_constructors_statement_enum: + if (link_order_scan (constructor_list.head, os)) + ret = true; + break; + case lang_output_section_statement_enum: + if (u->output_section_statement.constraint != -1 + && link_order_scan (u->output_section_statement.children.head, + &u->output_section_statement)) + ret = true; + break; + case lang_group_statement_enum: + if (link_order_scan (u->group_statement.children.head, os)) + ret = true; + break; + case lang_input_section_enum: + s = u->input_section.section; + if (s->output_section != NULL + && s->output_section->owner == link_info.output_bfd + && (s->output_section->flags & SEC_EXCLUDE) == 0 + && ((s->output_section->flags & SEC_HAS_CONTENTS) != 0 + || ((s->output_section->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) + == (SEC_LOAD | SEC_THREAD_LOCAL)))) + if (add_link_order_input_section (&u->input_section, os)) + ret = true; + break; + default: + break; + } + } + return ret; +} + +/* Compare two sections based on the locations of the sections they are + linked to. Used by fixup_link_order. */ + +static int +compare_link_order (const void *a, const void *b) +{ + const struct os_sections_input *ai = a; + const struct os_sections_input *bi = b; + asection *asec = NULL; + asection *bsec = NULL; + bfd_vma apos, bpos; + + if (bfd_get_flavour (ai->is->section->owner) == bfd_target_elf_flavour) + asec = elf_linked_to_section (ai->is->section); + if (bfd_get_flavour (bi->is->section->owner) == bfd_target_elf_flavour) + bsec = elf_linked_to_section (bi->is->section); + + /* Place unordered sections before ordered sections. */ + if (asec == NULL || bsec == NULL) + { + if (bsec != NULL) + return -1; + else if (asec != NULL) + return 1; + return ai->idx - bi->idx; + } + + apos = asec->output_section->lma + asec->output_offset; + bpos = bsec->output_section->lma + bsec->output_offset; + + if (apos < bpos) + return -1; + else if (apos > bpos) + return 1; + + if (! bfd_link_relocatable (&link_info)) + { + /* The only way we should get matching LMAs is when the first of + the two sections has zero size, or asec and bsec are the + same section. */ + if (asec->size < bsec->size) + return -1; + else if (asec->size > bsec->size) + return 1; + } + + /* If they are both zero size then they almost certainly have the same + VMA and thus are not ordered with respect to each other. Test VMA + anyway, and fall back to idx to make the result reproducible across + qsort implementations. */ + apos = asec->output_section->vma + asec->output_offset; + bpos = bsec->output_section->vma + bsec->output_offset; + if (apos < bpos) + return -1; + else if (apos > bpos) + return 1; + else + return ai->idx - bi->idx; +} + +/* Rearrange sections with SHF_LINK_ORDER into the same order as their + linked sections. */ + +static bool +fixup_link_order (lang_output_section_statement_type *os) +{ + struct os_sections *os_info = os->data; + unsigned int i, j; + lang_input_section_type **orig_is; + asection **save_s; + + for (i = 0; i < os_info->count; i = j) + { + /* Normally a linker script will select SHF_LINK_ORDER sections + with an input section wildcard something like the following: + *(.IA_64.unwind* .gnu.linkonce.ia64unw.*) + However if some other random sections are smashed into an + output section, or if SHF_LINK_ORDER are split up by the + linker script, then we only want to sort sections matching a + given wildcard. That's the purpose of the pattern test. */ + for (j = i + 1; j < os_info->count; j++) + if (os_info->isec[j].is->pattern != os_info->isec[i].is->pattern) + break; + if (j - i > 1) + qsort (&os_info->isec[i], j - i, sizeof (*os_info->isec), + compare_link_order); + } + for (i = 0; i < os_info->count; i++) + if (os_info->isec[i].idx != i) + break; + if (i == os_info->count) + return false; + + /* Now reorder the linker input section statements to reflect the + proper sorting. The is done by rewriting the existing statements + rather than fiddling with lists, since the only thing we need to + change is the bfd section pointer. */ + orig_is = xmalloc (os_info->count * sizeof (*orig_is)); + save_s = xmalloc (os_info->count * sizeof (*save_s)); + for (i = 0; i < os_info->count; i++) + { + orig_is[os_info->isec[i].idx] = os_info->isec[i].is; + save_s[i] = os_info->isec[i].is->section; + } + for (i = 0; i < os_info->count; i++) + if (os_info->isec[i].idx != i) + { + orig_is[i]->section = save_s[i]; + /* Restore os_info to pristine state before the qsort, for the + next pass over sections. */ + os_info->isec[i].is = orig_is[i]; + os_info->isec[i].idx = i; + } + free (save_s); + free (orig_is); + return true; +} + void -ldelf_map_segments (bfd_boolean need_layout) +ldelf_map_segments (bool need_layout) { int tries = 10; + static bool done_link_order_scan = false; do { lang_relax_sections (need_layout); - need_layout = FALSE; + need_layout = false; - if (link_info.output_bfd->xvec->flavour == bfd_target_elf_flavour + if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour) + { + lang_output_section_statement_type *os; + if (!done_link_order_scan) + { + link_order_scan (statement_list.head, NULL); + done_link_order_scan = true; + } + for (os = (void *) lang_os_list.head; os != NULL; os = os->next) + { + struct os_sections *os_info = os->data; + if (os_info != NULL && os_info->ordered != 0) + { + if (os_info->ordered != os_info->count + && bfd_link_relocatable (&link_info)) + { + einfo (_("%F%P: " + "%pA has both ordered and unordered sections\n"), + os->bfd_section); + return; + } + if (os_info->count > 1 + && fixup_link_order (os)) + need_layout = true; + } + } + } + + if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour && !bfd_link_relocatable (&link_info)) { bfd_size_type phdr_size; @@ -51,7 +304,8 @@ ldelf_map_segments (bfd_boolean need_layout) if (lang_phdr_list == NULL) elf_seg_map (link_info.output_bfd) = NULL; if (!_bfd_elf_map_sections_to_segments (link_info.output_bfd, - &link_info)) + &link_info, + &need_layout)) einfo (_("%F%P: map sections to segments failed: %E\n")); if (phdr_size != elf_program_header_size (link_info.output_bfd)) @@ -59,11 +313,11 @@ ldelf_map_segments (bfd_boolean need_layout) if (tries > 6) /* The first few times we allow any change to phdr_size . */ - need_layout = TRUE; + need_layout = true; else if (phdr_size < elf_program_header_size (link_info.output_bfd)) /* After that we only allow the size to grow. */ - need_layout = TRUE; + need_layout = true; else elf_program_header_size (link_info.output_bfd) = phdr_size; } @@ -72,9 +326,23 @@ ldelf_map_segments (bfd_boolean need_layout) while (need_layout && --tries); if (tries == 0) - einfo (_("%F%P: looping in map_segments")); + einfo (_("%F%P: looping in map_segments\n")); + + if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour + && lang_phdr_list == NULL) + { + /* If we don't have user supplied phdrs, strip zero-sized dynamic + sections and regenerate program headers. */ + const struct elf_backend_data *bed + = get_elf_backend_data (link_info.output_bfd); + if (bed->elf_backend_strip_zero_sized_dynamic_sections + && !bed->elf_backend_strip_zero_sized_dynamic_sections + (&link_info)) + einfo (_("%F%P: failed to strip zero-sized dynamic sections\n")); + } } +#ifdef ENABLE_LIBCTF /* We want to emit CTF early if and only if we are not targetting ELF with this invocation. */ @@ -89,11 +357,9 @@ ldelf_emit_ctf_early (void) /* Callbacks used to map from bfd types to libctf types, under libctf's control. */ -struct ctf_strsym_iter_cb_arg +struct ctf_strtab_iter_cb_arg { - struct elf_sym_strtab *syms; - bfd_size_type symcount; - struct elf_strtab_hash *symstrtab; + struct elf_strtab_hash *strtab; size_t next_i; size_t next_idx; }; @@ -107,20 +373,27 @@ ldelf_ctf_strtab_iter_cb (uint32_t *offset, void *arg_) bfd_size_type off; const char *ret; - struct ctf_strsym_iter_cb_arg *arg = - (struct ctf_strsym_iter_cb_arg *) arg_; + struct ctf_strtab_iter_cb_arg *arg = + (struct ctf_strtab_iter_cb_arg *) arg_; /* There is no zeroth string. */ if (arg->next_i == 0) arg->next_i = 1; - if (arg->next_i >= _bfd_elf_strtab_len (arg->symstrtab)) + /* Hunt through strings until we fall off the end or find one with + a nonzero refcount. */ + do { - arg->next_i = 0; - return NULL; + if (arg->next_i >= _bfd_elf_strtab_len (arg->strtab)) + { + arg->next_i = 0; + return NULL; + } + + ret = _bfd_elf_strtab_str (arg->strtab, arg->next_i++, &off); } + while (ret == NULL); - ret = _bfd_elf_strtab_str (arg->symstrtab, arg->next_i++, &off); *offset = off; /* If we've overflowed, we cannot share any further strings: the CTF @@ -131,56 +404,74 @@ ldelf_ctf_strtab_iter_cb (uint32_t *offset, void *arg_) return ret; } -/* Return symbols from the symbol table to libctf, one by one. We assume (and - assert) that the symbols in the elf_link_hash_table are in strictly ascending - order, and that none will be added in between existing ones. Returns NULL - when iteration is complete. */ - -static struct ctf_link_sym * -ldelf_ctf_symbols_iter_cb (struct ctf_link_sym *dest, - void *arg_) +void +ldelf_acquire_strings_for_ctf + (struct ctf_dict *ctf_output, struct elf_strtab_hash *strtab) { - struct ctf_strsym_iter_cb_arg *arg = - (struct ctf_strsym_iter_cb_arg *) arg_; + struct ctf_strtab_iter_cb_arg args = { strtab, 0, 0 }; + if (!ctf_output) + return; - if (arg->next_i > arg->symcount) + if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour) { - arg->next_i = 0; - arg->next_idx = 0; - return NULL; + if (ctf_link_add_strtab (ctf_output, ldelf_ctf_strtab_iter_cb, + &args) < 0) + einfo (_("%F%P: warning: CTF strtab association failed; strings will " + "not be shared: %s\n"), + ctf_errmsg (ctf_errno (ctf_output))); } - - ASSERT (arg->syms[arg->next_i].dest_index == arg->next_idx); - dest->st_name = _bfd_elf_strtab_str (arg->symstrtab, arg->next_i, NULL); - dest->st_shndx = arg->syms[arg->next_i].sym.st_shndx; - dest->st_type = ELF_ST_TYPE (arg->syms[arg->next_i].sym.st_info); - dest->st_value = arg->syms[arg->next_i].sym.st_value; - arg->next_i++; - return dest; } void -ldelf_examine_strtab_for_ctf - (struct ctf_file *ctf_output, struct elf_sym_strtab *syms, - bfd_size_type symcount, struct elf_strtab_hash *symstrtab) +ldelf_new_dynsym_for_ctf (struct ctf_dict *ctf_output, int symidx, + struct elf_internal_sym *sym) { - struct ctf_strsym_iter_cb_arg args = { syms, symcount, symstrtab, - 0, 0 }; - if (!ctf_output) + ctf_link_sym_t lsym; + + if (!ctf_output) return; - if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour - && !bfd_link_relocatable (&link_info)) + /* New symbol. */ + if (sym != NULL) { - if (ctf_link_add_strtab (ctf_output, ldelf_ctf_strtab_iter_cb, - &args) < 0) - einfo (_("%F%P: warning: CTF strtab association failed; strings will " - "not be shared: %s\n"), - ctf_errmsg (ctf_errno (ctf_output))); + lsym.st_name = NULL; + lsym.st_nameidx = sym->st_name; + lsym.st_nameidx_set = 1; + lsym.st_symidx = symidx; + lsym.st_shndx = sym->st_shndx; + lsym.st_type = ELF_ST_TYPE (sym->st_info); + lsym.st_value = sym->st_value; + if (ctf_link_add_linker_symbol (ctf_output, &lsym) < 0) + { + einfo (_("%F%P: warning: CTF symbol addition failed; CTF will " + "not be tied to symbols: %s\n"), + ctf_errmsg (ctf_errno (ctf_output))); + } + } + else + { + /* Shuffle all the symbols. */ - if (ctf_link_shuffle_syms (ctf_output, ldelf_ctf_symbols_iter_cb, - &args) < 0) - einfo (_("%F%P: warning: CTF symbol shuffling failed; slight space " - "cost: %s\n"), ctf_errmsg (ctf_errno (ctf_output))); + if (ctf_link_shuffle_syms (ctf_output) < 0) + einfo (_("%F%P: warning: CTF symbol shuffling failed; CTF will " + "not be tied to symbols: %s\n"), + ctf_errmsg (ctf_errno (ctf_output))); } } +#else +int +ldelf_emit_ctf_early (void) +{ + return 0; +} + +void +ldelf_acquire_strings_for_ctf (struct ctf_dict *ctf_output ATTRIBUTE_UNUSED, + struct elf_strtab_hash *strtab ATTRIBUTE_UNUSED) +{} +void +ldelf_new_dynsym_for_ctf (struct ctf_dict *ctf_output ATTRIBUTE_UNUSED, + int symidx ATTRIBUTE_UNUSED, + struct elf_internal_sym *sym ATTRIBUTE_UNUSED) +{} +#endif