mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2026-08-27 00:26:02 -04:00
bfd/RISC-V: move _bfd_riscv_elf_merge_private_bfd_data()
The sole size dependent parameter used there is ARCH_SIZE. For just this there's no good reason to build all of the involved code twice. Make the value a function parameter instead. Reviewed-by: Jiawei <jiawei@iscas.ac.cn>
This commit is contained in:
parent
de7a82d533
commit
3a5657bd3f
3 changed files with 572 additions and 558 deletions
|
|
@ -193,11 +193,6 @@ struct _bfd_riscv_elf_obj_tdata
|
|||
(*((h) != NULL ? &riscv_elf_hash_entry (h)->tls_type \
|
||||
: &_bfd_riscv_elf_local_got_tls_type (abfd) [symndx]))
|
||||
|
||||
#define is_riscv_elf(bfd) \
|
||||
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
|
||||
&& elf_tdata (bfd) != NULL \
|
||||
&& elf_object_id (bfd) == RISCV_ELF_DATA)
|
||||
|
||||
static bool
|
||||
elfNN_riscv_mkobject (bfd *abfd)
|
||||
{
|
||||
|
|
@ -3918,560 +3913,10 @@ riscv_reloc_type_class (const struct bfd_link_info *info,
|
|||
}
|
||||
}
|
||||
|
||||
/* Given the ELF header flags in FLAGS, it returns a string that describes the
|
||||
float ABI. */
|
||||
|
||||
static const char *
|
||||
riscv_float_abi_string (flagword flags)
|
||||
{
|
||||
switch (flags & EF_RISCV_FLOAT_ABI)
|
||||
{
|
||||
case EF_RISCV_FLOAT_ABI_SOFT:
|
||||
return "soft-float";
|
||||
break;
|
||||
case EF_RISCV_FLOAT_ABI_SINGLE:
|
||||
return "single-float";
|
||||
break;
|
||||
case EF_RISCV_FLOAT_ABI_DOUBLE:
|
||||
return "double-float";
|
||||
break;
|
||||
case EF_RISCV_FLOAT_ABI_QUAD:
|
||||
return "quad-float";
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
/* The information of architecture elf attributes. */
|
||||
static riscv_subset_list_t in_subsets;
|
||||
static riscv_subset_list_t out_subsets;
|
||||
static riscv_subset_list_t merged_subsets;
|
||||
|
||||
/* Predicator for standard extension. */
|
||||
|
||||
static bool
|
||||
riscv_std_ext_p (const char *name)
|
||||
riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
|
||||
{
|
||||
return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's');
|
||||
}
|
||||
|
||||
/* Update the output subset's version to match the input when the input
|
||||
subset's version is newer. */
|
||||
|
||||
static void
|
||||
riscv_update_subset_version (struct riscv_subset_t *in,
|
||||
struct riscv_subset_t *out)
|
||||
{
|
||||
if (in == NULL || out == NULL)
|
||||
return;
|
||||
|
||||
/* Update the output ISA versions to the newest ones, but otherwise don't
|
||||
provide any errors or warnings about mis-matched ISA versions as it's
|
||||
generally too tricky to check for these at link time. */
|
||||
if ((in->major_version > out->major_version)
|
||||
|| (in->major_version == out->major_version
|
||||
&& in->minor_version > out->minor_version)
|
||||
|| (out->major_version == RISCV_UNKNOWN_VERSION))
|
||||
{
|
||||
out->major_version = in->major_version;
|
||||
out->minor_version = in->minor_version;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if subset is 'i' or 'e'. */
|
||||
|
||||
static bool
|
||||
riscv_i_or_e_p (bfd *ibfd,
|
||||
const char *arch,
|
||||
struct riscv_subset_t *subset)
|
||||
{
|
||||
if ((strcasecmp (subset->name, "e") != 0)
|
||||
&& (strcasecmp (subset->name, "i") != 0))
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: corrupted ISA string '%s'. "
|
||||
"First letter should be 'i' or 'e' but got '%s'"),
|
||||
ibfd, arch, subset->name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Merge standard extensions.
|
||||
|
||||
Return Value:
|
||||
Return FALSE if failed to merge.
|
||||
|
||||
Arguments:
|
||||
`bfd`: bfd handler.
|
||||
`in_arch`: Raw ISA string for input object.
|
||||
`out_arch`: Raw ISA string for output object.
|
||||
`pin`: Subset list for input object.
|
||||
`pout`: Subset list for output object. */
|
||||
|
||||
static bool
|
||||
riscv_merge_std_ext (bfd *ibfd,
|
||||
const char *in_arch,
|
||||
const char *out_arch,
|
||||
struct riscv_subset_t **pin,
|
||||
struct riscv_subset_t **pout)
|
||||
{
|
||||
const char *standard_exts = "mafdqlcbjtpvnh";
|
||||
const char *p;
|
||||
struct riscv_subset_t *in = *pin;
|
||||
struct riscv_subset_t *out = *pout;
|
||||
|
||||
/* First letter should be 'i' or 'e'. */
|
||||
if (!riscv_i_or_e_p (ibfd, in_arch, in))
|
||||
return false;
|
||||
|
||||
if (!riscv_i_or_e_p (ibfd, out_arch, out))
|
||||
return false;
|
||||
|
||||
if (strcasecmp (in->name, out->name) != 0)
|
||||
{
|
||||
/* TODO: We might allow merge 'i' with 'e'. */
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: mis-matched ISA string to merge '%s' and '%s'"),
|
||||
ibfd, in->name, out->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
riscv_update_subset_version(in, out);
|
||||
riscv_add_subset (&merged_subsets,
|
||||
out->name, out->major_version, out->minor_version);
|
||||
|
||||
in = in->next;
|
||||
out = out->next;
|
||||
|
||||
/* Handle standard extension first. */
|
||||
for (p = standard_exts; *p; ++p)
|
||||
{
|
||||
struct riscv_subset_t *ext_in, *ext_out, *ext_merged;
|
||||
char find_ext[2] = {*p, '\0'};
|
||||
bool find_in, find_out;
|
||||
|
||||
find_in = riscv_lookup_subset (&in_subsets, find_ext, &ext_in);
|
||||
find_out = riscv_lookup_subset (&out_subsets, find_ext, &ext_out);
|
||||
|
||||
if (!find_in && !find_out)
|
||||
continue;
|
||||
|
||||
if (find_in && find_out)
|
||||
riscv_update_subset_version(ext_in, ext_out);
|
||||
|
||||
ext_merged = find_out ? ext_out : ext_in;
|
||||
riscv_add_subset (&merged_subsets, ext_merged->name,
|
||||
ext_merged->major_version, ext_merged->minor_version);
|
||||
}
|
||||
|
||||
/* Skip all standard extensions. */
|
||||
while ((in != NULL) && riscv_std_ext_p (in->name)) in = in->next;
|
||||
while ((out != NULL) && riscv_std_ext_p (out->name)) out = out->next;
|
||||
|
||||
*pin = in;
|
||||
*pout = out;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Merge multi letter extensions. PIN is a pointer to the head of the input
|
||||
object subset list. Likewise for POUT and the output object. Return TRUE
|
||||
on success and FALSE when a conflict is found. */
|
||||
|
||||
static bool
|
||||
riscv_merge_multi_letter_ext (riscv_subset_t **pin,
|
||||
riscv_subset_t **pout)
|
||||
{
|
||||
riscv_subset_t *in = *pin;
|
||||
riscv_subset_t *out = *pout;
|
||||
riscv_subset_t *tail;
|
||||
|
||||
int cmp;
|
||||
|
||||
while (in && out)
|
||||
{
|
||||
cmp = riscv_compare_subsets (in->name, out->name);
|
||||
|
||||
if (cmp < 0)
|
||||
{
|
||||
/* `in' comes before `out', append `in' and increment. */
|
||||
riscv_add_subset (&merged_subsets, in->name, in->major_version,
|
||||
in->minor_version);
|
||||
in = in->next;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
{
|
||||
/* `out' comes before `in', append `out' and increment. */
|
||||
riscv_add_subset (&merged_subsets, out->name, out->major_version,
|
||||
out->minor_version);
|
||||
out = out->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Both present, check version and increment both. */
|
||||
riscv_update_subset_version (in, out);
|
||||
|
||||
riscv_add_subset (&merged_subsets, out->name, out->major_version,
|
||||
out->minor_version);
|
||||
out = out->next;
|
||||
in = in->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (in || out)
|
||||
{
|
||||
/* If we're here, either `in' or `out' is running longer than
|
||||
the other. So, we need to append the corresponding tail. */
|
||||
tail = in ? in : out;
|
||||
while (tail)
|
||||
{
|
||||
riscv_add_subset (&merged_subsets, tail->name, tail->major_version,
|
||||
tail->minor_version);
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Merge Tag_RISCV_arch attribute. */
|
||||
|
||||
static char *
|
||||
riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
|
||||
{
|
||||
riscv_subset_t *in, *out;
|
||||
static char *merged_arch_str = NULL;
|
||||
|
||||
unsigned xlen_in, xlen_out;
|
||||
merged_subsets.head = NULL;
|
||||
merged_subsets.tail = NULL;
|
||||
|
||||
riscv_parse_subset_t riscv_rps_ld_in =
|
||||
{&in_subsets, _bfd_error_handler, &xlen_in, NULL, false};
|
||||
riscv_parse_subset_t riscv_rps_ld_out =
|
||||
{&out_subsets, _bfd_error_handler, &xlen_out, NULL, false};
|
||||
|
||||
if (in_arch == NULL && out_arch == NULL)
|
||||
return NULL;
|
||||
if (in_arch == NULL && out_arch != NULL)
|
||||
return out_arch;
|
||||
if (in_arch != NULL && out_arch == NULL)
|
||||
return in_arch;
|
||||
|
||||
/* Parse subset from ISA string. */
|
||||
if (!riscv_parse_subset (&riscv_rps_ld_in, in_arch))
|
||||
return NULL;
|
||||
if (!riscv_parse_subset (&riscv_rps_ld_out, out_arch))
|
||||
return NULL;
|
||||
|
||||
/* Checking XLEN. */
|
||||
if (xlen_out != xlen_in)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: ISA string of input (%s) doesn't match "
|
||||
"output (%s)"), ibfd, in_arch, out_arch);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Merge subset list. */
|
||||
in = in_subsets.head;
|
||||
out = out_subsets.head;
|
||||
|
||||
/* Merge standard extension. */
|
||||
if (!riscv_merge_std_ext (ibfd, in_arch, out_arch, &in, &out))
|
||||
return NULL;
|
||||
|
||||
/* Merge all non-single letter extensions with single call. */
|
||||
if (!riscv_merge_multi_letter_ext (&in, &out))
|
||||
return NULL;
|
||||
|
||||
if (xlen_in != xlen_out)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: XLEN of input (%u) doesn't match "
|
||||
"output (%u)"), ibfd, xlen_in, xlen_out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (xlen_in != ARCH_SIZE)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: unsupported XLEN (%u), you might be "
|
||||
"using wrong emulation"), ibfd, xlen_in);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free the previous merged_arch_str which called xmalloc. */
|
||||
free (merged_arch_str);
|
||||
|
||||
merged_arch_str = riscv_arch_str (ARCH_SIZE, &merged_subsets,
|
||||
false/* update */);
|
||||
|
||||
/* Release the subset lists. */
|
||||
riscv_release_subset_list (&in_subsets);
|
||||
riscv_release_subset_list (&out_subsets);
|
||||
riscv_release_subset_list (&merged_subsets);
|
||||
|
||||
return merged_arch_str;
|
||||
}
|
||||
|
||||
/* Merge object attributes from IBFD into output_bfd of INFO.
|
||||
Raise an error if there are conflicting attributes. */
|
||||
|
||||
static bool
|
||||
riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
|
||||
{
|
||||
bfd *obfd = info->output_bfd;
|
||||
obj_attribute *in_attr;
|
||||
obj_attribute *out_attr;
|
||||
bool result = true;
|
||||
bool priv_attrs_merged = false;
|
||||
const char *sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
|
||||
unsigned int i;
|
||||
|
||||
/* Skip linker created files. */
|
||||
if (ibfd->flags & BFD_LINKER_CREATED)
|
||||
return true;
|
||||
|
||||
/* Skip any input that doesn't have an attribute section.
|
||||
This enables to link object files without attribute section with
|
||||
any others. */
|
||||
if (bfd_get_section_by_name (ibfd, sec_name) == NULL)
|
||||
return true;
|
||||
|
||||
if (!elf_known_obj_attributes_proc (obfd)[0].i)
|
||||
{
|
||||
/* This is the first object. Copy the attributes. */
|
||||
_bfd_elf_copy_obj_attributes (ibfd, obfd);
|
||||
|
||||
out_attr = elf_known_obj_attributes_proc (obfd);
|
||||
|
||||
/* Use the Tag_null value to indicate the attributes have been
|
||||
initialized. */
|
||||
out_attr[0].i = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
in_attr = elf_known_obj_attributes_proc (ibfd);
|
||||
out_attr = elf_known_obj_attributes_proc (obfd);
|
||||
|
||||
for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case Tag_RISCV_arch:
|
||||
if (!out_attr[Tag_RISCV_arch].s)
|
||||
out_attr[Tag_RISCV_arch].s = in_attr[Tag_RISCV_arch].s;
|
||||
else if (in_attr[Tag_RISCV_arch].s
|
||||
&& out_attr[Tag_RISCV_arch].s)
|
||||
{
|
||||
/* Check compatible. */
|
||||
char *merged_arch =
|
||||
riscv_merge_arch_attr_info (ibfd,
|
||||
in_attr[Tag_RISCV_arch].s,
|
||||
out_attr[Tag_RISCV_arch].s);
|
||||
if (merged_arch == NULL)
|
||||
{
|
||||
result = false;
|
||||
out_attr[Tag_RISCV_arch].s = "";
|
||||
}
|
||||
else
|
||||
out_attr[Tag_RISCV_arch].s = merged_arch;
|
||||
}
|
||||
break;
|
||||
|
||||
case Tag_RISCV_priv_spec:
|
||||
case Tag_RISCV_priv_spec_minor:
|
||||
case Tag_RISCV_priv_spec_revision:
|
||||
/* If we have handled the privileged elf attributes, then skip it. */
|
||||
if (!priv_attrs_merged)
|
||||
{
|
||||
unsigned int Tag_a = Tag_RISCV_priv_spec;
|
||||
unsigned int Tag_b = Tag_RISCV_priv_spec_minor;
|
||||
unsigned int Tag_c = Tag_RISCV_priv_spec_revision;
|
||||
enum riscv_spec_class in_priv_spec = PRIV_SPEC_CLASS_NONE;
|
||||
enum riscv_spec_class out_priv_spec = PRIV_SPEC_CLASS_NONE;
|
||||
|
||||
/* Get the privileged spec class from elf attributes. */
|
||||
riscv_get_priv_spec_class_from_numbers (in_attr[Tag_a].i,
|
||||
in_attr[Tag_b].i,
|
||||
in_attr[Tag_c].i,
|
||||
&in_priv_spec);
|
||||
riscv_get_priv_spec_class_from_numbers (out_attr[Tag_a].i,
|
||||
out_attr[Tag_b].i,
|
||||
out_attr[Tag_c].i,
|
||||
&out_priv_spec);
|
||||
|
||||
/* Allow to link the object without the privileged specs. */
|
||||
if (out_priv_spec == PRIV_SPEC_CLASS_NONE)
|
||||
{
|
||||
out_attr[Tag_a].i = in_attr[Tag_a].i;
|
||||
out_attr[Tag_b].i = in_attr[Tag_b].i;
|
||||
out_attr[Tag_c].i = in_attr[Tag_c].i;
|
||||
}
|
||||
else if (in_priv_spec != PRIV_SPEC_CLASS_NONE
|
||||
&& in_priv_spec != out_priv_spec)
|
||||
{
|
||||
/* The abandoned privileged spec v1.9.1 can not be linked with
|
||||
others since the conflicts. Keep the check since compatible
|
||||
issue. */
|
||||
if (in_priv_spec == PRIV_SPEC_CLASS_1P9P1
|
||||
|| out_priv_spec == PRIV_SPEC_CLASS_1P9P1)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("warning: privileged spec version 1.9.1 can not be "
|
||||
"linked with other spec versions"));
|
||||
}
|
||||
|
||||
/* Update the output privileged spec to the newest one. */
|
||||
if (in_priv_spec > out_priv_spec)
|
||||
{
|
||||
out_attr[Tag_a].i = in_attr[Tag_a].i;
|
||||
out_attr[Tag_b].i = in_attr[Tag_b].i;
|
||||
out_attr[Tag_c].i = in_attr[Tag_c].i;
|
||||
}
|
||||
}
|
||||
priv_attrs_merged = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case Tag_RISCV_unaligned_access:
|
||||
out_attr[i].i |= in_attr[i].i;
|
||||
break;
|
||||
|
||||
case Tag_RISCV_stack_align:
|
||||
if (out_attr[i].i == 0)
|
||||
out_attr[i].i = in_attr[i].i;
|
||||
else if (in_attr[i].i != 0
|
||||
&& out_attr[i].i != 0
|
||||
&& out_attr[i].i != in_attr[i].i)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB use %u-byte stack aligned but the output "
|
||||
"use %u-byte stack aligned"),
|
||||
ibfd, in_attr[i].i, out_attr[i].i);
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
result &= _bfd_elf_merge_unknown_attribute_low (ibfd, obfd, i);
|
||||
}
|
||||
|
||||
/* If out_attr was copied from in_attr then it won't have a type yet. */
|
||||
if (in_attr[i].type && !out_attr[i].type)
|
||||
out_attr[i].type = in_attr[i].type;
|
||||
}
|
||||
|
||||
/* Merge Tag_compatibility attributes and any common GNU ones. */
|
||||
if (!_bfd_elf_merge_object_attributes (ibfd, info))
|
||||
return false;
|
||||
|
||||
/* Check for any attributes not known on RISC-V. */
|
||||
result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Merge backend specific data from an object file to the output
|
||||
object file when linking. */
|
||||
|
||||
static bool
|
||||
_bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
|
||||
{
|
||||
bfd *obfd = info->output_bfd;
|
||||
flagword new_flags, old_flags;
|
||||
|
||||
if (!is_riscv_elf (ibfd))
|
||||
return true;
|
||||
|
||||
if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0)
|
||||
{
|
||||
(*_bfd_error_handler)
|
||||
(_("%pB: ABI is incompatible with that of the selected emulation:\n"
|
||||
" target emulation `%s' does not match `%s'"),
|
||||
ibfd, bfd_get_target (ibfd), bfd_get_target (obfd));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_bfd_elf_merge_object_attributes (ibfd, info))
|
||||
return false;
|
||||
|
||||
if (!riscv_merge_attributes (ibfd, info))
|
||||
return false;
|
||||
|
||||
/* Check to see if the input BFD actually contains any sections. If not,
|
||||
its flags may not have been initialized either, but it cannot actually
|
||||
cause any incompatibility. Do not short-circuit dynamic objects; their
|
||||
section list may be emptied by elf_link_add_object_symbols.
|
||||
|
||||
Also check to see if there are no code sections in the input. In this
|
||||
case, there is no need to check for code specific flags. */
|
||||
if (!(ibfd->flags & DYNAMIC))
|
||||
{
|
||||
bool null_input_bfd = true;
|
||||
bool only_data_sections = true;
|
||||
asection *sec;
|
||||
|
||||
for (sec = ibfd->sections; sec != NULL; sec = sec->next)
|
||||
{
|
||||
null_input_bfd = false;
|
||||
|
||||
if ((bfd_section_flags (sec)
|
||||
& (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
|
||||
== (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
|
||||
{
|
||||
only_data_sections = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (null_input_bfd || only_data_sections)
|
||||
return true;
|
||||
}
|
||||
|
||||
new_flags = elf_elfheader (ibfd)->e_flags;
|
||||
old_flags = elf_elfheader (obfd)->e_flags;
|
||||
|
||||
if (!elf_flags_init (obfd))
|
||||
{
|
||||
elf_flags_init (obfd) = true;
|
||||
elf_elfheader (obfd)->e_flags = new_flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Disallow linking different float ABIs. */
|
||||
if ((old_flags ^ new_flags) & EF_RISCV_FLOAT_ABI)
|
||||
{
|
||||
(*_bfd_error_handler)
|
||||
(_("%pB: can't link %s modules with %s modules"), ibfd,
|
||||
riscv_float_abi_string (new_flags),
|
||||
riscv_float_abi_string (old_flags));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Disallow linking RVE and non-RVE. */
|
||||
if ((old_flags ^ new_flags) & EF_RISCV_RVE)
|
||||
{
|
||||
(*_bfd_error_handler)
|
||||
(_("%pB: can't link RVE with other target"), ibfd);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Allow linking RVC and non-RVC, and keep the RVC flag. */
|
||||
elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_RVC;
|
||||
|
||||
/* Allow linking TSO and non-TSO, and keep the TSO flag. */
|
||||
elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_TSO;
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
bfd_set_error (bfd_error_bad_value);
|
||||
return false;
|
||||
return _bfd_riscv_elf_merge_private_bfd_data (ibfd, info, ARCH_SIZE);
|
||||
}
|
||||
|
||||
/* Ignore and report warning for the unknwon elf attribute. */
|
||||
|
|
@ -6095,7 +5540,7 @@ elfNN_riscv_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd,
|
|||
riscv_elf_link_hash_table_create
|
||||
#define bfd_elfNN_bfd_reloc_type_lookup riscv_reloc_type_lookup
|
||||
#define bfd_elfNN_bfd_merge_private_bfd_data \
|
||||
_bfd_riscv_elf_merge_private_bfd_data
|
||||
riscv_elf_merge_private_bfd_data
|
||||
#define bfd_elfNN_bfd_is_target_special_symbol \
|
||||
riscv_elf_is_target_special_symbol
|
||||
#define bfd_elfNN_bfd_relax_section _bfd_riscv_relax_section
|
||||
|
|
|
|||
|
|
@ -3432,6 +3432,566 @@ riscv_print_extensions (void)
|
|||
printf ("\n");
|
||||
}
|
||||
|
||||
/* Given the ELF header flags in FLAGS, it returns a string that describes the
|
||||
float ABI. */
|
||||
|
||||
static const char *
|
||||
riscv_float_abi_string (flagword flags)
|
||||
{
|
||||
switch (flags & EF_RISCV_FLOAT_ABI)
|
||||
{
|
||||
case EF_RISCV_FLOAT_ABI_SOFT:
|
||||
return "soft-float";
|
||||
break;
|
||||
case EF_RISCV_FLOAT_ABI_SINGLE:
|
||||
return "single-float";
|
||||
break;
|
||||
case EF_RISCV_FLOAT_ABI_DOUBLE:
|
||||
return "double-float";
|
||||
break;
|
||||
case EF_RISCV_FLOAT_ABI_QUAD:
|
||||
return "quad-float";
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
/* The information of architecture elf attributes. */
|
||||
static riscv_subset_list_t in_subsets;
|
||||
static riscv_subset_list_t out_subsets;
|
||||
static riscv_subset_list_t merged_subsets;
|
||||
|
||||
/* Predicator for standard extension. */
|
||||
|
||||
static bool
|
||||
riscv_std_ext_p (const char *name)
|
||||
{
|
||||
return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's');
|
||||
}
|
||||
|
||||
/* Update the output subset's version to match the input when the input
|
||||
subset's version is newer. */
|
||||
|
||||
static void
|
||||
riscv_update_subset_version (struct riscv_subset_t *in,
|
||||
struct riscv_subset_t *out)
|
||||
{
|
||||
if (in == NULL || out == NULL)
|
||||
return;
|
||||
|
||||
/* Update the output ISA versions to the newest ones, but otherwise don't
|
||||
provide any errors or warnings about mis-matched ISA versions as it's
|
||||
generally too tricky to check for these at link time. */
|
||||
if ((in->major_version > out->major_version)
|
||||
|| (in->major_version == out->major_version
|
||||
&& in->minor_version > out->minor_version)
|
||||
|| (out->major_version == RISCV_UNKNOWN_VERSION))
|
||||
{
|
||||
out->major_version = in->major_version;
|
||||
out->minor_version = in->minor_version;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if subset is 'i' or 'e'. */
|
||||
|
||||
static bool
|
||||
riscv_i_or_e_p (bfd *ibfd,
|
||||
const char *arch,
|
||||
struct riscv_subset_t *subset)
|
||||
{
|
||||
if ((strcasecmp (subset->name, "e") != 0)
|
||||
&& (strcasecmp (subset->name, "i") != 0))
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: corrupted ISA string '%s'. "
|
||||
"First letter should be 'i' or 'e' but got '%s'"),
|
||||
ibfd, arch, subset->name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Merge standard extensions.
|
||||
|
||||
Return Value:
|
||||
Return FALSE if failed to merge.
|
||||
|
||||
Arguments:
|
||||
`bfd`: bfd handler.
|
||||
`in_arch`: Raw ISA string for input object.
|
||||
`out_arch`: Raw ISA string for output object.
|
||||
`pin`: Subset list for input object.
|
||||
`pout`: Subset list for output object. */
|
||||
|
||||
static bool
|
||||
riscv_merge_std_ext (bfd *ibfd,
|
||||
const char *in_arch,
|
||||
const char *out_arch,
|
||||
struct riscv_subset_t **pin,
|
||||
struct riscv_subset_t **pout)
|
||||
{
|
||||
const char *standard_exts = "mafdqlcbjtpvnh";
|
||||
const char *p;
|
||||
struct riscv_subset_t *in = *pin;
|
||||
struct riscv_subset_t *out = *pout;
|
||||
|
||||
/* First letter should be 'i' or 'e'. */
|
||||
if (!riscv_i_or_e_p (ibfd, in_arch, in))
|
||||
return false;
|
||||
|
||||
if (!riscv_i_or_e_p (ibfd, out_arch, out))
|
||||
return false;
|
||||
|
||||
if (strcasecmp (in->name, out->name) != 0)
|
||||
{
|
||||
/* TODO: We might allow merge 'i' with 'e'. */
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: mis-matched ISA string to merge '%s' and '%s'"),
|
||||
ibfd, in->name, out->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
riscv_update_subset_version(in, out);
|
||||
riscv_add_subset (&merged_subsets,
|
||||
out->name, out->major_version, out->minor_version);
|
||||
|
||||
in = in->next;
|
||||
out = out->next;
|
||||
|
||||
/* Handle standard extension first. */
|
||||
for (p = standard_exts; *p; ++p)
|
||||
{
|
||||
struct riscv_subset_t *ext_in, *ext_out, *ext_merged;
|
||||
char find_ext[2] = {*p, '\0'};
|
||||
bool find_in, find_out;
|
||||
|
||||
find_in = riscv_lookup_subset (&in_subsets, find_ext, &ext_in);
|
||||
find_out = riscv_lookup_subset (&out_subsets, find_ext, &ext_out);
|
||||
|
||||
if (!find_in && !find_out)
|
||||
continue;
|
||||
|
||||
if (find_in && find_out)
|
||||
riscv_update_subset_version(ext_in, ext_out);
|
||||
|
||||
ext_merged = find_out ? ext_out : ext_in;
|
||||
riscv_add_subset (&merged_subsets, ext_merged->name,
|
||||
ext_merged->major_version, ext_merged->minor_version);
|
||||
}
|
||||
|
||||
/* Skip all standard extensions. */
|
||||
while ((in != NULL) && riscv_std_ext_p (in->name)) in = in->next;
|
||||
while ((out != NULL) && riscv_std_ext_p (out->name)) out = out->next;
|
||||
|
||||
*pin = in;
|
||||
*pout = out;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Merge multi letter extensions. PIN is a pointer to the head of the input
|
||||
object subset list. Likewise for POUT and the output object. Return TRUE
|
||||
on success and FALSE when a conflict is found. */
|
||||
|
||||
static bool
|
||||
riscv_merge_multi_letter_ext (riscv_subset_t **pin,
|
||||
riscv_subset_t **pout)
|
||||
{
|
||||
riscv_subset_t *in = *pin;
|
||||
riscv_subset_t *out = *pout;
|
||||
riscv_subset_t *tail;
|
||||
|
||||
int cmp;
|
||||
|
||||
while (in && out)
|
||||
{
|
||||
cmp = riscv_compare_subsets (in->name, out->name);
|
||||
|
||||
if (cmp < 0)
|
||||
{
|
||||
/* `in' comes before `out', append `in' and increment. */
|
||||
riscv_add_subset (&merged_subsets, in->name, in->major_version,
|
||||
in->minor_version);
|
||||
in = in->next;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
{
|
||||
/* `out' comes before `in', append `out' and increment. */
|
||||
riscv_add_subset (&merged_subsets, out->name, out->major_version,
|
||||
out->minor_version);
|
||||
out = out->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Both present, check version and increment both. */
|
||||
riscv_update_subset_version (in, out);
|
||||
|
||||
riscv_add_subset (&merged_subsets, out->name, out->major_version,
|
||||
out->minor_version);
|
||||
out = out->next;
|
||||
in = in->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (in || out)
|
||||
{
|
||||
/* If we're here, either `in' or `out' is running longer than
|
||||
the other. So, we need to append the corresponding tail. */
|
||||
tail = in ? in : out;
|
||||
while (tail)
|
||||
{
|
||||
riscv_add_subset (&merged_subsets, tail->name, tail->major_version,
|
||||
tail->minor_version);
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Merge Tag_RISCV_arch attribute. */
|
||||
|
||||
static char *
|
||||
riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch,
|
||||
unsigned int arch_size)
|
||||
{
|
||||
riscv_subset_t *in, *out;
|
||||
static char *merged_arch_str = NULL;
|
||||
|
||||
unsigned xlen_in, xlen_out;
|
||||
merged_subsets.head = NULL;
|
||||
merged_subsets.tail = NULL;
|
||||
|
||||
riscv_parse_subset_t riscv_rps_ld_in =
|
||||
{&in_subsets, _bfd_error_handler, &xlen_in, NULL, false};
|
||||
riscv_parse_subset_t riscv_rps_ld_out =
|
||||
{&out_subsets, _bfd_error_handler, &xlen_out, NULL, false};
|
||||
|
||||
if (in_arch == NULL && out_arch == NULL)
|
||||
return NULL;
|
||||
if (in_arch == NULL && out_arch != NULL)
|
||||
return out_arch;
|
||||
if (in_arch != NULL && out_arch == NULL)
|
||||
return in_arch;
|
||||
|
||||
/* Parse subset from ISA string. */
|
||||
if (!riscv_parse_subset (&riscv_rps_ld_in, in_arch))
|
||||
return NULL;
|
||||
if (!riscv_parse_subset (&riscv_rps_ld_out, out_arch))
|
||||
return NULL;
|
||||
|
||||
/* Checking XLEN. */
|
||||
if (xlen_out != xlen_in)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: ISA string of input (%s) doesn't match "
|
||||
"output (%s)"), ibfd, in_arch, out_arch);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Merge subset list. */
|
||||
in = in_subsets.head;
|
||||
out = out_subsets.head;
|
||||
|
||||
/* Merge standard extension. */
|
||||
if (!riscv_merge_std_ext (ibfd, in_arch, out_arch, &in, &out))
|
||||
return NULL;
|
||||
|
||||
/* Merge all non-single letter extensions with single call. */
|
||||
if (!riscv_merge_multi_letter_ext (&in, &out))
|
||||
return NULL;
|
||||
|
||||
if (xlen_in != xlen_out)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: XLEN of input (%u) doesn't match "
|
||||
"output (%u)"), ibfd, xlen_in, xlen_out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (xlen_in != arch_size)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB: unsupported XLEN (%u), you might be "
|
||||
"using wrong emulation"), ibfd, xlen_in);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free the previous merged_arch_str which called xmalloc. */
|
||||
free (merged_arch_str);
|
||||
|
||||
merged_arch_str = riscv_arch_str (arch_size, &merged_subsets,
|
||||
false/* update */);
|
||||
|
||||
/* Release the subset lists. */
|
||||
riscv_release_subset_list (&in_subsets);
|
||||
riscv_release_subset_list (&out_subsets);
|
||||
riscv_release_subset_list (&merged_subsets);
|
||||
|
||||
return merged_arch_str;
|
||||
}
|
||||
|
||||
/* Merge object attributes from IBFD into output_bfd of INFO.
|
||||
Raise an error if there are conflicting attributes. */
|
||||
|
||||
static bool
|
||||
riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info,
|
||||
unsigned int arch_size)
|
||||
{
|
||||
bfd *obfd = info->output_bfd;
|
||||
obj_attribute *in_attr;
|
||||
obj_attribute *out_attr;
|
||||
bool result = true;
|
||||
bool priv_attrs_merged = false;
|
||||
const char *sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
|
||||
unsigned int i;
|
||||
|
||||
/* Skip linker created files. */
|
||||
if (ibfd->flags & BFD_LINKER_CREATED)
|
||||
return true;
|
||||
|
||||
/* Skip any input that doesn't have an attribute section.
|
||||
This enables to link object files without attribute section with
|
||||
any others. */
|
||||
if (bfd_get_section_by_name (ibfd, sec_name) == NULL)
|
||||
return true;
|
||||
|
||||
if (!elf_known_obj_attributes_proc (obfd)[0].i)
|
||||
{
|
||||
/* This is the first object. Copy the attributes. */
|
||||
_bfd_elf_copy_obj_attributes (ibfd, obfd);
|
||||
|
||||
out_attr = elf_known_obj_attributes_proc (obfd);
|
||||
|
||||
/* Use the Tag_null value to indicate the attributes have been
|
||||
initialized. */
|
||||
out_attr[0].i = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
in_attr = elf_known_obj_attributes_proc (ibfd);
|
||||
out_attr = elf_known_obj_attributes_proc (obfd);
|
||||
|
||||
for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case Tag_RISCV_arch:
|
||||
if (!out_attr[Tag_RISCV_arch].s)
|
||||
out_attr[Tag_RISCV_arch].s = in_attr[Tag_RISCV_arch].s;
|
||||
else if (in_attr[Tag_RISCV_arch].s
|
||||
&& out_attr[Tag_RISCV_arch].s)
|
||||
{
|
||||
/* Check compatible. */
|
||||
char *merged_arch =
|
||||
riscv_merge_arch_attr_info (ibfd,
|
||||
in_attr[Tag_RISCV_arch].s,
|
||||
out_attr[Tag_RISCV_arch].s,
|
||||
arch_size);
|
||||
if (merged_arch == NULL)
|
||||
{
|
||||
result = false;
|
||||
out_attr[Tag_RISCV_arch].s = "";
|
||||
}
|
||||
else
|
||||
out_attr[Tag_RISCV_arch].s = merged_arch;
|
||||
}
|
||||
break;
|
||||
|
||||
case Tag_RISCV_priv_spec:
|
||||
case Tag_RISCV_priv_spec_minor:
|
||||
case Tag_RISCV_priv_spec_revision:
|
||||
/* If we have handled the privileged elf attributes, then skip it. */
|
||||
if (!priv_attrs_merged)
|
||||
{
|
||||
unsigned int Tag_a = Tag_RISCV_priv_spec;
|
||||
unsigned int Tag_b = Tag_RISCV_priv_spec_minor;
|
||||
unsigned int Tag_c = Tag_RISCV_priv_spec_revision;
|
||||
enum riscv_spec_class in_priv_spec = PRIV_SPEC_CLASS_NONE;
|
||||
enum riscv_spec_class out_priv_spec = PRIV_SPEC_CLASS_NONE;
|
||||
|
||||
/* Get the privileged spec class from elf attributes. */
|
||||
riscv_get_priv_spec_class_from_numbers (in_attr[Tag_a].i,
|
||||
in_attr[Tag_b].i,
|
||||
in_attr[Tag_c].i,
|
||||
&in_priv_spec);
|
||||
riscv_get_priv_spec_class_from_numbers (out_attr[Tag_a].i,
|
||||
out_attr[Tag_b].i,
|
||||
out_attr[Tag_c].i,
|
||||
&out_priv_spec);
|
||||
|
||||
/* Allow to link the object without the privileged specs. */
|
||||
if (out_priv_spec == PRIV_SPEC_CLASS_NONE)
|
||||
{
|
||||
out_attr[Tag_a].i = in_attr[Tag_a].i;
|
||||
out_attr[Tag_b].i = in_attr[Tag_b].i;
|
||||
out_attr[Tag_c].i = in_attr[Tag_c].i;
|
||||
}
|
||||
else if (in_priv_spec != PRIV_SPEC_CLASS_NONE
|
||||
&& in_priv_spec != out_priv_spec)
|
||||
{
|
||||
/* The abandoned privileged spec v1.9.1 can not be linked with
|
||||
others since the conflicts. Keep the check since compatible
|
||||
issue. */
|
||||
if (in_priv_spec == PRIV_SPEC_CLASS_1P9P1
|
||||
|| out_priv_spec == PRIV_SPEC_CLASS_1P9P1)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("warning: privileged spec version 1.9.1 can not be "
|
||||
"linked with other spec versions"));
|
||||
}
|
||||
|
||||
/* Update the output privileged spec to the newest one. */
|
||||
if (in_priv_spec > out_priv_spec)
|
||||
{
|
||||
out_attr[Tag_a].i = in_attr[Tag_a].i;
|
||||
out_attr[Tag_b].i = in_attr[Tag_b].i;
|
||||
out_attr[Tag_c].i = in_attr[Tag_c].i;
|
||||
}
|
||||
}
|
||||
priv_attrs_merged = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case Tag_RISCV_unaligned_access:
|
||||
out_attr[i].i |= in_attr[i].i;
|
||||
break;
|
||||
|
||||
case Tag_RISCV_stack_align:
|
||||
if (out_attr[i].i == 0)
|
||||
out_attr[i].i = in_attr[i].i;
|
||||
else if (in_attr[i].i != 0
|
||||
&& out_attr[i].i != 0
|
||||
&& out_attr[i].i != in_attr[i].i)
|
||||
{
|
||||
_bfd_error_handler
|
||||
(_("error: %pB use %u-byte stack aligned but the output "
|
||||
"use %u-byte stack aligned"),
|
||||
ibfd, in_attr[i].i, out_attr[i].i);
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
result &= _bfd_elf_merge_unknown_attribute_low (ibfd, obfd, i);
|
||||
}
|
||||
|
||||
/* If out_attr was copied from in_attr then it won't have a type yet. */
|
||||
if (in_attr[i].type && !out_attr[i].type)
|
||||
out_attr[i].type = in_attr[i].type;
|
||||
}
|
||||
|
||||
/* Merge Tag_compatibility attributes and any common GNU ones. */
|
||||
if (!_bfd_elf_merge_object_attributes (ibfd, info))
|
||||
return false;
|
||||
|
||||
/* Check for any attributes not known on RISC-V. */
|
||||
result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Merge backend specific data from an object file to the output
|
||||
object file when linking. */
|
||||
|
||||
bool
|
||||
_bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info,
|
||||
unsigned int arch_size)
|
||||
{
|
||||
bfd *obfd = info->output_bfd;
|
||||
flagword new_flags, old_flags;
|
||||
|
||||
if (!is_riscv_elf (ibfd))
|
||||
return true;
|
||||
|
||||
if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0)
|
||||
{
|
||||
(*_bfd_error_handler)
|
||||
(_("%pB: ABI is incompatible with that of the selected emulation:\n"
|
||||
" target emulation `%s' does not match `%s'"),
|
||||
ibfd, bfd_get_target (ibfd), bfd_get_target (obfd));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_bfd_elf_merge_object_attributes (ibfd, info))
|
||||
return false;
|
||||
|
||||
if (!riscv_merge_attributes (ibfd, info, arch_size))
|
||||
return false;
|
||||
|
||||
/* Check to see if the input BFD actually contains any sections. If not,
|
||||
its flags may not have been initialized either, but it cannot actually
|
||||
cause any incompatibility. Do not short-circuit dynamic objects; their
|
||||
section list may be emptied by elf_link_add_object_symbols.
|
||||
|
||||
Also check to see if there are no code sections in the input. In this
|
||||
case, there is no need to check for code specific flags. */
|
||||
if (!(ibfd->flags & DYNAMIC))
|
||||
{
|
||||
bool null_input_bfd = true;
|
||||
bool only_data_sections = true;
|
||||
asection *sec;
|
||||
|
||||
for (sec = ibfd->sections; sec != NULL; sec = sec->next)
|
||||
{
|
||||
null_input_bfd = false;
|
||||
|
||||
if ((bfd_section_flags (sec)
|
||||
& (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
|
||||
== (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
|
||||
{
|
||||
only_data_sections = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (null_input_bfd || only_data_sections)
|
||||
return true;
|
||||
}
|
||||
|
||||
new_flags = elf_elfheader (ibfd)->e_flags;
|
||||
old_flags = elf_elfheader (obfd)->e_flags;
|
||||
|
||||
if (!elf_flags_init (obfd))
|
||||
{
|
||||
elf_flags_init (obfd) = true;
|
||||
elf_elfheader (obfd)->e_flags = new_flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Disallow linking different float ABIs. */
|
||||
if ((old_flags ^ new_flags) & EF_RISCV_FLOAT_ABI)
|
||||
{
|
||||
(*_bfd_error_handler)
|
||||
(_("%pB: can't link %s modules with %s modules"), ibfd,
|
||||
riscv_float_abi_string (new_flags),
|
||||
riscv_float_abi_string (old_flags));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Disallow linking RVE and non-RVE. */
|
||||
if ((old_flags ^ new_flags) & EF_RISCV_RVE)
|
||||
{
|
||||
(*_bfd_error_handler)
|
||||
(_("%pB: can't link RVE with other target"), ibfd);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Allow linking RVC and non-RVC, and keep the RVC flag. */
|
||||
elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_RVC;
|
||||
|
||||
/* Allow linking TSO and non-TSO, and keep the TSO flag. */
|
||||
elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_TSO;
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
bfd_set_error (bfd_error_bad_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find the first input bfd with GNU property and merge it with GPROP. If no
|
||||
such input is found, add it to a new section at the last input. Update
|
||||
GPROP accordingly. */
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@
|
|||
|
||||
#define RISCV_UNKNOWN_VERSION -1
|
||||
|
||||
#define is_riscv_elf(bfd) \
|
||||
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
|
||||
&& elf_tdata (bfd) != NULL \
|
||||
&& elf_object_id (bfd) == RISCV_ELF_DATA)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PLT_NORMAL = 0x0, /* Normal plts. */
|
||||
|
|
@ -138,6 +143,10 @@ bfd_elf32_riscv_set_data_segment_info (struct bfd_link_info *, int *);
|
|||
extern void
|
||||
bfd_elf64_riscv_set_data_segment_info (struct bfd_link_info *, int *);
|
||||
|
||||
extern bool
|
||||
_bfd_riscv_elf_merge_private_bfd_data (bfd *, struct bfd_link_info *,
|
||||
unsigned int) ATTRIBUTE_HIDDEN;
|
||||
|
||||
extern bfd *
|
||||
_bfd_riscv_elf_link_setup_gnu_properties (struct bfd_link_info *, uint32_t *) ATTRIBUTE_HIDDEN;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue