]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commit - bfd/elfnn-aarch64.c
Various fixes for capability IFUNCs
authorMatthew Malcomson <matthew.malcomson@arm.com>
Thu, 13 Oct 2022 10:19:47 +0000 (11:19 +0100)
committerMatthew Malcomson <matthew.malcomson@arm.com>
Thu, 13 Oct 2022 10:22:52 +0000 (11:22 +0100)
commit36b6002396dc0898b6f7097bca4589fefabaac87
tree5d974f4d138a68fa74d9875bc89bb8ddcdecdef1
parent8504495ada45975d42a5ee1e5d20a19161767461
Various fixes for capability IFUNCs

1) Enable having a CAPINIT relocation against an IFUNC.
   We update the `final_link_relocate` switch case around IFUNC's to
   also handle CAPINIT relocations.  The handling of CAPINIT relocations
   is slightly different than for AARCH64_NN (i.e. ABS64) relocations
   since we generally need to emit a dynamic relocation.

   Handling this relocation also needs to manage the PDE case when a
   hard-coded address has been put into code to satisfy something like
   an `adrp`.  In these cases the canonical address of the IFUNC becomes
   its PLT stub rather than the result of the resolver.  We then need to
   use a RELATIVE relocation rather than an IRELATIVE one.
   N.b. unlike the ABS64 relocation, since a CAPINIT will always emit a
   dynamic relocation we do not require pointer equality adjustments on
   a symbol from having seen a CAPINIT.  That means we do not need to
   request that the PLT stub of an IFUNC is treated as the canonical
   address just from having seen a CAPINIT relocation.

   A CAPINIT relocation against an IFUNC needs to be recorded internally
   so that _bfd_elf_allocate_ifunc_dyn_relocs does not garbage collect
   the PLT stub and associated IRELATIVE relocation.

   See changes in the CAPINIT case of the IFUNC switch of
   elfNN_aarch64_final_link_relocate, and in the CAPINIT case of
   elfNN_aarch64_check_relocs.

2) Ensure that GOT relocations against an IFUNC have their fragment
   populated with the LSB set.

   For GOT relocations against a capability IFUNC we need to introduce a
   relocation for the runtime to provide us with a valid capability.

   See changes in the GOT cases of the IFUNC switch of
   elfNN_aarch64_final_link_relocate, changes in the
   elfNN_aarch64_allocate_ifunc_dynrelocs function, and changes around
   handling an IFUNC GOT entry in elfNN_aarch64_finish_dynamic_symbol.

3) Ensure that mapping symbols are emitted for the .iplt.  Without this
   many of the testcases here are disassembled incorrectly.

   See changes in elfNN_aarch64_output_arch_local_syms.

4) IRELATIVE relocations are against symbols which are not in the
   dynamic symbol table, hence they need their fragment populated to
   inform the dynamic linker the bounds and permissions to call the
   associated resolver with.

   See part of the CAPINIT IFUNC handling in
   elfNN_aarch64_final_link_relocate, and the IRELATIVE handling in
   elfNN_aarch64_create_small_pltn_entry.

5) Disallow an ABS64 relocation against a purecap IFUNC.  Such a
   relocation is expecting a 64-bit value but the function will return a
   capability.  Some handling could be implemented by some communication
   method to the dynamic linker that this particular value should be
   64-bit (maybe by emitting an AARCH64_IRELATIVE relocation rather than
   a MORELLO_IRELATIVE one), but as yet GCC doesn't generate such a
   relocation and we believe it's unlikely to be needed.

   See new error check in AARCH64_NN clause of
   elfNN_aarch64_final_link_relocate.

6) Ensure that for statically linked PDE's, we segregate IRELATIVE and
   RELATIVE relocations.  IRELATIVE relocs should be in the .rela.iplt
   section, while RELATIVE relocs should be in the .rela.dyn section.

   Correspondingly all RELATIVE relocations should be between the
   __rela_dyn_{start,end} symbols, and all IRELATIVE relocations should
   be between the __rela_iplt_{start,end} symbols.

   This segregation is made based on dynamic relocation type rather than
   static relocation that generates it.  The segregation allows the
   static libc to more easily handle relocations.

Update testcases accordingly.
We introduce some new testcases, morello-ifunc.s contains uses of an
IFUNC which has been referenced directly in code.  When compiling a PDE
this triggers the pointer equality requirement and hence the canonical
address for this symbol becomes the PLT stub rather than the result of
the resolver.

morello-ifunc1.s does not use the IFUNC directly in code so that the
address used everywhere is the result of the resolver.

Both of these have testcases assembled and linked for static,
dynamically linked PDE, and PIE.  The testcase without a hard-coded
access also has a testcase for -shared.

morello-ifunc2.s is written to check that a CAPINIT relocation does
indeed stop the garbage collection of an IFUNC's PLT and IRELATIVE
relocation.

morello-ifunc3.s tests that we error on an ABS64 relocation against a
C64 IFUNC.

morello-ifunc-dynlink.s tests that a CAPINIT relocation against an IFUNC
symbol defined in a shared library behaves the same way as one against a
FUNC symbol defined in a shared library.

Implementation note:
When segregating IRELATIVE and RELATIVE relocs the change for
relocations against IFUNC symbols populated in the GOT is
straightforward.

For CAPINIT relocations the change is not as straightforward.  The
problem is that on sight of CAPINIT relocations in check_relocs we
immediately allocate space in the srelcaps section.  In trying to
satisfy the above we need to know whether we're going to be emitting an
IRELATIVE relocation or RELATIVE one in order to know which section it
should go in.  The determining factor between these two kinds of
relocations is whether there is a text relocation to this IFUNC symbol,
since that determines whether we need to make this CAPINIT relocation
a RELATIVE relocation pointing to the PLT stub (in order to satisfy
pointer equality) or an IRELATIVE relocation pointing to the resolver.

Whether such a relocation occurs is recorded against each symbol in the
pointer_equality_needed member.  This can only be known after all
relocations have been seen in check_relocs.  Hence, when coming across a
CAPINIT relocation in check_relocs we do not in general know whether
this CAPINIT relocation should end up as an IRELATIVE or RELATIVE
relocation.

This patch postpones the decision by recording the number of CAPINIT
relocations against a given symbol in a hash table while going through
check_relocs and allocating the relevant space in the required section
in size_dynamic_sections.

N.b. this is similar in purpose to the dyn_relocs linked list on a
symbol.  We do not use that existing member which is on every symbol
since the structure does not allow any indication of what kind of
relocation triggered the need.  Moreover the structure is used for
different purposes throughout the linker and disentangling the new
meaning from the existing ones seems overly confusing.

Overall, the decisions about which sections relocations against an IFUNC
should go in are:
  CAPINIT relocations:
    If this is a static PDE link, and the symbol does not need pointer
    equality handling, then this should emit an IRELATIVE relocation and
    that should go in the .rela.iplt section.

    If this is a PIC link, then this should go in the .rela.ifunc
    section (along with all other dynamic relocations against the IFUNC,
    as commented in _bfd_elf_allocate_ifunc_dyn_relocs).

    Otherwise this relocation should go in the srelcaps section (which
    goes in .rela.dyn).

  GOT relocations:
    If this is a static PDE link, and the symbol does not need pointer
    equality, then this should emit an IRELATIVE relocation into the
    .rela.iplt section.

    If this is a static PDE link, then this should emit a RELATIVE
    relocation and that should go in the srelcaps section (which is in
    .rela.dyn).

    Otherwise this should go in .rela.got section.
27 files changed:
bfd/elfnn-aarch64.c
ld/testsuite/ld-aarch64/aarch64-elf.exp
ld/testsuite/ld-aarch64/c64-ifunc-2-local.d
ld/testsuite/ld-aarch64/c64-ifunc-2.d
ld/testsuite/ld-aarch64/c64-ifunc-3a.d
ld/testsuite/ld-aarch64/morello-ifunc-a.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc-b.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc-dynlink-pie.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc-dynlink.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc-dynlink.s [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc-shared.s [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc.s [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc1-a.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc1-b.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc1-c.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc1.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc1.s [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc2-b.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc2.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc2.s [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc3.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc3.s [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc4.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc4.s [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc4a.d [new file with mode: 0644]
ld/testsuite/ld-aarch64/morello-ifunc4a.s [new file with mode: 0644]