11#define _GNU_SOURCE
22#define SYSCALL_NO_TLS 1
3+ #include <inttypes.h>
34#include <stdlib.h>
45#include <stdarg.h>
56#include <stddef.h>
1920#include <dlfcn.h>
2021#include <semaphore.h>
2122#include <sys/membarrier.h>
23+ #if __has_feature (ptrauth_intrinsics )
24+ #include <ptrauth.h>
25+ #endif
2226#include "pthread_impl.h"
2327#include "fork_impl.h"
2428#include "dynlink.h"
@@ -45,6 +49,18 @@ static void (*error)(const char *, ...) = error_noop;
4549#define container_of (p ,t ,m ) ((t*)((char *)(p)-offsetof(t,m)))
4650#define countof (a ) ((sizeof (a))/(sizeof (a)[0]))
4751
52+ #ifndef TARGET_RELOCATE
53+ #define TARGET_RELOCATE (...) 0
54+ #endif
55+
56+ #ifndef DO_TARGET_RELR
57+ #define DO_TARGET_RELR (...)
58+ #endif
59+
60+ #ifndef FPTR_CAST
61+ #define FPTR_CAST (fty , p ) ((fty)(p))
62+ #endif
63+
4864struct debug {
4965 int ver ;
5066 void * head ;
@@ -111,6 +127,11 @@ struct dso {
111127 size_t * got ;
112128 } * funcdescs ;
113129 size_t * got ;
130+ #ifdef __aarch64__
131+ /* PAuth core info as defined in PAUTHABIELF64:
132+ * https://github.com/ARM-software/abi-aa/blob/2025Q1/pauthabielf64/pauthabielf64.rst#core-information */
133+ size_t * pauth ;
134+ #endif
114135 char buf [];
115136};
116137
@@ -471,6 +492,9 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
471492 case REL_GOT :
472493 case REL_PLT :
473494 * reloc_addr = sym_val + addend ;
495+ /* If AArch64 PAC is enabled and DT_AARCH64_PAC_PLT is present, sign the contents of R_AARCH64_JUMP_SLOT.
496+ * Otherwise, do nothing. */
497+ TARGET_RELOCATE (type , reloc_addr , (size_t )base , sym_val , addend , head == & ldso , dso -> dynv , (uint64_t )error );
474498 break ;
475499 case REL_USYMBOLIC :
476500 memcpy (reloc_addr , & (size_t ){sym_val + addend }, sizeof (size_t ));
@@ -518,6 +542,15 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
518542#endif
519543 case REL_TLSDESC :
520544 if (stride < 3 ) addend = reloc_addr [!TLSDESC_BACKWARDS ];
545+ #ifdef __aarch64__
546+ /* TODO: Submit implementation of undefined weak TLS symbols support to
547+ * mainline musl when it's implemented for other architectures.
548+ * The patch is work-in-progress. */
549+ if (sym && sym -> st_info >>4 == STB_WEAK && sym -> st_shndx == SHN_UNDEF ) {
550+ reloc_addr [0 ] = (size_t )__tlsdesc_undef_weak ;
551+ reloc_addr [1 ] = 0 ;
552+ } else
553+ #endif
521554 if (def .dso -> tls_id > static_tls_cnt ) {
522555 struct td_index * new = malloc (sizeof * new );
523556 if (!new ) {
@@ -533,7 +566,11 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
533566 reloc_addr [0 ] = (size_t )__tlsdesc_dynamic ;
534567 reloc_addr [1 ] = (size_t )new ;
535568 } else {
569+ #if __has_feature (ptrauth_intrinsics ) && !__has_feature (ptrauth_elf_got )
570+ reloc_addr [0 ] = (size_t )ptrauth_strip (& __tlsdesc_static , 0 );
571+ #else
536572 reloc_addr [0 ] = (size_t )__tlsdesc_static ;
573+ #endif
537574#ifdef TLS_ABOVE_TP
538575 reloc_addr [1 ] = tls_val + def .dso -> tls .offset
539576 + TPOFF_K + addend ;
@@ -549,8 +586,18 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
549586 reloc_addr [0 ] = reloc_addr [1 ];
550587 reloc_addr [1 ] = tmp ;
551588 }
589+ #if __has_feature (ptrauth_elf_got )
590+ /* FIXME: actually, signing scheme is written in-place in relocation slot, and we should read and use that.
591+ * However, the scheme is known (IA key + addr div for function and DA key + addr div for data).
592+ * So, we just hard-code that. See also:
593+ * https://github.com/ARM-software/abi-aa/blob/2025Q1/pauthabielf64/pauthabielf64.rst#default-signing-schema */
594+ reloc_addr [0 ] = (size_t )(ptrauth_auth_and_resign ((void * )(reloc_addr [0 ]), 0 , 0 , 0 , (size_t )(reloc_addr )));
595+ reloc_addr [1 ] = (size_t )(ptrauth_sign_unauthenticated ((void * )(reloc_addr [1 ]), 2 , (size_t )(reloc_addr ) + 8 ));
596+ #endif
552597 break ;
553598 default :
599+ if (TARGET_RELOCATE (type , reloc_addr , (size_t )base , sym_val , addend , head == & ldso , dso -> dynv , (uint64_t )error ))
600+ break ;
554601 error ("Error relocating %s: unsupported relocation type %d" ,
555602 dso -> name , type );
556603 if (runtime ) longjmp (* rtld_fail , 1 );
@@ -684,6 +731,84 @@ static void unmap_library(struct dso *dso)
684731 }
685732}
686733
734+ #ifdef __aarch64__
735+
736+ /* See https://github.com/ARM-software/abi-aa/blob/2025Q1/pauthabielf64/pauthabielf64.rst#elf-marking */
737+ #define GNU_PROPERTY_AARCH64_FEATURE_PAUTH 0xc0000001
738+
739+ static uint32_t align8 (uint32_t val ) {
740+ if (val % 8 == 0 )
741+ return val ;
742+ return val + 8 - (val % 8 );
743+ }
744+
745+ static void get_pauth_core_info (struct dso * dso ) {
746+ for (int i = 0 ; i < dso -> phnum ; ++ i ) {
747+ Phdr * ph = & dso -> phdr [i ];
748+
749+ /* Minimal GNU property section containing PAuth core info has 40 bytes size. */
750+ if (!(ph -> p_type == PT_NOTE && ph -> p_memsz >= 40 ))
751+ continue ;
752+
753+ uint32_t * note = laddr (dso , ph -> p_vaddr );
754+ uint32_t * note_arr_end = (uint32_t * )((uintptr_t )note + ph -> p_memsz );
755+
756+ for (; note != note_arr_end ;
757+ /* We can hardcode 8-byte alignment since this code runs only on AArch64. */
758+ note = (uint32_t * )((uintptr_t )note + 4 + 4 + align8 (4 + note [0 ]) + align8 (note [1 ]))) {
759+ /* Note segment is ill-formed: last note information entry exceeds the right segment boundary. */
760+ if (note > note_arr_end ) return ;
761+
762+ if (!(note [0 ] == 4 && note [2 ] == NT_GNU_PROPERTY_TYPE_0 && strncmp ((char * )& note [3 ], "GNU" , 4 ) == 0 ))
763+ continue ;
764+
765+ uint32_t * prop = & note [4 ];
766+ uint32_t * prop_arr_end = (uint32_t * )((uintptr_t )prop + note [1 ]);
767+ for (; prop != prop_arr_end ;
768+ /* We can hardcode 8-byte alignment since this code runs only on AArch64. */
769+ prop = (uint32_t * )((uintptr_t )prop + 4 + 4 + align8 (prop [1 ]))) {
770+ /* GNU property array is ill-formed: its last element end exceeds the right array boundary. */
771+ if (prop > prop_arr_end ) return ;
772+
773+ if (prop [0 ] != GNU_PROPERTY_AARCH64_FEATURE_PAUTH ) continue ;
774+
775+ /* PAuth GNU property must have exactly 16 bytes length:
776+ * 8 bytes for platform and 8 bytes for version value. */
777+ if (prop [1 ] != 16 ) return ;
778+
779+ /* We do not expect multiple PAuth GNU properties. */
780+ if (dso -> pauth != 0 ) return ;
781+
782+ dso -> pauth = (size_t * )& prop [2 ];
783+ }
784+ }
785+ }
786+ }
787+
788+ static void print_pauth_core_info (size_t * pauth , const char * name ) {
789+ if (pauth == 0 ) {
790+ dprintf (2 , "%s: no PAuth core info\n" , name );
791+ return ;
792+ }
793+ dprintf (2 , "%s: (platform: 0x%" PRIx64 "; version: 0x%" PRIx64 ")\n" , name , pauth [0 ], pauth [1 ]);
794+ }
795+
796+ static int check_pauth_core_info_compatibility (size_t * pauth1 , const char * name1 , size_t * pauth2 , const char * name2 ) {
797+ if (pauth1 == pauth2 )
798+ return 1 ;
799+
800+ if (pauth1 == 0 || pauth2 == 0 || pauth1 [0 ] != pauth2 [0 ] || pauth1 [1 ] != pauth2 [1 ]) {
801+ dprintf (2 , "incompatible PAuth core info between %s and %s\n" , name1 , name2 );
802+ print_pauth_core_info (pauth1 , name1 );
803+ print_pauth_core_info (pauth2 , name2 );
804+ return 0 ;
805+ }
806+
807+ return 1 ;
808+ }
809+
810+ #endif
811+
687812static void * map_library (int fd , struct dso * dso )
688813{
689814 Ehdr buf [(896 + sizeof (Ehdr ))/sizeof (Ehdr )];
@@ -860,6 +985,9 @@ static void *map_library(int fd, struct dso *dso)
860985 dso -> base = base ;
861986 dso -> dynv = laddr (dso , dyn );
862987 if (dso -> tls .size ) dso -> tls .image = laddr (dso , tls_image );
988+ #ifdef __aarch64__
989+ get_pauth_core_info (dso );
990+ #endif
863991 free (allocated_buf );
864992 return map ;
865993noexec :
@@ -1184,6 +1312,11 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
11841312 close (fd );
11851313 if (!map ) return 0 ;
11861314
1315+ #ifdef __aarch64__
1316+ if (!check_pauth_core_info_compatibility (head -> pauth , head -> name , temp_dso .pauth , name ))
1317+ return 0 ;
1318+ #endif
1319+
11871320 /* Avoid the danger of getting two versions of libc mapped into the
11881321 * same process when an absolute pathname was used. The symbols
11891322 * checked are chosen to catch both musl and glibc, and to avoid
@@ -1421,6 +1554,9 @@ static void reloc_all(struct dso *p)
14211554 do_relocs (p , laddr (p , dyn [DT_RELA ]), dyn [DT_RELASZ ], 3 );
14221555 if (!DL_FDPIC )
14231556 do_relr_relocs (p , laddr (p , dyn [DT_RELR ]), dyn [DT_RELRSZ ]);
1557+ if (p != & ldso ) {
1558+ DO_TARGET_RELR ((uint64_t )p -> base , p -> dynv );
1559+ }
14241560
14251561 if (head != & ldso && p -> relro_start != p -> relro_end ) {
14261562 long ret = __syscall (SYS_mprotect , laddr (p , p -> relro_start ),
@@ -1464,6 +1600,9 @@ static void kernel_mapped_dso(struct dso *p)
14641600 p -> map = p -> base + min_addr ;
14651601 p -> map_len = max_addr - min_addr ;
14661602 p -> kernel_mapped = 1 ;
1603+ #ifdef __aarch64__
1604+ get_pauth_core_info (p );
1605+ #endif
14671606}
14681607
14691608void __libc_exit_fini ()
@@ -1487,7 +1626,20 @@ void __libc_exit_fini()
14871626 if (dyn [0 ] & (1 <<DT_FINI_ARRAY )) {
14881627 size_t n = dyn [DT_FINI_ARRAYSZ ]/sizeof (size_t );
14891628 size_t * fn = (size_t * )laddr (p , dyn [DT_FINI_ARRAY ])+ n ;
1490- while (n -- ) ((void (* )(void ))* -- fn )();
1629+ #if __has_feature (ptrauth_init_fini )
1630+ while (n -- ) {
1631+ ptrauth_auth_function (
1632+ (void (* )(void ))* -- fn ,
1633+ ptrauth_key_asia ,
1634+ #if __has_feature (ptrauth_init_fini_address_discrimination )
1635+ ptrauth_blend_discriminator (fn , __ptrauth_init_fini_discriminator ))();
1636+ #else
1637+ __ptrauth_init_fini_discriminator )();
1638+ #endif
1639+ }
1640+ #else
1641+ while (n -- ) FPTR_CAST (void (* )(void ), (void * )* (-- fn ))();
1642+ #endif
14911643 }
14921644#ifndef NO_LEGACY_INITFINI
14931645 if ((dyn [0 ] & (1 <<DT_FINI )) && dyn [DT_FINI ])
@@ -1605,7 +1757,21 @@ static void do_init_fini(struct dso **queue)
16051757 if (dyn [0 ] & (1 <<DT_INIT_ARRAY )) {
16061758 size_t n = dyn [DT_INIT_ARRAYSZ ]/sizeof (size_t );
16071759 size_t * fn = laddr (p , dyn [DT_INIT_ARRAY ]);
1608- while (n -- ) ((void (* )(void ))* fn ++ )();
1760+ #if __has_feature (ptrauth_init_fini )
1761+ while (n -- ) {
1762+ ptrauth_auth_function (
1763+ (void (* )(void ))* fn ,
1764+ ptrauth_key_asia ,
1765+ #if __has_feature (ptrauth_init_fini_address_discrimination )
1766+ ptrauth_blend_discriminator (fn , __ptrauth_init_fini_discriminator ))();
1767+ #else
1768+ __ptrauth_init_fini_discriminator )();
1769+ #endif
1770+ ++ fn ;
1771+ }
1772+ #else
1773+ while (n -- ) FPTR_CAST (void (* )(void ), (void * )* fn ++ )();
1774+ #endif
16091775 }
16101776
16111777 pthread_mutex_lock (& init_fini_lock );
@@ -1727,7 +1893,16 @@ hidden void __dls2(unsigned char *base, size_t *sp)
17271893 } else {
17281894 ldso .base = base ;
17291895 }
1896+ #if __has_feature (ptrauth_elf_got )
1897+ /* TODO: support non-null __ehdr_start. Since at this point relocations
1898+ * are not resolved yet, the contents of the GOT slot contains signing schema
1899+ * so the value is treated as non-null even when it is actually null.
1900+ * For undefined weak symbols with non-null values, implicit authentication
1901+ * sequence is emitted on access attempt, and this authentication obviously fails. */
1902+ Ehdr * ehdr = (void * )ldso .base ;
1903+ #else
17301904 Ehdr * ehdr = __ehdr_start ? (void * )__ehdr_start : (void * )ldso .base ;
1905+ #endif
17311906 ldso .name = ldso .shortname = "libc.so" ;
17321907 ldso .phnum = ehdr -> e_phnum ;
17331908 ldso .phdr = laddr (& ldso , ehdr -> e_phoff );
@@ -1764,7 +1939,7 @@ hidden void __dls2(unsigned char *base, size_t *sp)
17641939 * load across the above relocation processing. */
17651940 struct symdef dls2b_def = find_sym (& ldso , "__dls2b" , 0 );
17661941 if (DL_FDPIC ) ((stage3_func )& ldso .funcdescs [dls2b_def .sym - ldso .syms ])(sp , auxv );
1767- else (( stage3_func ) laddr (& ldso , dls2b_def .sym -> st_value ))(sp , auxv );
1942+ else FPTR_CAST ( stage3_func , laddr (& ldso , dls2b_def .sym -> st_value ))(sp , auxv );
17681943}
17691944
17701945/* Stage 2b sets up a valid thread pointer, which requires relocations
@@ -1788,7 +1963,7 @@ void __dls2b(size_t *sp, size_t *auxv)
17881963
17891964 struct symdef dls3_def = find_sym (& ldso , "__dls3" , 0 );
17901965 if (DL_FDPIC ) ((stage3_func )& ldso .funcdescs [dls3_def .sym - ldso .syms ])(sp , auxv );
1791- else (( stage3_func ) laddr (& ldso , dls3_def .sym -> st_value ))(sp , auxv );
1966+ else FPTR_CAST ( stage3_func , laddr (& ldso , dls3_def .sym -> st_value ))(sp , auxv );
17921967}
17931968
17941969/* Stage 3 of the dynamic linker is called with the dynamic linker/libc
0 commit comments