a.c (1153B)
1 /* &my_fn round-trips through a function pointer (cross-TU). 2 * 3 * a.c defines the IFUNC; b.c references it via &my_fn so the 4 * address taken in b.c is observed across a TU boundary. After 5 * layout_iplt, the canonical IFUNC LinkSymbol is redirected to 6 * the iplt stub; the per-input undef LinkSymbol in b.c had its 7 * fields copied from the def by resolve_undefs (pre-redirect). 8 * The propagation pass at the tail of layout_iplt re-syncs that 9 * undef to the post-redirect (stub) vaddr, so the function 10 * pointer in b.c carries the stub's address. 11 * 12 * Calling through the pointer goes through the stub: ADRP / 13 * LDR / BR x16, hitting impl_a via the .igot.plt slot. The 14 * raw resolver address would have returned a function-pointer- 15 * to-resolver that, when invoked, returns impl_a (a different 16 * call shape we'd notice via test_main's return value). */ 17 18 extern int impl_a(void); 19 extern int impl_b(void); 20 extern int (*resolve(void))(void); 21 22 int impl_a(void) { return 42; } 23 int impl_b(void) { return 99; } 24 25 int (*resolve(void))(void) { 26 volatile int x = 1; 27 return x ? impl_a : impl_b; 28 } 29 30 int my_fn(void) __attribute__((ifunc("resolve")));