https://maskray.me/blog/2023-03-05-linker-notes-on-aarch64 MaskRay Home Archives Presentations [github] [twitter] [ ] 2023-03-05 Linker notes on AArch64 This article describes target-specific details about AArch64 in ELF linkers. AArch64 is the 64-bit execution state for the Arm architecture. The AArch64 execution state runs the A64 instruction set. The AArch32 and AArch64 execution states use very different instruction sets, so many pieces of software use two ports for the two execution states of the Arm architecture. There were the "ARM architecture" and the "ARM instruction set", leading to many software projects using "ARM" or "arm" as their port names. In 2011, ARMv8 introduced two execution states, AArch32 and AArch64. The previous instruction sets "ARM" and "Thumb" were renamed to "A32" and "T32", respectively. In 2017, the architecture was renamed to the "Arm architecture" to reflect the rebranding of the company name. So, the "ARMv8-A" architecture profile is now named "Armv8-A". For the AArch64 execution state, while many projects use "AArch64" as their port name, for legacy reasons, macOS, Windows, the Linux kernel, and some BSD operating systems unfortunately use "arm64". (Support for AArch64 was added to the Linux kernel in version 3.7. Initially, the patch set was named "aarch64", but it was later changed at the request of kernel developers.) ABI documents * ELF for the Arm(r) 64-bit Architecture (AArch64) * System V ABI for the Arm(r) 64-bit Architecture (AArch64) Global Offset Table The Global Offset Table consists of two sections: * .got.plt holds code addresses for PLT. * .got holds other addresses and offsets. The symbol _GLOBAL_OFFSET_TABLE_ is defined at the beginning of the .got section. GNU ld reserves a single entry for .got and .got[0] holds the link-time address of _DYNAMIC for a legacy reason Versions of glibc prior to 2.35 have the _DYNAMIC requirement. See All about Global Offset Table. .got.plt[1] and .got.plt[2] are for lazy binding PLT. Linkers communicate the address of .got.plt to rtld with the dynamic tag DT_PLTGOT. Procedure Linkage Table The registers x16 (IP0) and x17 (IP1) are the first and second intra-procedure-call temporary registers. They may be used by PLT entries and veneers. The PLT header looks like: 1 bti c // If BTI 2 stp x16, x30, [sp,#-16]! 3 adrp x16, &.got.plt[2] 4 ldr x17, [x16, :lo12: &.got.plt[2]] 5 add x16, x16, :lo12: &.got.plt[2] 6 br x17 The Nth PLT entry looks like: 1 bti c // If BTI 2 adrp x16, &.got.plt[N + 3] 3 ldr x17, [x16, :lo12: &.got.plt[N + 3]] 4 add x16, x16, :lo12: &.got.plt[N + 3] 5 autia1716 // If PAC-PLT 6 br x17 When BTI is enabled for the output file, the code sequence starts with bti c. When PAC-PLT is enabled, the code sequence includes autia1716 before br x17. Relocation optimization See All about Global Offset Table#GOT optimization for GOT optimization. There are a few optimization schemes beside GOT optimization, e.g. 1 add x2, x2, 0 // R__ADD_ABS_LO12_NC 2 3 => 4 5 nop 1 adrp x0, symbol 2 add x0, x0, :lo12: symbol 3 4 => 5 6 nop 7 adr x0, symbol --no-relax disables the optimization. See ELF for the Arm(r) 64-bit Architecture (AArch64)#Relocation optimization. Thread Local Storage AArch64 uses a variant of TLS Variant I: the static TLS blocks are placed above the thread pointer. The thread pointer points to the end of the thread control block. The linker performs TLS optimization. See All about thread-local storage. Program Property A .note.gnu.property section contains program property notes that describe special handling requirements for the linker and the dynamic loader. The linker parses input .note.gnu.property sections and recognizes command line options -z force-bti and -z pac-plt to compute the output .note.gnu.property (type is SHT_NOTE) section. Without these options, linkers only set the feature bit in the output file if all the input relocatable object files have the corresponding feature set. 1 for (ELFFileBase *f : ctx.objectFiles) { 2 uint32_t features = f->andFeatures; 3 if (!(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) { 4 if (config->zBtiReport == "error") 5 error(toString(f) + ": -z bti-report: file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property"); 6 else if (config->zBtiReport == "warning") 7 warn(toString(f) + ": -z bti-report: file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property"); 8 } 9 10 if (config->zForceBti && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) { 11 if (config->zBtiReport == "none") 12 warn(toString(f) + ": -z force-bti: file does not have " 13 "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property"); 14 features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI; 15 } 16 if (config->zPacPlt && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { 17 warn(toString(f) + ": -z pac-plt: file does not have " 18 "GNU_PROPERTY_AARCH64_FEATURE_1_PAC property"); 19 features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC; 20 } 21 ret &= features; 22 } Range extension thunks Function calls typically use B and BL instructions. The two instructions have a range of +/-128MiB and may use 2 relocation types: R_AARCH64_CALL26 and R_AARCH64_JUMP26. The range is larger than the branch range for many other instruction sets. If the destination is not reachable by a single B/BL, linkers may insert a veneer (range extension thunk). -no-pie links may use a thunk with absolute addressing targeting any location in the 64-bit address space. 1 : 2 bl __AArch64AbsLongThunk_nonpreemptible 3 b __AArch64AbsLongThunk_nonpreemptible 4 5 <__AArch64AbsLongThunk_nonpreemptible>: 6 ldr x16, .+8 7 br x16 8 9 <$d>: 10 .word 0x00000000 11 .word 0x00000010 12 13 <.plt>: -pie and -shared links need to use a thunk with PC-relative addressing targeting a range of +/-4GiB. 1 : 2 bl __AArch64ADRPThunk_nonpreemptible 3 b __AArch64ADRPThunk_nonpreemptible 4 5 <__AArch64ADRPThunk_nonpreemptible>: 6 adrp x16, nonpreemptible 7 add x16, x16, :lo12: nonpreemptible 8 br x16 The branch target of a thunk may be a PLT entry: 1 : 2 bl __AArch64ADRPThunk_preemptible 3 4 <__AArch64ADRPThunk_preemptible>: 5 adrp x16, preemptible@plt 6 add x16, x16, :lo12: preemptible@plt 7 br x16 8 9 ... 10 11 : 12 adrp x16, &.got.plt[N + 3] 13 ldr x17, [x16, :lo12: &.got.plt[N + 3]] 14 add x16, x16, :lo12: &.got.plt[N + 3] 15 br x17 --fix-cortex-a53-843419 This option enables a linker workaround for Arm Cortex-A53 Errata 843419. Full details are available in the ARM-EPM-048406 document. In ld.lld this additionally sets a workaround when relocating R_AARCH64_JUMP26. Small code model On x86-64, symbols in the small code model are required to be located in the range [0, 2**31 - 2**24). The AArch64 small code model allows for a maximum text segment size of 2GiB and a maximum combined span of text and data segments of 4GiB. For small position-independent code (pic), there is an additional restriction on the size of the Global Offset Table (GOT), which must be smaller than 32KiB. The maximum combined span of text and data segments is larger than that of x86-64. Linked image sizes for AArch64 and x86-64 are comparable, but AArch64 linked images are more resistant to relocation overflows. There are several types of relocation overflows that we need to pay attention to: * .text <-> .rodata * .text <-> .eh_frame (.eh_frame has 32-bit offsets) * .text <-> .bss * .rodata <-> .bss In many programs, .text <-> .data/.bss relocations have the tightest constraints. Overflows due to .text <-> .rodata relocations are possible but rare (I have seen such issues in the past). .rodata is usually larger than .data+.bss. .rodata <-> .bss overflows usually do not occur, but metadata needs to be careful using .quad label-. instead of .long label-.. Such issues can be trivially fixed on the compiler side.) For .text <-> .rodata and .text <-> .bss references, x86-64 uses R_X86_64_REX_GOTPCRELX/R_X86_64_PC32 relocations, which have a small range [-2**31,2**31). In contrast, R_AARCH64_ADR_PREL_PG_HI21 on AArch64 has a doubled range [-2**32,2**32), making it unlikely that AArch64 will hit an issue before the binary becomes excessively oversized for x86-64. --android-memtag-{mode,stack,heap} The options instruct ld.lld to create DT_AARCH64_MEMTAG_* dynamic tags. See Memtag ABI Extension to ELF for the Arm(r) 64-bit Architecture (AArch64). Share Comments * linker * llvm Older Linker notes on Power ISA Please enable JavaScript to view the comments powered by Disqus. Popular Tag Cloud adc ai9 algorithm asc automaton awesome bctf binary binutils bmc build system c c++ ccls cgc chroot clang codinsanity coffee script compiler computer security contest csv ctf data structure debug defcon desktop docker elf emacs email emoji emscripten event expect ext4 feeds firmware floating point forensics freebsd game gcc gentoo glibc graph drawing gtk hanoi haskell hpc inotify ipsec irc isc j javascript josephus problem jq kernel kythe ld leetcode libunwind linker linux llvm lsp m68k makefile math maze mirror ml musl mutt n-body network nginx nim nlp node.js noip notmuch npm ocaml offlineimap oi oj openwrt parallel parser generator perl powerpc presentation puzzle python qq radare2 regex regular expression reverse engineering review router rtld ruby ructfe sanitizer scheme search security shell ssh stringology student festival puzzle suffix array suffix automaton summary suricata telegram telegramircd terminal tls traversal tree trendmicro udev unicode usb vim vpn vte wargame web analytics webqqircd website wechat wechatircd window manager xbindkeys xmonad yanshi Blogroll * BYVoid * fqj1994 * ppwwyyxx (c) 2023 MaskRay Powered by Hexo Home Archives Presentations