aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf-properties.c
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2017-04-03 08:03:14 -0700
committerH.J. Lu <hjl.tools@gmail.com>2017-04-03 08:08:27 -0700
commit46bed6796d5821832e8ba373ddb2e7fdc45a109d (patch)
tree0c7b181ff39eb800307fc0f7f2dc81d35ecb2aac /bfd/elf-properties.c
parent82156ab704b08b124d319c0decdbd48b3ca2dac5 (diff)
downloadgdb-46bed6796d5821832e8ba373ddb2e7fdc45a109d.zip
gdb-46bed6796d5821832e8ba373ddb2e7fdc45a109d.tar.gz
gdb-46bed6796d5821832e8ba373ddb2e7fdc45a109d.tar.bz2
ld: Support ELF GNU program properties
From .note.gnu.property section in each ELF input, we build a list of GNU properties if .note.gnu.property section isn't corrupt. The unknown properties are ignored. All property lists in relocatable inputs are merged into an output property list. When -z stack-size=N is used and N isn't 0, the GNU_PROPERTY_STACK_SIZE property will be merged with or added to the output property list. .note.gnu.property section is generated in output from the output property list. bfd/ * Makefile.am (BFD32_BACKENDS): Add elf-properties.lo. (BFD32_BACKENDS_CFILES): Add elf-properties.c. * configure.ac (elf): Add elf-properties.lo. * Makefile.in: Regenerated. * configure: Likewise. * elf-bfd.h (elf_property_kind): New. (elf_property): Likewise. (elf_property_list): Likewise. (elf_properties): Likewise. (_bfd_elf_parse_gnu_properties): Likewise. (_bfd_elf_get_property): Likewise. (_bfd_elf_link_setup_gnu_properties): Likewise. (elf_backend_data): Add parse_gnu_properties, merge_gnu_properties and setup_gnu_properties. (elf_obj_tdata): Add properties. * elf-properties.c: New file. * elf32-i386.c (elf_i386_parse_gnu_properties): New. (elf_i386_merge_gnu_properties): Likewise. (elf_backend_parse_gnu_properties): Likewise. (elf_backend_merge_gnu_properties): Likewise. * elf64-x86-64.c (elf_x86_64_parse_gnu_properties): Likewise. (elf_x86_64_merge_gnu_properties): Likewise. (elf_backend_parse_gnu_properties): Likewise. (elf_backend_merge_gnu_properties): Likewise. * elfxx-target.h (elf_backend_merge_gnu_properties): Likewise. (elf_backend_parse_gnu_properties): Likewise. (elf_backend_setup_gnu_properties): Likewise. (elfNN_bed): Add elf_backend_parse_gnu_properties, elf_backend_merge_gnu_properties and elf_backend_setup_gnu_properties. ld/ * ld/NEWS: Mention support for ELF GNU program properties. * emultempl/elf32.em (gld${EMULATION_NAME}_after_open): Call ELF setup_gnu_properties. * testsuite/ld-i386/i386.exp: Run property tests for Linux/i386. * testsuite/ld-i386/pass.c: New file. * testsuite/ld-i386/property-1.r: Likewise. * testsuite/ld-i386/property-2.r: Likewise. * testsuite/ld-i386/property-3.r: Likewise. * testsuite/ld-i386/property-4.r: Likewise. * testsuite/ld-i386/property-5.r: Likewise. * testsuite/ld-i386/property-6.r: Likewise. * testsuite/ld-i386/property-6a.c: Likewise. * testsuite/ld-i386/property-6b.c: Likewise. * testsuite/ld-i386/property-6c.S: Likewise. * testsuite/ld-i386/property-7.r: Likewise. * testsuite/ld-i386/property-no-copy.S: Likewise. * testsuite/ld-i386/property-stack.S: Likewise. * testsuite/ld-i386/property-unsorted-1.S: Likewise. * testsuite/ld-i386/property-unsorted-2.S: Likewise. * testsuite/ld-i386/property-x86-1.S: Likewise. * testsuite/ld-i386/property-x86-2.S: Likewise. * testsuite/ld-x86-64/pass.c: Likewise. * testsuite/ld-x86-64/property-1.r: Likewise. * testsuite/ld-x86-64/property-2.r: Likewise. * testsuite/ld-x86-64/property-3.r: Likewise. * testsuite/ld-x86-64/property-4.r: Likewise. * testsuite/ld-x86-64/property-5.r: Likewise. * testsuite/ld-x86-64/property-6.r: Likewise. * testsuite/ld-x86-64/property-6a.c: Likewise. * testsuite/ld-x86-64/property-6b.c: Likewise. * testsuite/ld-x86-64/property-6c.S: Likewise. * testsuite/ld-x86-64/property-7.r: Likewise. * testsuite/ld-x86-64/property-no-copy.S: Likewise. * testsuite/ld-x86-64/property-stack.S: Likewise. * testsuite/ld-x86-64/property-unsorted-1.S: Likewise. * testsuite/ld-x86-64/property-unsorted-2.S: Likewise. * testsuite/ld-x86-64/property-x86-1.S: Likewise. * testsuite/ld-x86-64/property-x86-2.S: Likewise. * testsuite/ld-x86-64/x86-64.exp: Run property tests for Linux/x86-64.
Diffstat (limited to 'bfd/elf-properties.c')
-rw-r--r--bfd/elf-properties.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/bfd/elf-properties.c b/bfd/elf-properties.c
new file mode 100644
index 0000000..a0456f8
--- /dev/null
+++ b/bfd/elf-properties.c
@@ -0,0 +1,483 @@
+/* ELF program property support.
+ Copyright (C) 2017 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 (_("%B: 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: %B: corrupt GNU_PROPERTY_TYPE (%ld) size: %#lx\n"),
+ abfd, note->type, note->descsz);
+ return FALSE;
+ }
+
+ while (1)
+ {
+ unsigned int type = bfd_h_get_32 (abfd, ptr);
+ unsigned int datasz = bfd_h_get_32 (abfd, ptr + 4);
+ elf_property *prop;
+
+ ptr += 8;
+
+ if ((ptr + datasz) > ptr_end)
+ {
+ _bfd_error_handler
+ (_("warning: %B: corrupt GNU_PROPERTY_TYPE (%ld) type (0x%x) datasz: 0x%x\n"),
+ abfd, note->type, type, datasz);
+ /* Clear all properties. */
+ elf_properties (abfd) = NULL;
+ return FALSE;
+ }
+
+ if (type >= GNU_PROPERTY_LOPROC)
+ {
+ 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: %B: corrupt stack size: 0x%x\n"),
+ 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: %B: corrupt no copy on protected size: 0x%x\n"),
+ abfd, datasz);
+ /* Clear all properties. */
+ elf_properties (abfd) = NULL;
+ return FALSE;
+ }
+ prop = _bfd_elf_get_property (abfd, type, datasz);
+ prop->pr_kind = property_number;
+ goto next;
+
+ default:
+ break;
+ }
+ }
+
+ _bfd_error_handler
+ (_("warning: %B: unsupported GNU_PROPERTY_TYPE (%ld) type: 0x%x\n"),
+ abfd, note->type, type);
+
+next:
+ ptr += (datasz + (align_size - 1)) & ~ (align_size - 1);
+ if (ptr == ptr_end)
+ break;
+
+ if (ptr > (ptr_end - 8))
+ goto bad_size;
+ }
+
+ 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 (bfd *abfd, 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 (abfd, 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)
+{
+ elf_property_list *list;
+
+ for (list = *listp; list; list = list->next)
+ {
+ if (type == list->property.pr_type)
+ {
+ /* Remove this property. */
+ *listp = list->next;
+ return &list->property;
+ }
+ else if (type < list->property.pr_type)
+ break;
+ listp = &list->next;
+ }
+
+ return NULL;
+}
+
+/* Merge GNU property list *LISTP with ABFD. */
+
+static void
+elf_merge_gnu_property_list (bfd *abfd, elf_property_list **listp)
+{
+ elf_property_list *p, **lastp;
+ elf_property *pr;
+
+ /* Merge each GNU property in ABFD with the one on *LISTP. */
+ lastp = &elf_properties (abfd);
+ for (p = *lastp; p; p = p->next)
+ {
+ pr = elf_find_and_remove_property (listp, p->property.pr_type);
+ /* Pass NULL to elf_merge_gnu_properties for the property which
+ isn't on *LISTP. */
+ elf_merge_gnu_properties (abfd, &p->property, pr);
+ if (p->property.pr_kind == property_remove)
+ {
+ /* Remove this property. */
+ *lastp = p->next;
+ continue;
+ }
+ lastp = &p->next;
+ }
+
+ /* Merge the remaining properties on *LISTP with ABFD. */
+ for (p = *listp; p != NULL; p = p->next)
+ if (elf_merge_gnu_properties (abfd, NULL, &p->property))
+ {
+ pr = _bfd_elf_get_property (abfd, 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;
+ }
+}
+
+/* Set up GNU properties. */
+
+void
+_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
+ && bfd_count_sections (abfd) != 0
+ && elf_properties (abfd) != NULL)
+ {
+ has_properties = TRUE;
+
+ /* Ignore GNU properties from ELF objects with different machine
+ code or class. */
+ if ((elf_machine_code
+ == get_elf_backend_data (abfd)->elf_machine_code)
+ && (elfclass
+ == get_elf_backend_data (abfd)->s->elfclass))
+ {
+ /* 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;
+
+ /* Merge .note.gnu.property sections. */
+ for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
+ if (abfd != first_pbfd && bfd_count_sections (abfd) != 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 (first_pbfd, listp);
+
+ if (list != NULL)
+ {
+ /* Discard .note.gnu.property section in the rest inputs. */
+ sec = bfd_get_section_by_name (abfd,
+ NOTE_GNU_PROPERTY_SECTION_NAME);
+ 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)
+ {
+ unsigned int size;
+ unsigned int descsz;
+ bfd_byte *contents;
+ Elf_External_Note *e_note;
+ unsigned int align_size = bed->s->elfclass == ELFCLASS64 ? 8 : 4;
+
+ sec = bfd_get_section_by_name (first_pbfd,
+ NOTE_GNU_PROPERTY_SECTION_NAME);
+
+ /* 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;
+ }
+
+ /* Compute the section size. */
+ descsz = offsetof (Elf_External_Note, name[sizeof "GNU"]);
+ descsz = (descsz + 3) & -(unsigned int) 4;
+ size = descsz;
+ for (list = elf_properties (first_pbfd);
+ list != NULL;
+ list = list->next)
+ {
+ /* There are 4 byte type + 4 byte datasz for each property. */
+ size += 4 + 4 + list->property.pr_datasz;
+ /* Align each property. */
+ size = (size + (align_size - 1)) & ~(align_size - 1);
+ }
+
+ /* Update .note.gnu.property section now. */
+ sec->size = size;
+ contents = (bfd_byte *) bfd_zalloc (first_pbfd, size);
+
+ e_note = (Elf_External_Note *) contents;
+ bfd_h_put_32 (first_pbfd, sizeof "GNU", &e_note->namesz);
+ bfd_h_put_32 (first_pbfd, size - descsz, &e_note->descsz);
+ bfd_h_put_32 (first_pbfd, NT_GNU_PROPERTY_TYPE_0, &e_note->type);
+ memcpy (e_note->name, "GNU", sizeof "GNU");
+
+ size = descsz;
+ for (list = elf_properties (first_pbfd);
+ list != NULL;
+ list = list->next)
+ {
+ /* There are 4 byte type + 4 byte datasz for each property. */
+ bfd_h_put_32 (first_pbfd, list->property.pr_type,
+ contents + size);
+ bfd_h_put_32 (first_pbfd, list->property.pr_datasz,
+ contents + size + 4);
+ size += 4 + 4;
+
+ /* Write out property value. */
+ switch (list->property.pr_kind)
+ {
+ case property_number:
+ switch (list->property.pr_datasz)
+ {
+ default:
+ /* Never should happen. */
+ abort ();
+
+ case 0:
+ break;
+
+ case 4:
+ bfd_h_put_32 (first_pbfd, list->property.u.number,
+ contents + size);
+ break;
+
+ case 8:
+ bfd_h_put_64 (first_pbfd, list->property.u.number,
+ contents + size);
+ break;
+ }
+ break;
+
+ default:
+ /* Never should happen. */
+ abort ();
+ }
+ size += list->property.pr_datasz;
+
+ /* Align each property. */
+ size = (size + (align_size - 1)) & ~ (align_size - 1);
+ }
+
+ /* Cache the section contents for elf_link_input_bfd. */
+ elf_section_data (sec)->this_hdr.contents = contents;
+ }
+}