Files
binutils-gdb/bfd/elf-attrs.c
Jan Beulich 4a890d55ad bfd/ELF: _bfd_elf_obj_attrs_arg_type() is exposed to gas
As a non-private function, it shouldn't have a "_bfd_" prefix, but merely
a "bfd_" one.
2025-11-07 14:59:16 +01:00

838 lines
22 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ELF attributes support (based on ARM EABI attributes).
Copyright (C) 2005-2025 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
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 "libiberty.h"
#include "libbfd.h"
#include "elf-bfd.h"
/* Return the number of bytes needed by I in uleb128 format. */
static uint32_t
uleb128_size (uint32_t i)
{
uint32_t size = 1;
while (i >= 0x80)
{
i >>= 7;
size++;
}
return size;
}
/* Return TRUE if the attribute has the default value (0/""). */
static bool
is_default_attr (obj_attribute *attr)
{
if (ATTR_TYPE_HAS_ERROR (attr->type))
return true;
if (ATTR_TYPE_HAS_INT_VAL (attr->type) && attr->i != 0)
return false;
if (ATTR_TYPE_HAS_STR_VAL (attr->type) && attr->s && *attr->s)
return false;
if (ATTR_TYPE_HAS_NO_DEFAULT (attr->type))
return false;
return true;
}
/* Return the vendor name for a given object attributes section. */
static const char *
obj_attr_v1_vendor_name (bfd *abfd, int vendor)
{
return (vendor == OBJ_ATTR_PROC
? get_elf_backend_data (abfd)->obj_attrs_vendor
: "gnu");
}
/* Return the size of a single attribute. */
static bfd_vma
obj_attr_v1_size (unsigned int tag, obj_attribute *attr)
{
bfd_vma size;
if (is_default_attr (attr))
return 0;
size = uleb128_size (tag);
if (ATTR_TYPE_HAS_INT_VAL (attr->type))
size += uleb128_size (attr->i);
if (ATTR_TYPE_HAS_STR_VAL (attr->type))
size += strlen ((char *)attr->s) + 1;
return size;
}
/* Return the size of the object attributes section for VENDOR
(OBJ_ATTR_PROC or OBJ_ATTR_GNU), or 0 if there are no attributes
for that vendor to record and the vendor is OBJ_ATTR_GNU. */
static bfd_vma
vendor_obj_attrs_v1_size (bfd *abfd, int vendor)
{
bfd_vma size;
obj_attribute *attr;
obj_attribute_list *list;
int i;
const char *vendor_name = obj_attr_v1_vendor_name (abfd, vendor);
if (!vendor_name)
return 0;
attr = elf_known_obj_attributes (abfd)[vendor];
size = 0;
for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
size += obj_attr_v1_size (i, &attr[i]);
for (list = elf_other_obj_attributes (abfd)[vendor];
list;
list = list->next)
size += obj_attr_v1_size (list->tag, &list->attr);
/* <size> <vendor_name> NUL 0x1 <size> */
return (size
? size + 10 + strlen (vendor_name)
: 0);
}
static bfd_vma
bfd_elf_obj_attrs_v1_size (bfd *abfd)
{
bfd_vma size = 0;
size = vendor_obj_attrs_v1_size (abfd, OBJ_ATTR_PROC);
size += vendor_obj_attrs_v1_size (abfd, OBJ_ATTR_GNU);
if (size > 0)
size += sizeof(uint8_t); /* <format-version: A> */
return size;
}
/* Return the size of the object attributes section. */
bfd_vma
bfd_elf_obj_attr_size (bfd *abfd)
{
return bfd_elf_obj_attrs_v1_size (abfd);
}
/* Write VAL in uleb128 format to P, returning a pointer to the
following byte. */
static bfd_byte *
write_uleb128 (bfd_byte *p, uint32_t val)
{
bfd_byte c;
do
{
c = val & 0x7f;
val >>= 7;
if (val)
c |= 0x80;
*(p++) = c;
}
while (val);
return p;
}
/* Write attribute ATTR to butter P, and return a pointer to the following
byte. */
static bfd_byte *
write_obj_attr_v1 (bfd_byte *p, unsigned int tag, obj_attribute *attr)
{
/* Suppress default entries. */
if (is_default_attr (attr))
return p;
p = write_uleb128 (p, tag);
if (ATTR_TYPE_HAS_INT_VAL (attr->type))
p = write_uleb128 (p, attr->i);
if (ATTR_TYPE_HAS_STR_VAL (attr->type))
{
int len;
len = strlen (attr->s) + 1;
memcpy (p, attr->s, len);
p += len;
}
return p;
}
/* Write the contents of the object attributes section (length SIZE)
for VENDOR to CONTENTS. */
static void
write_vendor_obj_attrs_v1 (bfd *abfd, bfd_byte *contents, bfd_vma size,
int vendor)
{
bfd_byte *p;
obj_attribute *attr;
obj_attribute_list *list;
int i;
const char *vendor_name = obj_attr_v1_vendor_name (abfd, vendor);
size_t vendor_length = strlen (vendor_name) + 1;
p = contents;
bfd_put_32 (abfd, size, p);
p += 4;
memcpy (p, vendor_name, vendor_length);
p += vendor_length;
*(p++) = Tag_File;
bfd_put_32 (abfd, size - 4 - vendor_length, p);
p += 4;
attr = elf_known_obj_attributes (abfd)[vendor];
for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
{
unsigned int tag = i;
if (get_elf_backend_data (abfd)->obj_attrs_order)
tag = get_elf_backend_data (abfd)->obj_attrs_order (i);
p = write_obj_attr_v1 (p, tag, &attr[tag]);
}
for (list = elf_other_obj_attributes (abfd)[vendor];
list;
list = list->next)
p = write_obj_attr_v1 (p, list->tag, &list->attr);
}
static void
write_obj_attr_section_v1 (bfd *abfd, bfd_byte *buffer, bfd_vma size)
{
bfd_byte *p = buffer;
/* <format-version: A> */
*(p++) = 'A';
for (int vendor = OBJ_ATTR_FIRST; vendor <= OBJ_ATTR_LAST; ++vendor)
{
bfd_vma vendor_size = vendor_obj_attrs_v1_size (abfd, vendor);
if (vendor_size > 0)
write_vendor_obj_attrs_v1 (abfd, p, vendor_size, vendor);
p += vendor_size;
}
/* We didn't overrun the buffer. */
BFD_ASSERT (p <= buffer + size);
}
/* Write the contents of the object attributes section to CONTENTS. */
void
bfd_elf_set_obj_attr_contents (bfd *abfd, bfd_byte *buffer, bfd_vma size)
{
write_obj_attr_section_v1 (abfd, buffer, size);
}
/* Allocate/find an object attribute. */
static obj_attribute *
elf_new_obj_attr (bfd *abfd, int vendor, unsigned int tag)
{
obj_attribute *attr;
obj_attribute_list *list;
obj_attribute_list *p;
obj_attribute_list **lastp;
if (tag < NUM_KNOWN_OBJ_ATTRIBUTES)
{
/* Known tags are preallocated. */
attr = &elf_known_obj_attributes (abfd)[vendor][tag];
}
else
{
/* Create a new tag. */
list = (obj_attribute_list *)
bfd_alloc (abfd, sizeof (obj_attribute_list));
if (list == NULL)
return NULL;
memset (list, 0, sizeof (obj_attribute_list));
list->tag = tag;
/* Keep the tag list in order. */
lastp = &elf_other_obj_attributes (abfd)[vendor];
for (p = *lastp; p; p = p->next)
{
if (tag < p->tag)
break;
lastp = &p->next;
}
list->next = *lastp;
*lastp = list;
attr = &list->attr;
}
return attr;
}
/* Return the value of an integer object attribute. */
int
bfd_elf_get_obj_attr_int (bfd *abfd, int vendor, unsigned int tag)
{
obj_attribute_list *p;
if (tag < NUM_KNOWN_OBJ_ATTRIBUTES)
{
/* Known tags are preallocated. */
return elf_known_obj_attributes (abfd)[vendor][tag].i;
}
else
{
for (p = elf_other_obj_attributes (abfd)[vendor];
p;
p = p->next)
{
if (tag == p->tag)
return p->attr.i;
if (tag < p->tag)
break;
}
return 0;
}
}
/* Add an integer object attribute. */
obj_attribute *
bfd_elf_add_obj_attr_int (bfd *abfd, int vendor, unsigned int tag, unsigned int i)
{
obj_attribute *attr;
attr = elf_new_obj_attr (abfd, vendor, tag);
if (attr != NULL)
{
attr->type = bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
attr->i = i;
}
return attr;
}
/* Duplicate an object attribute string value. */
static char *
elf_attr_strdup (bfd *abfd, const char *s, const char *end)
{
char *p;
size_t len;
if (end)
len = strnlen (s, end - s);
else
len = strlen (s);
p = (char *) bfd_alloc (abfd, len + 1);
if (p != NULL)
{
memcpy (p, s, len);
p[len] = 0;
}
return p;
}
char *
_bfd_elf_attr_strdup (bfd *abfd, const char *s)
{
return elf_attr_strdup (abfd, s, NULL);
}
/* Add a string object attribute. */
static obj_attribute *
elf_add_obj_attr_string (bfd *abfd, int vendor, unsigned int tag,
const char *s, const char *end)
{
obj_attribute *attr;
attr = elf_new_obj_attr (abfd, vendor, tag);
if (attr != NULL)
{
attr->type = bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
attr->s = elf_attr_strdup (abfd, s, end);
if (attr->s == NULL)
return NULL;
}
return attr;
}
obj_attribute *
bfd_elf_add_obj_attr_string (bfd *abfd, int vendor, unsigned int tag,
const char *s)
{
return elf_add_obj_attr_string (abfd, vendor, tag, s, NULL);
}
/* Add a int+string object attribute. */
static obj_attribute *
elf_add_obj_attr_int_string (bfd *abfd, int vendor, unsigned int tag,
unsigned int i, const char *s, const char *end)
{
obj_attribute *attr;
attr = elf_new_obj_attr (abfd, vendor, tag);
if (attr != NULL)
{
attr->type = bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
attr->i = i;
attr->s = elf_attr_strdup (abfd, s, end);
if (attr->s == NULL)
return NULL;
}
return attr;
}
obj_attribute *
bfd_elf_add_obj_attr_int_string (bfd *abfd, int vendor, unsigned int tag,
unsigned int i, const char *s)
{
return elf_add_obj_attr_int_string (abfd, vendor, tag, i, s, NULL);
}
/* Copy the object attributes from IBFD to OBFD. */
void
_bfd_elf_copy_obj_attributes (bfd *ibfd, bfd *obfd)
{
obj_attribute *in_attr;
obj_attribute *out_attr;
obj_attribute_list *list;
int i;
int vendor;
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return;
for (vendor = OBJ_ATTR_FIRST; vendor <= OBJ_ATTR_LAST; vendor++)
{
in_attr
= &elf_known_obj_attributes (ibfd)[vendor][LEAST_KNOWN_OBJ_ATTRIBUTE];
out_attr
= &elf_known_obj_attributes (obfd)[vendor][LEAST_KNOWN_OBJ_ATTRIBUTE];
for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
{
out_attr->type = in_attr->type;
out_attr->i = in_attr->i;
if (in_attr->s && *in_attr->s)
{
out_attr->s = _bfd_elf_attr_strdup (obfd, in_attr->s);
if (out_attr->s == NULL)
bfd_perror (_("error adding attribute"));
}
in_attr++;
out_attr++;
}
for (list = elf_other_obj_attributes (ibfd)[vendor];
list;
list = list->next)
{
bool ok = false;
in_attr = &list->attr;
switch (in_attr->type & (ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_STR_VAL))
{
case ATTR_TYPE_FLAG_INT_VAL:
ok = bfd_elf_add_obj_attr_int (obfd, vendor,
list->tag, in_attr->i);
break;
case ATTR_TYPE_FLAG_STR_VAL:
ok = bfd_elf_add_obj_attr_string (obfd, vendor, list->tag,
in_attr->s);
break;
case ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_STR_VAL:
ok = bfd_elf_add_obj_attr_int_string (obfd, vendor, list->tag,
in_attr->i, in_attr->s);
break;
default:
abort ();
}
if (!ok)
bfd_perror (_("error adding attribute"));
}
}
}
/* Determine whether a GNU object attribute tag takes an integer, a
string or both. */
static int
gnu_obj_attrs_arg_type (unsigned int tag)
{
/* Except for Tag_compatibility, for GNU attributes we follow the
same rule ARM ones > 32 follow: odd-numbered tags take strings
and even-numbered tags take integers. In addition, tag & 2 is
nonzero for architecture-independent tags and zero for
architecture-dependent ones. */
if (tag == Tag_compatibility)
return 3;
else
return (tag & 1) != 0 ? 2 : 1;
}
/* Determine what arguments an attribute tag takes. */
int
bfd_elf_obj_attrs_arg_type (bfd *abfd, int vendor, unsigned int tag)
{
switch (vendor)
{
case OBJ_ATTR_PROC:
return get_elf_backend_data (abfd)->obj_attrs_arg_type (tag);
break;
case OBJ_ATTR_GNU:
return gnu_obj_attrs_arg_type (tag);
break;
default:
abort ();
}
}
static void
bfd_elf_parse_attr_section_v1 (bfd *abfd, bfd_byte *p, bfd_byte *p_end)
{
const char *std_sec = get_elf_backend_data (abfd)->obj_attrs_vendor;
while (p_end - p >= 4)
{
size_t len = p_end - p;
size_t namelen;
size_t section_len;
int vendor;
section_len = bfd_get_32 (abfd, p);
p += 4;
if (section_len == 0)
break;
if (section_len > len)
section_len = len;
if (section_len <= 4)
{
_bfd_error_handler
(_("%pB: error: attribute section length too small: %ld"),
abfd, (long) section_len);
break;
}
section_len -= 4;
namelen = strnlen ((char *) p, section_len) + 1;
if (namelen >= section_len)
break;
if (std_sec && strcmp ((char *) p, std_sec) == 0)
vendor = OBJ_ATTR_PROC;
else if (strcmp ((char *) p, "gnu") == 0)
vendor = OBJ_ATTR_GNU;
else
{
/* Other vendor section. Ignore it. */
p += section_len;
continue;
}
p += namelen;
section_len -= namelen;
while (section_len > 0)
{
unsigned int tag;
unsigned int val;
size_t subsection_len;
bfd_byte *end, *orig_p;
orig_p = p;
tag = _bfd_safe_read_leb128 (abfd, &p, false, p_end);
if (p_end - p >= 4)
{
subsection_len = bfd_get_32 (abfd, p);
p += 4;
}
else
{
p = p_end;
break;
}
if (subsection_len > section_len)
subsection_len = section_len;
section_len -= subsection_len;
end = orig_p + subsection_len;
if (end < p)
break;
switch (tag)
{
case Tag_File:
while (p < end)
{
int type;
bool ok = false;
tag = _bfd_safe_read_leb128 (abfd, &p, false, end);
type = bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
switch (type & (ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_STR_VAL))
{
case ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_STR_VAL:
val = _bfd_safe_read_leb128 (abfd, &p, false, end);
ok = elf_add_obj_attr_int_string (abfd, vendor, tag,
val, (char *) p,
(char *) end);
p += strnlen ((char *) p, end - p);
if (p < end)
p++;
break;
case ATTR_TYPE_FLAG_STR_VAL:
ok = elf_add_obj_attr_string (abfd, vendor, tag,
(char *) p,
(char *) end);
p += strnlen ((char *) p, end - p);
if (p < end)
p++;
break;
case ATTR_TYPE_FLAG_INT_VAL:
val = _bfd_safe_read_leb128 (abfd, &p, false, end);
ok = bfd_elf_add_obj_attr_int (abfd, vendor, tag, val);
break;
default:
abort ();
}
if (!ok)
bfd_perror (_("error adding attribute"));
}
break;
case Tag_Section:
case Tag_Symbol:
/* Don't have anywhere convenient to attach these.
Fall through for now. */
default:
/* Ignore things we don't know about. */
p = end;
break;
}
}
}
}
/* Parse an object attributes section. */
void
_bfd_elf_parse_attributes (bfd *abfd, Elf_Internal_Shdr * hdr)
{
/* PR 17512: file: 2844a11d. */
if (hdr->sh_size == 0)
return;
ufile_ptr filesize = bfd_get_file_size (abfd);
if (filesize != 0 && hdr->sh_size > filesize)
{
_bfd_error_handler (_("%pB: error: attribute section '%pA' too big: %#llx"),
abfd, hdr->bfd_section, (long long) hdr->sh_size);
bfd_set_error (bfd_error_invalid_operation);
return;
}
bfd_byte *data = (bfd_byte *) bfd_malloc (hdr->sh_size);
if (!data)
return;
if (!bfd_get_section_contents (abfd, hdr->bfd_section, data, 0, hdr->sh_size))
goto free_data;
unsigned char *cursor = data;
/* The first character is the version of the attributes.
Currently only version 'A' is recognised here. */
if (*cursor != 'A')
{
_bfd_error_handler (_("%pB: error: unknown attributes version '%c'(%d) "
"- expecting 'A'\n"), abfd, *cursor, *cursor);
bfd_set_error (bfd_error_wrong_format);
goto free_data;
}
++cursor;
bfd_elf_parse_attr_section_v1 (abfd, cursor, data + hdr->sh_size);
free_data:
free (data);
}
/* Merge common object attributes from IBFD into OBFD. Raise an error
if there are conflicting attributes. Any processor-specific
attributes have already been merged. This must be called from the
bfd_elfNN_bfd_merge_private_bfd_data hook for each individual
target, along with any target-specific merging. Because there are
no common attributes other than Tag_compatibility at present, and
non-"gnu" Tag_compatibility is not expected in "gnu" sections, this
is not presently called for targets without their own
attributes. */
bool
_bfd_elf_merge_object_attributes (bfd *ibfd, struct bfd_link_info *info)
{
bfd *obfd = info->output_bfd;
obj_attribute *in_attr;
obj_attribute *out_attr;
int vendor;
/* The only common attribute is currently Tag_compatibility,
accepted in both processor and "gnu" sections. */
for (vendor = OBJ_ATTR_FIRST; vendor <= OBJ_ATTR_LAST; vendor++)
{
/* Handle Tag_compatibility. The tags are only compatible if the flags
are identical and, if the flags are '1', the strings are identical.
If the flags are non-zero, then we can only use the string "gnu". */
in_attr = &elf_known_obj_attributes (ibfd)[vendor][Tag_compatibility];
out_attr = &elf_known_obj_attributes (obfd)[vendor][Tag_compatibility];
if (in_attr->i > 0 && strcmp (in_attr->s, "gnu") != 0)
{
_bfd_error_handler
/* xgettext:c-format */
(_("error: %pB: object has vendor-specific contents that "
"must be processed by the '%s' toolchain"),
ibfd, in_attr->s);
return false;
}
if (in_attr->i != out_attr->i
|| (in_attr->i != 0 && strcmp (in_attr->s, out_attr->s) != 0))
{
/* xgettext:c-format */
_bfd_error_handler (_("error: %pB: object tag '%d, %s' is "
"incompatible with tag '%d, %s'"),
ibfd,
in_attr->i, in_attr->s ? in_attr->s : "",
out_attr->i, out_attr->s ? out_attr->s : "");
return false;
}
}
return true;
}
/* Merge an unknown processor-specific attribute TAG, within the range
of known attributes, from IBFD into OBFD; return TRUE if the link
is OK, FALSE if it must fail. */
bool
_bfd_elf_merge_unknown_attribute_low (bfd *ibfd, bfd *obfd, int tag)
{
obj_attribute *in_attr;
obj_attribute *out_attr;
bfd *err_bfd = NULL;
bool result = true;
in_attr = elf_known_obj_attributes_proc (ibfd);
out_attr = elf_known_obj_attributes_proc (obfd);
if (out_attr[tag].i != 0 || out_attr[tag].s != NULL)
err_bfd = obfd;
else if (in_attr[tag].i != 0 || in_attr[tag].s != NULL)
err_bfd = ibfd;
if (err_bfd != NULL)
result
= get_elf_backend_data (err_bfd)->obj_attrs_handle_unknown (err_bfd, tag);
/* Only pass on attributes that match in both inputs. */
if (in_attr[tag].i != out_attr[tag].i
|| (in_attr[tag].s == NULL) != (out_attr[tag].s == NULL)
|| (in_attr[tag].s != NULL && out_attr[tag].s != NULL
&& strcmp (in_attr[tag].s, out_attr[tag].s) != 0))
{
out_attr[tag].i = 0;
out_attr[tag].s = NULL;
}
return result;
}
/* Merge the lists of unknown processor-specific attributes, outside
the known range, from IBFD into OBFD; return TRUE if the link is
OK, FALSE if it must fail. */
bool
_bfd_elf_merge_unknown_attribute_list (bfd *ibfd, bfd *obfd)
{
obj_attribute_list *in_list;
obj_attribute_list *out_list;
obj_attribute_list **out_listp;
bool result = true;
in_list = elf_other_obj_attributes_proc (ibfd);
out_listp = &elf_other_obj_attributes_proc (obfd);
out_list = *out_listp;
for (; in_list || out_list; )
{
bfd *err_bfd = NULL;
unsigned int err_tag = 0;
/* The tags for each list are in numerical order. */
/* If the tags are equal, then merge. */
if (out_list && (!in_list || in_list->tag > out_list->tag))
{
/* This attribute only exists in obfd. We can't merge, and we don't
know what the tag means, so delete it. */
err_bfd = obfd;
err_tag = out_list->tag;
*out_listp = out_list->next;
out_list = *out_listp;
}
else if (in_list && (!out_list || in_list->tag < out_list->tag))
{
/* This attribute only exists in ibfd. We can't merge, and we don't
know what the tag means, so ignore it. */
err_bfd = ibfd;
err_tag = in_list->tag;
in_list = in_list->next;
}
else /* The tags are equal. */
{
/* As present, all attributes in the list are unknown, and
therefore can't be merged meaningfully. */
err_bfd = obfd;
err_tag = out_list->tag;
/* Only pass on attributes that match in both inputs. */
if (in_list->attr.i != out_list->attr.i
|| (in_list->attr.s == NULL) != (out_list->attr.s == NULL)
|| (in_list->attr.s && out_list->attr.s
&& strcmp (in_list->attr.s, out_list->attr.s) != 0))
{
/* No match. Delete the attribute. */
*out_listp = out_list->next;
out_list = *out_listp;
}
else
{
/* Matched. Keep the attribute and move to the next. */
out_list = out_list->next;
in_list = in_list->next;
}
}
if (err_bfd)
result = result
&& get_elf_backend_data (err_bfd)->obj_attrs_handle_unknown (err_bfd,
err_tag);
}
return result;
}
bool _bfd_elf_write_section_build_attributes (bfd *abfd,
struct bfd_link_info *info ATTRIBUTE_UNUSED)
{
asection *sec = elf_obj_build_attributes (abfd);
if (sec == NULL)
return true;
bfd_byte *contents = (bfd_byte *) bfd_malloc (sec->size);
if (contents == NULL)
return false; /* Bail out and fail. */
bfd_elf_set_obj_attr_contents (abfd, contents, sec->size);
bfd_set_section_contents (abfd, sec, contents, 0, sec->size);
free (contents);
return true;
}