From afca762f598d453c563f244cd3777715b1a0cb72 Mon Sep 17 00:00:00 2001 From: Andreas Krebbel Date: Thu, 21 Dec 2017 13:12:03 +0100 Subject: S/390: Improve partial relro support for 64 bit Currently on S/390 the .got.plt always comes first which prevents the GNU_RELRO segment from being extended across the non-plt GOT entries. Just swapping both unfortunately is not that simple since our ABI requires the _GLOBAL_OFFSET_TABLE_ symbol to point to the very beginning of the entire GOT. Of the 3 magic GOT entries the first is accessed via got pointer while second and third are being accessed via DT_PLTGOT. In order to keep them together we make DT_PLTGOT to point to the .got instead of .got.plt. However, this violates an assumption in the dynamic linker prelink undo code about the GOTPLT entries starting at DT_PLTGOT + 3. We got rid of this requirement with a Glibc patch already in version 2.24: https://sourceware.org/ml/libc-alpha/2016-06/msg01302.html So the S/390 relro GOT layout will look like this with this patch: +----------------------------------+ |got[0]: DYNAMIC | <--- _GLOBAL_OFFSET_TABLE_ == DT_PLTGOT .got |got[1]: link_map parm | |got[2]: &_dl_runtime_resolve | +----------------------------------+ | | non-plt GOT entries | | | | +----------------------------------+ | | <--- .gotplt, PLT GOT entries | | | | | | +----------------------------------+ The patch detects the current layout in size_dynamic_section in order to deal also with linker scripts not generated by this ld version. With partial relro enabled we pick a linker script where .got and .got.plt are swapped which then triggers the rest of the logic. ld/ChangeLog: 2018-07-18 Andreas Krebbel * emulparams/elf64_s390.sh: Define GENERATE_RELRO_SCRIPT and SEPARATE_GOTPLT. * testsuite/ld-s390/gotreloc_64-relro-1.dd: New test. * testsuite/ld-s390/gotreloc_64-norelro-1.dd: Renamed from ... * testsuite/ld-s390/gotreloc_64-1.dd: ... this. * testsuite/ld-s390/s390.exp: Split the GOT testcase into two. bfd/ChangeLog: 2018-07-18 Andreas Krebbel * elf-s390-common.c (s390_gotplt_after_got_p): New function. (s390_got_pointer): New function. (s390_got_offset): New function. (s390_gotplt_offset): New function. * elf64-s390.c (allocate_dynrelocs): Adjust comment. (elf_s390_size_dynamic_sections): Move space for magic GOT entries from .got.plt to .got if necessary and pick the right location for _GLOBAL_OFFSET_TABLE_. (elf_s390_relocate_section): Use the wrapper functions from elf-s390-common.c to deal with both possible layouts (either .got or .got.plt first). (elf_s390_finish_dynamic_sections): Likewise. (elf_s390_finish_dynamic_symbol): Make the location of the GOT magic entries conditional. --- bfd/elf-s390-common.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'bfd/elf-s390-common.c') diff --git a/bfd/elf-s390-common.c b/bfd/elf-s390-common.c index b37cb25..032ba27 100644 --- a/bfd/elf-s390-common.c +++ b/bfd/elf-s390-common.c @@ -30,6 +30,87 @@ s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h) return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0; } +/* Return true if .got.plt is supposed to be emitted after .got. */ + +static inline bfd_boolean +s390_gotplt_after_got_p (struct bfd_link_info *info) +{ + struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info); + + if (!htab->elf.sgot || !htab->elf.sgotplt) + return TRUE; + + if (htab->elf.sgot->output_section == htab->elf.sgotplt->output_section) + { + if (htab->elf.sgot->output_offset < htab->elf.sgotplt->output_offset) + return TRUE; + } + else + { + if (htab->elf.sgot->output_section->vma + <= htab->elf.sgotplt->output_section->vma) + return TRUE; + } + return FALSE; +} + +/* Return the value of the _GLOBAL_OFFSET_TABLE_ symbol. */ + +static inline bfd_vma +s390_got_pointer (struct bfd_link_info *info) +{ + struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info); + bfd_vma got_pointer; + + BFD_ASSERT (htab && htab->elf.hgot); + + got_pointer = (htab->elf.hgot->root.u.def.section->output_section->vma + + htab->elf.hgot->root.u.def.section->output_offset); + /* Our ABI requires the GOT pointer to point at the very beginning + of the global offset table. */ + BFD_ASSERT (got_pointer + <= (htab->elf.sgot->output_section->vma + + htab->elf.sgot->output_offset)); + BFD_ASSERT (got_pointer + <= (htab->elf.sgotplt->output_section->vma + + htab->elf.sgotplt->output_offset)); + + return got_pointer; +} + + +/* Return the offset of the .got versus _GLOBAL_OFFSET_TABLE_. */ + +static inline bfd_vma +s390_got_offset (struct bfd_link_info *info) +{ + struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info); + + /* The absolute address of the .got in the target image. */ + bfd_vma got_address = (htab->elf.sgot->output_section->vma + + htab->elf.sgot->output_offset); + + /* GOT offset must not be negative. */ + BFD_ASSERT (s390_got_pointer (info) <= got_address); + return got_address - s390_got_pointer (info); +} + +/* Return the offset of the .got.plt versus _GLOBAL_OFFSET_TABLE_. */ + +static inline bfd_vma +s390_gotplt_offset (struct bfd_link_info *info) +{ + struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info); + + /* The absolute address of the .got.plt in the target image. */ + bfd_vma gotplt_address = (htab->elf.sgotplt->output_section->vma + + htab->elf.sgotplt->output_offset); + + /* GOT offset must not be negative. */ + BFD_ASSERT (s390_got_pointer (info) <= gotplt_address); + return gotplt_address - s390_got_pointer (info); +} + /* Create sections needed by STT_GNU_IFUNC symbol. */ static bfd_boolean -- cgit v1.1