binutils-gdb/bfd/elf-properties.c
Sudakshina Das 4e5391148d [BFD, AArch64, x86] Improve warning for --force-bti
The AArch64 linker option to turn on BTI (--force-bti) warns in case there are
input objects which have a missing GNU NOTE section for BTI. This patch is trying
to improve the warnings that come out.

In order to do so, I propose adding a new argument to elf_merge_gnu_properties
and the backend function merge_gnu_properties. This new argument makes sure
that we now pass both the objects along with the properties to which they
belong to. The x86 backend function has also been updated to match this
change.

*** bfd/ChangeLog ***

2019-03-21  Sudakshina Das  <sudi.das@arm.com>

	* elf-bfd.h (struct elf_backend_data): Add argument to
	merge_gnu_properties.
	* elf-properties.c (elf_merge_gnu_properties): Add argument to
	itself and while calling bed->merge_gnu_properties.
	(elf_merge_gnu_property_list): Update the calls for
	elf_merge_gnu_properties.
	* elfnn-aarch64.c (elfNN_aarch64_merge_gnu_properties): Update handling
	of --force-bti warning and add argument.
	* elfxx-aarch64.c (_bfd_aarch64_elf_link_setup_gnu_properties): Add
	warning.
	* elfxx-x86.c (_bfd_x86_elf_merge_gnu_properties): Add argument.
	* elfxx-x86.h (_bfd_x86_elf_merge_gnu_properties): Likewise in
	declaration.

*** ld/ChangeLog ***

2019-03-21  Sudakshina Das  <sudi.das@arm.com>

	* testsuite/ld-aarch64/aarch64-elf.exp: Add new test.
	* testsuite/ld-aarch64/bti-plt-1.s: Add .ifdef for PAC note section.
	* testsuite/ld-aarch64/bti-plt-6.d: Update warning.
	* testsuite/ld-aarch64/bti-plt-7.d: Likewise.
	* testsuite/ld-aarch64/bti-warn.d: New test.
2019-03-21 16:20:21 +00:00

721 lines
19 KiB
C

/* ELF program property support.
Copyright (C) 2017-2019 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. */
/* GNU program property draft is at:
https://github.com/hjl-tools/linux-abi/wiki/property-draft.pdf
*/
#include "sysdep.h"
#include "bfd.h"
#include "libbfd.h"
#include "elf-bfd.h"
/* Get a property, allocate a new one if needed. */
elf_property *
_bfd_elf_get_property (bfd *abfd, unsigned int type, unsigned int datasz)
{
elf_property_list *p, **lastp;
if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
{
/* Never should happen. */
abort ();
}
/* Keep the property list in order of type. */
lastp = &elf_properties (abfd);
for (p = *lastp; p; p = p->next)
{
/* Reuse the existing entry. */
if (type == p->property.pr_type)
{
if (datasz > p->property.pr_datasz)
{
/* This can happen when mixing 32-bit and 64-bit objects. */
p->property.pr_datasz = datasz;
}
return &p->property;
}
else if (type < p->property.pr_type)
break;
lastp = &p->next;
}
p = (elf_property_list *) bfd_alloc (abfd, sizeof (*p));
if (p == NULL)
{
_bfd_error_handler (_("%pB: out of memory in _bfd_elf_get_property"),
abfd);
_exit (EXIT_FAILURE);
}
memset (p, 0, sizeof (*p));
p->property.pr_type = type;
p->property.pr_datasz = datasz;
p->next = *lastp;
*lastp = p;
return &p->property;
}
/* Parse GNU properties. */
bfd_boolean
_bfd_elf_parse_gnu_properties (bfd *abfd, Elf_Internal_Note *note)
{
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
unsigned int align_size = bed->s->elfclass == ELFCLASS64 ? 8 : 4;
bfd_byte *ptr = (bfd_byte *) note->descdata;
bfd_byte *ptr_end = ptr + note->descsz;
if (note->descsz < 8 || (note->descsz % align_size) != 0)
{
bad_size:
_bfd_error_handler
(_("warning: %pB: corrupt GNU_PROPERTY_TYPE (%ld) size: %#lx"),
abfd, note->type, note->descsz);
return FALSE;
}
while (ptr != ptr_end)
{
unsigned int type;
unsigned int datasz;
elf_property *prop;
if ((size_t) (ptr_end - ptr) < 8)
goto bad_size;
type = bfd_h_get_32 (abfd, ptr);
datasz = bfd_h_get_32 (abfd, ptr + 4);
ptr += 8;
if (datasz > (size_t) (ptr_end - ptr))
{
_bfd_error_handler
(_("warning: %pB: corrupt GNU_PROPERTY_TYPE (%ld) type (0x%x) datasz: 0x%x"),
abfd, note->type, type, datasz);
/* Clear all properties. */
elf_properties (abfd) = NULL;
return FALSE;
}
if (type >= GNU_PROPERTY_LOPROC)
{
if (bed->elf_machine_code == EM_NONE)
{
/* Ignore processor-specific properties with generic ELF
target vector. They should be handled by the matching
ELF target vector. */
goto next;
}
else if (type < GNU_PROPERTY_LOUSER
&& bed->parse_gnu_properties)
{
enum elf_property_kind kind
= bed->parse_gnu_properties (abfd, type, ptr, datasz);
if (kind == property_corrupt)
{
/* Clear all properties. */
elf_properties (abfd) = NULL;
return FALSE;
}
else if (kind != property_ignored)
goto next;
}
}
else
{
switch (type)
{
case GNU_PROPERTY_STACK_SIZE:
if (datasz != align_size)
{
_bfd_error_handler
(_("warning: %pB: corrupt stack size: 0x%x"),
abfd, datasz);
/* Clear all properties. */
elf_properties (abfd) = NULL;
return FALSE;
}
prop = _bfd_elf_get_property (abfd, type, datasz);
if (datasz == 8)
prop->u.number = bfd_h_get_64 (abfd, ptr);
else
prop->u.number = bfd_h_get_32 (abfd, ptr);
prop->pr_kind = property_number;
goto next;
case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
if (datasz != 0)
{
_bfd_error_handler
(_("warning: %pB: corrupt no copy on protected size: 0x%x"),
abfd, datasz);
/* Clear all properties. */
elf_properties (abfd) = NULL;
return FALSE;
}
prop = _bfd_elf_get_property (abfd, type, datasz);
elf_has_no_copy_on_protected (abfd) = TRUE;
prop->pr_kind = property_number;
goto next;
default:
break;
}
}
_bfd_error_handler
(_("warning: %pB: unsupported GNU_PROPERTY_TYPE (%ld) type: 0x%x"),
abfd, note->type, type);
next:
ptr += (datasz + (align_size - 1)) & ~ (align_size - 1);
}
return TRUE;
}
/* Merge GNU property BPROP with APROP. If APROP isn't NULL, return TRUE
if APROP is updated. Otherwise, return TRUE if BPROP should be merged
with ABFD. */
static bfd_boolean
elf_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, bfd *bbfd,
elf_property *aprop, elf_property *bprop)
{
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
unsigned int pr_type = aprop != NULL ? aprop->pr_type : bprop->pr_type;
if (bed->merge_gnu_properties != NULL
&& pr_type >= GNU_PROPERTY_LOPROC
&& pr_type < GNU_PROPERTY_LOUSER)
return bed->merge_gnu_properties (info, abfd, bbfd, aprop, bprop);
switch (pr_type)
{
case GNU_PROPERTY_STACK_SIZE:
if (aprop != NULL && bprop != NULL)
{
if (bprop->u.number > aprop->u.number)
{
aprop->u.number = bprop->u.number;
return TRUE;
}
break;
}
/* FALLTHROUGH */
case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
/* Return TRUE if APROP is NULL to indicate that BPROP should
be added to ABFD. */
return aprop == NULL;
default:
/* Never should happen. */
abort ();
}
return FALSE;
}
/* Return the property of TYPE on *LISTP and remove it from *LISTP.
Return NULL if not found. */
static elf_property *
elf_find_and_remove_property (elf_property_list **listp,
unsigned int type, bfd_boolean remove)
{
elf_property_list *list;
for (list = *listp; list; list = list->next)
{
if (type == list->property.pr_type)
{
/* Remove this property. */
if (remove)
*listp = list->next;
return &list->property;
}
else if (type < list->property.pr_type)
break;
listp = &list->next;
}
return NULL;
}
/* Merge GNU property list *LISTP in ABFD with FIRST_PBFD. */
static void
elf_merge_gnu_property_list (struct bfd_link_info *info, bfd *first_pbfd,
bfd *abfd, elf_property_list **listp)
{
elf_property_list *p, **lastp;
elf_property *pr;
bfd_boolean number_p;
bfd_vma number = 0;
/* Merge each GNU property in FIRST_PBFD with the one on *LISTP. */
lastp = &elf_properties (first_pbfd);
for (p = *lastp; p; p = p->next)
if (p->property.pr_kind != property_remove)
{
if (p->property.pr_kind == property_number)
{
number_p = TRUE;
number = p->property.u.number;
}
else
number_p = FALSE;
pr = elf_find_and_remove_property (listp, p->property.pr_type,
TRUE);
/* Pass NULL to elf_merge_gnu_properties for the property which
isn't on *LISTP. */
elf_merge_gnu_properties (info, first_pbfd, abfd, &p->property, pr);
if (p->property.pr_kind == property_remove)
{
if (info->has_map_file)
{
if (number_p)
{
if (pr != NULL)
info->callbacks->minfo
(_("Removed property %W to merge %pB (0x%v) "
"and %pB (0x%v)\n"),
(bfd_vma) p->property.pr_type, first_pbfd,
number, abfd, pr->u.number);
else
info->callbacks->minfo
(_("Removed property %W to merge %pB (0x%v) "
"and %pB (not found)\n"),
(bfd_vma) p->property.pr_type, first_pbfd,
number, abfd);
}
else
{
if (pr != NULL)
info->callbacks->minfo
(_("Removed property %W to merge %pB and %pB\n"),
(bfd_vma) p->property.pr_type, first_pbfd, abfd);
else
info->callbacks->minfo
(_("Removed property %W to merge %pB and %pB "
"(not found)\n"),
(bfd_vma) p->property.pr_type, first_pbfd, abfd);
}
}
else
{
/* Remove this property. */
*lastp = p->next;
continue;
}
}
else if (number_p)
{
if (pr != NULL)
{
if (p->property.u.number != number
|| p->property.u.number != pr->u.number)
info->callbacks->minfo
(_("Updated property %W (0x%v) to merge %pB (0x%v) "
"and %pB (0x%v)\n"),
(bfd_vma) p->property.pr_type, p->property.u.number,
first_pbfd, number, abfd, pr->u.number);
}
else
{
if (p->property.u.number != number)
info->callbacks->minfo
(_("Updated property %W (%v) to merge %pB (0x%v) "
"and %pB (not found)\n"),
(bfd_vma) p->property.pr_type, p->property.u.number,
first_pbfd, number, abfd);
}
}
lastp = &p->next;
}
/* Merge the remaining properties on *LISTP with FIRST_PBFD. */
for (p = *listp; p != NULL; p = p->next)
{
if (p->property.pr_kind == property_number)
{
number_p = TRUE;
number = p->property.u.number;
}
else
number_p = FALSE;
if (elf_merge_gnu_properties (info, first_pbfd, abfd, NULL, &p->property))
{
if (p->property.pr_type == GNU_PROPERTY_NO_COPY_ON_PROTECTED)
elf_has_no_copy_on_protected (first_pbfd) = TRUE;
pr = _bfd_elf_get_property (first_pbfd, p->property.pr_type,
p->property.pr_datasz);
/* It must be a new property. */
if (pr->pr_kind != property_unknown)
abort ();
/* Add a new property. */
*pr = p->property;
}
else
{
pr = elf_find_and_remove_property (&elf_properties (first_pbfd),
p->property.pr_type,
FALSE);
if (pr == NULL)
{
if (number_p)
info->callbacks->minfo
(_("Removed property %W to merge %pB (not found) and "
"%pB (0x%v)\n"),
(bfd_vma) p->property.pr_type, first_pbfd, abfd,
number);
else
info->callbacks->minfo
(_("Removed property %W to merge %pB and %pB\n"),
(bfd_vma) p->property.pr_type, first_pbfd, abfd);
}
else if (pr->pr_kind != property_remove)
abort ();
}
}
}
/* Get GNU property section size. */
static bfd_size_type
elf_get_gnu_property_section_size (elf_property_list *list,
unsigned int align_size)
{
bfd_size_type size;
unsigned int descsz;
/* Compute the output section size. */
descsz = offsetof (Elf_External_Note, name[sizeof "GNU"]);
descsz = (descsz + 3) & -(unsigned int) 4;
size = descsz;
for (; list != NULL; list = list->next)
{
unsigned int datasz;
/* Check if this property should be skipped. */
if (list->property.pr_kind == property_remove)
continue;
/* There are 4 byte type + 4 byte datasz for each property. */
if (list->property.pr_type == GNU_PROPERTY_STACK_SIZE)
datasz = align_size;
else
datasz = list->property.pr_datasz;
size += 4 + 4 + datasz;
/* Align each property. */
size = (size + (align_size - 1)) & ~(align_size - 1);
}
return size;
}
/* Write GNU properties. */
static void
elf_write_gnu_properties (bfd *abfd, bfd_byte *contents,
elf_property_list *list, unsigned int size,
unsigned int align_size)
{
unsigned int descsz;
unsigned int datasz;
Elf_External_Note *e_note;
e_note = (Elf_External_Note *) contents;
descsz = offsetof (Elf_External_Note, name[sizeof "GNU"]);
descsz = (descsz + 3) & -(unsigned int) 4;
bfd_h_put_32 (abfd, sizeof "GNU", &e_note->namesz);
bfd_h_put_32 (abfd, size - descsz, &e_note->descsz);
bfd_h_put_32 (abfd, NT_GNU_PROPERTY_TYPE_0, &e_note->type);
memcpy (e_note->name, "GNU", sizeof "GNU");
size = descsz;
for (; list != NULL; list = list->next)
{
/* Check if this property should be skipped. */
if (list->property.pr_kind == property_remove)
continue;
/* There are 4 byte type + 4 byte datasz for each property. */
if (list->property.pr_type == GNU_PROPERTY_STACK_SIZE)
datasz = align_size;
else
datasz = list->property.pr_datasz;
bfd_h_put_32 (abfd, list->property.pr_type, contents + size);
bfd_h_put_32 (abfd, datasz, contents + size + 4);
size += 4 + 4;
/* Write out property value. */
switch (list->property.pr_kind)
{
case property_number:
switch (datasz)
{
default:
/* Never should happen. */
abort ();
case 0:
break;
case 4:
bfd_h_put_32 (abfd, list->property.u.number,
contents + size);
break;
case 8:
bfd_h_put_64 (abfd, list->property.u.number,
contents + size);
break;
}
break;
default:
/* Never should happen. */
abort ();
}
size += datasz;
/* Align each property. */
size = (size + (align_size - 1)) & ~ (align_size - 1);
}
}
/* Set up GNU properties. Return the first relocatable ELF input with
GNU properties if found. Otherwise, return NULL. */
bfd *
_bfd_elf_link_setup_gnu_properties (struct bfd_link_info *info)
{
bfd *abfd, *first_pbfd = NULL;
elf_property_list *list;
asection *sec;
bfd_boolean has_properties = FALSE;
const struct elf_backend_data *bed
= get_elf_backend_data (info->output_bfd);
unsigned int elfclass = bed->s->elfclass;
int elf_machine_code = bed->elf_machine_code;
/* Find the first relocatable ELF input with GNU properties. */
for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
&& (abfd->flags & DYNAMIC) == 0
&& elf_properties (abfd) != NULL)
{
has_properties = TRUE;
/* Ignore GNU properties from ELF objects with different machine
code or class. Also skip objects without a GNU_PROPERTY note
section. */
if ((elf_machine_code
== get_elf_backend_data (abfd)->elf_machine_code)
&& (elfclass
== get_elf_backend_data (abfd)->s->elfclass)
&& bfd_get_section_by_name (abfd,
NOTE_GNU_PROPERTY_SECTION_NAME) != NULL
)
{
/* Keep .note.gnu.property section in FIRST_PBFD. */
first_pbfd = abfd;
break;
}
}
/* Do nothing if there is no .note.gnu.property section. */
if (!has_properties)
return NULL;
/* Merge .note.gnu.property sections. */
info->callbacks->minfo (_("\n"));
info->callbacks->minfo (_("Merging program properties\n"));
info->callbacks->minfo (_("\n"));
for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
if (abfd != first_pbfd
&& (abfd->flags & (DYNAMIC | BFD_PLUGIN | BFD_LINKER_CREATED)) == 0)
{
elf_property_list *null_ptr = NULL;
elf_property_list **listp = &null_ptr;
/* Merge .note.gnu.property section in relocatable ELF input. */
if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
{
list = elf_properties (abfd);
/* Ignore GNU properties from ELF objects with different
machine code. */
if (list != NULL
&& (elf_machine_code
== get_elf_backend_data (abfd)->elf_machine_code))
listp = &elf_properties (abfd);
}
else
list = NULL;
/* Merge properties with FIRST_PBFD. FIRST_PBFD can be NULL
when all properties are from ELF objects with different
machine code or class. */
if (first_pbfd != NULL)
elf_merge_gnu_property_list (info, first_pbfd, abfd, listp);
if (list != NULL)
{
/* Discard the .note.gnu.property section in this bfd. */
sec = bfd_get_section_by_name (abfd,
NOTE_GNU_PROPERTY_SECTION_NAME);
if (sec != NULL)
sec->output_section = bfd_abs_section_ptr;
}
}
/* Rewrite .note.gnu.property section so that GNU properties are
always sorted by type even if input GNU properties aren't sorted. */
if (first_pbfd != NULL)
{
bfd_size_type size;
bfd_byte *contents;
unsigned int align_size = elfclass == ELFCLASS64 ? 8 : 4;
sec = bfd_get_section_by_name (first_pbfd,
NOTE_GNU_PROPERTY_SECTION_NAME);
BFD_ASSERT (sec != NULL);
/* Update stack size in .note.gnu.property with -z stack-size=N
if N > 0. */
if (info->stacksize > 0)
{
elf_property *p;
bfd_vma stacksize = info->stacksize;
p = _bfd_elf_get_property (first_pbfd, GNU_PROPERTY_STACK_SIZE,
align_size);
if (p->pr_kind == property_unknown)
{
/* Create GNU_PROPERTY_STACK_SIZE. */
p->u.number = stacksize;
p->pr_kind = property_number;
}
else if (stacksize > p->u.number)
p->u.number = stacksize;
}
else if (elf_properties (first_pbfd) == NULL)
{
/* Discard .note.gnu.property section if all properties have
been removed. */
sec->output_section = bfd_abs_section_ptr;
return NULL;
}
/* Fix up GNU properties. */
if (bed->fixup_gnu_properties)
bed->fixup_gnu_properties (info, &elf_properties (first_pbfd));
if (elf_properties (first_pbfd) == NULL)
{
/* Discard .note.gnu.property section if all properties have
been removed. */
sec->output_section = bfd_abs_section_ptr;
return NULL;
}
/* Compute the section size. */
list = elf_properties (first_pbfd);
size = elf_get_gnu_property_section_size (list, align_size);
/* Update .note.gnu.property section now. */
sec->size = size;
contents = (bfd_byte *) bfd_zalloc (first_pbfd, size);
elf_write_gnu_properties (first_pbfd, contents, list, size,
align_size);
/* Cache the section contents for elf_link_input_bfd. */
elf_section_data (sec)->this_hdr.contents = contents;
/* If GNU_PROPERTY_NO_COPY_ON_PROTECTED is set, protected data
symbol is defined in the shared object. */
if (elf_has_no_copy_on_protected (first_pbfd))
info->extern_protected_data = FALSE;
}
return first_pbfd;
}
/* Convert GNU property size. */
bfd_size_type
_bfd_elf_convert_gnu_property_size (bfd *ibfd, bfd *obfd)
{
unsigned int align_size;
const struct elf_backend_data *bed;
elf_property_list *list = elf_properties (ibfd);
bed = get_elf_backend_data (obfd);
align_size = bed->s->elfclass == ELFCLASS64 ? 8 : 4;
/* Get the output .note.gnu.property section size. */
return elf_get_gnu_property_section_size (list, align_size);
}
/* Convert GNU properties. */
bfd_boolean
_bfd_elf_convert_gnu_properties (bfd *ibfd, asection *isec,
bfd *obfd, bfd_byte **ptr,
bfd_size_type *ptr_size)
{
unsigned int size;
bfd_byte *contents;
unsigned int align_shift;
const struct elf_backend_data *bed;
elf_property_list *list = elf_properties (ibfd);
bed = get_elf_backend_data (obfd);
align_shift = bed->s->elfclass == ELFCLASS64 ? 3 : 2;
/* Get the output .note.gnu.property section size. */
size = bfd_get_section_size (isec->output_section);
/* Update the output .note.gnu.property section alignment. */
bfd_set_section_alignment (obfd, isec->output_section, align_shift);
if (size > bfd_get_section_size (isec))
{
contents = (bfd_byte *) bfd_malloc (size);
free (*ptr);
*ptr = contents;
}
else
contents = *ptr;
*ptr_size = size;
/* Generate the output .note.gnu.property section. */
elf_write_gnu_properties (ibfd, contents, list, size, 1 << align_shift);
return TRUE;
}