mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-24 23:32:06 +00:00
478 lines
13 KiB
C
478 lines
13 KiB
C
/* Emulation code used by all ELF targets.
|
|
Copyright (C) 1991-2022 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU Binutils.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include "sysdep.h"
|
|
#include "bfd.h"
|
|
#include "bfdlink.h"
|
|
#include "ctf-api.h"
|
|
#include "ld.h"
|
|
#include "ldmain.h"
|
|
#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 (bool need_layout)
|
|
{
|
|
int tries = 10;
|
|
static bool done_link_order_scan = false;
|
|
|
|
do
|
|
{
|
|
lang_relax_sections (need_layout);
|
|
need_layout = false;
|
|
|
|
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;
|
|
|
|
phdr_size = elf_program_header_size (link_info.output_bfd);
|
|
/* If we don't have user supplied phdrs, throw away any
|
|
previous linker generated program headers. */
|
|
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,
|
|
&need_layout))
|
|
einfo (_("%F%P: map sections to segments failed: %E\n"));
|
|
|
|
if (phdr_size != elf_program_header_size (link_info.output_bfd))
|
|
{
|
|
if (tries > 6)
|
|
/* The first few times we allow any change to
|
|
phdr_size . */
|
|
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;
|
|
else
|
|
elf_program_header_size (link_info.output_bfd) = phdr_size;
|
|
}
|
|
}
|
|
}
|
|
while (need_layout && --tries);
|
|
|
|
if (tries == 0)
|
|
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. */
|
|
|
|
int
|
|
ldelf_emit_ctf_early (void)
|
|
{
|
|
if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Callbacks used to map from bfd types to libctf types, under libctf's
|
|
control. */
|
|
|
|
struct ctf_strtab_iter_cb_arg
|
|
{
|
|
struct elf_strtab_hash *strtab;
|
|
size_t next_i;
|
|
size_t next_idx;
|
|
};
|
|
|
|
/* Return strings from the strtab to libctf, one by one. Returns NULL when
|
|
iteration is complete. */
|
|
|
|
static const char *
|
|
ldelf_ctf_strtab_iter_cb (uint32_t *offset, void *arg_)
|
|
{
|
|
bfd_size_type off;
|
|
const char *ret;
|
|
|
|
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;
|
|
|
|
/* Hunt through strings until we fall off the end or find one with
|
|
a nonzero refcount. */
|
|
do
|
|
{
|
|
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);
|
|
|
|
*offset = off;
|
|
|
|
/* If we've overflowed, we cannot share any further strings: the CTF
|
|
format cannot encode strings with such high offsets. */
|
|
if (*offset != off)
|
|
return NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
ldelf_acquire_strings_for_ctf
|
|
(struct ctf_dict *ctf_output, struct elf_strtab_hash *strtab)
|
|
{
|
|
struct ctf_strtab_iter_cb_arg args = { strtab, 0, 0 };
|
|
if (!ctf_output)
|
|
return;
|
|
|
|
if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
|
|
{
|
|
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)));
|
|
}
|
|
}
|
|
|
|
void
|
|
ldelf_new_dynsym_for_ctf (struct ctf_dict *ctf_output, int symidx,
|
|
struct elf_internal_sym *sym)
|
|
{
|
|
ctf_link_sym_t lsym;
|
|
|
|
if (!ctf_output)
|
|
return;
|
|
|
|
/* New symbol. */
|
|
if (sym != NULL)
|
|
{
|
|
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) < 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
|