Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions aaelf64/aaelf64.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,8 @@ The ``PAUTH`` and ``ENCD`` operators are defined in `PAUTHABIELF64`_.
+------------+------------+----------------------------------------+--------------------------------------+----------------------+
| 597 | \- | R\_AARCH64\_AUTH\_TLSDESC\_ADD\_LO12 | G(ENCD(GTLSDESC(S))) | See `PAUTHABIELF64`_ |
+------------+------------+----------------------------------------+--------------------------------------+----------------------+
| 598 | \- | R\_AARCH64\_AUTH\_TLSDESC\_CALL | None | See `PAUTHABIELF64`_ |
+------------+------------+----------------------------------------+--------------------------------------+----------------------+

Relocations for Structure Protection Extension
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
135 changes: 135 additions & 0 deletions design-documents/pauthabi-tls.rst

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: GitHub's markdown rendering is not showing code blocks from this file for some reason (at least for me in chromium). Maybe it's worth considering using triple-backtick usual formatting or fixing it somehow else - I guess that many people would read it in GitHub's web interface and would just miss the code blocks as not displayed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK, I'll switch to using code-block:: asm

Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
..
Copyright (c) 2026, Arm Limited and its affiliates. All rights reserved.
CC-BY-SA-4.0 AND Apache-Patent-License
See LICENSE file for details

.. _AAELF64: https://github.com/ARM-software/abi-aa/tree/main/aaelf64/aaelf64.rst
.. _PAUTHABIELF64: https://github.com/ARM-software/abi-aa/tree/main/pauthabielf64/pauthabielf64.rst
.. _SYSVABI64: https://github.com/ARM-software/abi-aa/tree/main/sysvabi64/sysvabi64.rst
.. _TLSDESC: http://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-ARM.txt

TLS in the PAuthABI
*******************

Preamble
========

Background
----------

This document contains additional context for implementing TLS in the
PAuthABI as defined in the ABI extension `PAUTHABIELF64`_. This
document is not normative and `PAUTHABIELF64`_ takes precedence if
there are any discrepancies.

TLS Dialect
-----------

The base ABI in `AAELF64`_ defines relocations for two TLS
dialects. The "traditional" dialect and the "descriptor" dialect. In
the traditional dialect global and local dynamic TLS use the
``R_<CLS>_TLSGD``, ``R_<CLS>_TLSLD`` prefixed relocations. These
create a pair of GOT entries relocated by ``R_<CLS>_TLS_DTPMOD`` and
``R_<CLS>_TLS_DTPREL``. In the "descriptor" dialect, global and local
dynamic TLS use the ``R_<CLS>_TLSDESC`` prefixed relocations. These
create a pair of GOT entries relocated by ``R_<CLS>_TLSDESC``. Local
Exec and Initial Exec TLS are handled the same way in both dialects.

The `PAUTHABIELF64`_ only supports the descriptor based dialect,
primarily because the traditional dialect is now legacy. GCC defaults
to the "descriptor" dialect and clang only supports the "descriptor"
based dialect.

Auth variant TLS relocations
----------------------------

The signing-schema for accessing TLS is platform defined. The
following section is a discussion of the most likely choices.

If the GOT is unsigned, the TLS code-sequences, relocations and
relaxations, from the base ABI can be used, see `AAELF64`_ and
`SYSVABI64`_ for details.

If a GOT entry is signed then AUTH variant static TLS relocations are
required for the static linker to be able use an AUTH variant dynamic
relocation that informs the dynamic linker to sign the GOT entries.

At present there are AUTH variant static and dynamic relocations
defined for TLSDESC, but not for Initial Exec.

Local dynamic TLS does not use the GOT so it can be handled by the
``R_<CLS>_TLSLE`` prefixed relocations from the base ABI defined in
`AAELF64`_.

The choice of which GOT entries to sign is a property of the
signing-schema for the platform. For example a signing-schema may only
sign GOT entries containing code-pointers, which would permit Initial
Exec TLS using the ``R_<CLS>_TLSIE`` prefixed relocations defined in
`AAELF64`_. Alternatively a signing-schema may sign all GOT entries,
which would require AUTH variant static and dynamic relocations to be
defined for Initial Exec.

TLS Relaxation
--------------

The static linker may relax a more general TLS model to a more
constrained model when TLS variables meet the requirements for using
the constrained model and the relaxed sequence is permitted by the
signing-schema of the platform.

The AUTH variant TLSDESC sequence to access TLS variable ``v`` is as
described below:

.. code-block:: asm

adrp x0, :tlsdesc_auth:v // R_AARCH64_AUTH_TLS_ADR_PAGE21
ldr x16, [x0, #:tlsdesc_auth_lo12:v] // R_AARCH64_AUTH_TLSDESC_LD64_LO12
add x0, x0, #:tlsdesc_auth_lo12:v // R_AARCH64_AUTH_TLSDESC_ADD_LO12
.tlsauthdesccall v //R_AARCH64_AUTH_TLSDESC_CALL
blraa x16, x0

There are no AUTH variant TLSIE relocations defined so a relaxation is
only possible to an unsigned GOT entry if permitted by the
signing-schema:

.. code-block:: asm

adrp x0, :gottprel:v // R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 v
ldr x0, [x0, #:gottprel_lo12:v] // R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC v
nop
nop

A theoretical relaxation for a signed GOT is possible when the
architectural feature ``FEAT_FPAC`` is available or a signing-schema
does not require an immediate trap on failure of an AUTH
(``-fno-ptrauth-auth-traps`` in clang).

.. code-block:: asm

adrp x0, :gottprel_auth:v // R_AARCH64_AUTH_TLSIE_ADR_GOTTPREL_PAGE21 v
add x1, x0, #:gottprel_auth_lo12:v // R_AARCH64_AUTH_TLSIE_ADD_LO12 v
ldr x0, [x1]
autia x0, x1

There are no spare instructions to test whether the TLS offset in
``x0`` authenticated successfully.

A relaxation to local-exec is always possible as no GOT entry is used.

.. code-block:: asm

movz x0, :tprel_g1:v // R_AARCH64_TLSLE_MOVW_TPREL_G1 v
movk x0, :tprel_g0:v // R_AARCH64_TLSLE_MOVW_TPREL_G0_NC v
nop
nop

A weak reference that is known at static link time to be undefined can
be relaxed by setting the offset so that it cancels to 0 when added to
the thread pointer.

.. code-block:: asm

mrs x0, tpidr_el0
neg x0, x0
nop
nop
26 changes: 23 additions & 3 deletions pauthabielf64/pauthabielf64.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
.. _CPPABI64: https://github.com/ARM-software/abi-aa/releases
.. _LSB: https://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/noteabitag.html
.. _MEMTAGABI: https://github.com/ARM-software/abi-aa/releases
.. _PAUTHABITLS: https://github.com/ARM-software/abi-aa/tree/main/design-documents
.. _SCO-ELF: http://www.sco.com/developers/gabi/
.. _SYSVABI64: https://github.com/ARM-software/abi-aa/releases
.. _TLSDESC: http://www.fsfla.org/~lxoliva/writeups/TLS/paper-lk2006.pdf
Expand Down Expand Up @@ -251,6 +252,8 @@ changes to the content of the document for that release.
+------------+-----------------------------+------------------------------------------------------------------+
| 2026Q2 | 6\ :sup:`th` January 2026 | - Clarify combination of MemtagABI and PAuthABI relocations |
+------------+-----------------------------+------------------------------------------------------------------+
| 2026Q2 | 14\ :sup:`th` May 2026 | - Add R_AARCH64_AUTH_TLSDESC_CALL relocation |
+------------+-----------------------------+------------------------------------------------------------------+

References
----------
Expand All @@ -274,6 +277,8 @@ This document refers to, or is referred to by, the following documents.
+-----------------------------------------------------------------------------------------+-------------------------------------------------------------+--------------------------------------------------------------------------+
| MEMTAGABI_ | memtagabi64 | Memtag ABI Extension for the Arm 64-bit Architecture |
+-----------------------------------------------------------------------------------------+-------------------------------------------------------------+--------------------------------------------------------------------------+
| PAUTHABITLS_ | | Rationale for and possible relaxations for TLS in a PAuthABI environment |
+-----------------------------------------------------------------------------------------+-------------------------------------------------------------+--------------------------------------------------------------------------+
| SCO-ELF_ | http://www.sco.com/developers/gabi/ | System V Application Binary Interface – DRAFT |
+-----------------------------------------------------------------------------------------+-------------------------------------------------------------+--------------------------------------------------------------------------+
| SYSVABI64_ | sysvabi64 | System V Application Binary Interface (ABI) for the Arm 64-bit |
Expand Down Expand Up @@ -469,7 +474,9 @@ General Restrictions
way to avoid COPY relocations is to access imported signed pointers
via the GOT.

- PAUTHELF64 only supports the descriptor based TLS (TLSDESC).
- PAUTHELF64 only supports the descriptor based TLS (TLSDESC) dialect.
The "traditional" dialect as selected by -mtls-dialect=traditional is
not supported.

The Rationale behind the requirement to avoid copy relocations is that
the static linker creates the storage that the copy is placed; which
Expand Down Expand Up @@ -1113,9 +1120,11 @@ AUTH variant GOT Generating Relocations
``ENCD(value)`` is the encoding of the signing schema into the GOT
slot using the ``IA`` key for symbols of type STT_FUNC and the ``DA``
key for all other symbol types. The address of the GOT slot ``G`` is
used as a modifer. For ``ENCD(GTLSDESC(S)`` the encoding applies to
used as a modifer. For ``ENCD(GTLSDESC(S)`` the encoding must apply to
the first entry containing the pointer to the variable's TLS
descriptor resolver function.
descriptor resolver function. Whether the parameter to the resolver
function is signed is platform defined as it is a contract between the
dynamic loader and the resolver function.

The GOT entries must be relocated by AUTH variant dynamic relocations.

Expand Down Expand Up @@ -1201,6 +1210,17 @@ The GOT entries must be relocated by AUTH variant dynamic relocations.
| | | | field to bits [11:0] of |
| | | | X. No overflow check. |
+-----------------+----------------------------------------+----------------------------------------+--------------------------+
| 0x266 (598) | R\_AARCH64\_AUTH\_TLSDESC\_CALL | None | For relaxation only. See |
| | | | notes below. |
+-----------------+----------------------------------------+----------------------------------------+--------------------------+

.. note::

Relocation code ``R_AARCH64_AUTH_TLSDESC_CALL`` is needed to permit

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll move the details about the relaxation to a separate document in the design-documents folder.

I think it is platform specific choice of whether both fields of the TLS descriptor are signed. If only the resolver function address is signed then more relaxations are possible.

Likely to be next week before I can do that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's not about whether the second word of the descriptor is signed. The generated code never uses that, either it passes a pointer to it as an opaque blob to the resolver or it relaxes the entire sequence so there is no descriptor to sign? The question is whether the TLS data is being signed like globals can be, and therefore whether &tls_var - TP is the same for all threads or differs in the high bits due to signing. If the data isn't signed, you can just relax to that constant (which will be the same as non-PAuth, and is either an LE immediate or an IE run-time constant), it's only if the data is signed that IE/LE are fundamentally broken?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment. I'll probably just take the rationale/relaxation bits out of the main document for now until I've got the time to work through this slowly.

Reading through the initial issue again #393. I think I've put too much weight on the comment:

Broader relaxations (such as GD->IE or GD->LE with non-statically-known-NULL symbols) are not possible because pointer authentication requires signing at program start-up.

I've missed the non-statically-known-NULL symbols part and in my haste reading. I had been trying to reconcile why other relaxations weren't possible with the code sequence the compiler uses for TLSDESC and a signed GOT.

As an aside:

Empirically using: clang --target=aarch64-linux -march=armv8.3-a -S -O2 tlsdesc.c -o - -fptrauth-elf-got -mabi=pauthtest with a trivial __thread int x; int val() { return x; }

I get:

        pacibsp
        stp     x29, x30, [sp, #-16]!           // 16-byte Folded Spill
        mov     x29, sp
        adrp    x0, :tlsdesc_auth:x
        ldr     x16, [x0, :tlsdesc_auth_lo12:x]
        add     x0, x0, :tlsdesc_auth_lo12:x
        blraa   x16, x0
        mrs     x8, TPIDR_EL0
        ldr     w0, [x8, x0]
        ldp     x29, x30, [sp], #16             // 16-byte Folded Reload
        retab

If I'm reading that correctly, the return value from the TLS resolver function isn't signed. Nor is the value of x. It looks like the only thing that is signed is the descriptor in the GOT.

That looks relaxable in principle, although maybe not in practice to initial exec. I'd expect if it were initial exec the (&tls_var - TP) would be signed in the GOT, there are enough spare instructions and registers to extract the unsigned (&tls_var - TP), but there aren't enough spare instructions to test whether the authenticate failed, which I believe is a requirement for -fptrauth-traps (for systems without FEAT_FPAC).

linker optimization of TLS descriptor code sequences involving
signed GOT entries. Further information, including possible
relaxations, is available in the `PAUTHABITLS`_ design document.

.. raw:: pdf

PageBreak
Expand Down
Loading