xref: /freebsd/contrib/llvm-project/lld/ELF/Arch/AArch64.cpp (revision 298c3e8d6b6405cbea13a7a1a1fbc1ad5a45c378)
10b57cec5SDimitry Andric //===- AArch64.cpp --------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "Symbols.h"
100b57cec5SDimitry Andric #include "SyntheticSections.h"
110b57cec5SDimitry Andric #include "Target.h"
120b57cec5SDimitry Andric #include "Thunks.h"
130b57cec5SDimitry Andric #include "lld/Common/ErrorHandler.h"
140b57cec5SDimitry Andric #include "llvm/Object/ELF.h"
150b57cec5SDimitry Andric #include "llvm/Support/Endian.h"
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric using namespace llvm;
180b57cec5SDimitry Andric using namespace llvm::support::endian;
190b57cec5SDimitry Andric using namespace llvm::ELF;
205ffd83dbSDimitry Andric using namespace lld;
215ffd83dbSDimitry Andric using namespace lld::elf;
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric // Page(Expr) is the page address of the expression Expr, defined
240b57cec5SDimitry Andric // as (Expr & ~0xFFF). (This applies even if the machine page size
250b57cec5SDimitry Andric // supported by the platform has a different value.)
265ffd83dbSDimitry Andric uint64_t elf::getAArch64Page(uint64_t expr) {
270b57cec5SDimitry Andric   return expr & ~static_cast<uint64_t>(0xFFF);
280b57cec5SDimitry Andric }
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric namespace {
310b57cec5SDimitry Andric class AArch64 : public TargetInfo {
320b57cec5SDimitry Andric public:
330b57cec5SDimitry Andric   AArch64();
340b57cec5SDimitry Andric   RelExpr getRelExpr(RelType type, const Symbol &s,
350b57cec5SDimitry Andric                      const uint8_t *loc) const override;
360b57cec5SDimitry Andric   RelType getDynRel(RelType type) const override;
37fe6060f1SDimitry Andric   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
380b57cec5SDimitry Andric   void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
390b57cec5SDimitry Andric   void writePltHeader(uint8_t *buf) const override;
40480093f4SDimitry Andric   void writePlt(uint8_t *buf, const Symbol &sym,
41480093f4SDimitry Andric                 uint64_t pltEntryAddr) const override;
420b57cec5SDimitry Andric   bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
43480093f4SDimitry Andric                   uint64_t branchAddr, const Symbol &s,
44480093f4SDimitry Andric                   int64_t a) const override;
450b57cec5SDimitry Andric   uint32_t getThunkSectionSpacing() const override;
460b57cec5SDimitry Andric   bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
470b57cec5SDimitry Andric   bool usesOnlyLowPageBits(RelType type) const override;
485ffd83dbSDimitry Andric   void relocate(uint8_t *loc, const Relocation &rel,
495ffd83dbSDimitry Andric                 uint64_t val) const override;
50e8d8bef9SDimitry Andric   RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
515ffd83dbSDimitry Andric   void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
525ffd83dbSDimitry Andric                       uint64_t val) const override;
535ffd83dbSDimitry Andric   void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
545ffd83dbSDimitry Andric                       uint64_t val) const override;
555ffd83dbSDimitry Andric   void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
565ffd83dbSDimitry Andric                       uint64_t val) const override;
570b57cec5SDimitry Andric };
580b57cec5SDimitry Andric } // namespace
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric AArch64::AArch64() {
610b57cec5SDimitry Andric   copyRel = R_AARCH64_COPY;
620b57cec5SDimitry Andric   relativeRel = R_AARCH64_RELATIVE;
630b57cec5SDimitry Andric   iRelativeRel = R_AARCH64_IRELATIVE;
640b57cec5SDimitry Andric   gotRel = R_AARCH64_GLOB_DAT;
650b57cec5SDimitry Andric   noneRel = R_AARCH64_NONE;
660b57cec5SDimitry Andric   pltRel = R_AARCH64_JUMP_SLOT;
670b57cec5SDimitry Andric   symbolicRel = R_AARCH64_ABS64;
680b57cec5SDimitry Andric   tlsDescRel = R_AARCH64_TLSDESC;
690b57cec5SDimitry Andric   tlsGotRel = R_AARCH64_TLS_TPREL64;
700b57cec5SDimitry Andric   pltHeaderSize = 32;
71480093f4SDimitry Andric   pltEntrySize = 16;
72480093f4SDimitry Andric   ipltEntrySize = 16;
730b57cec5SDimitry Andric   defaultMaxPageSize = 65536;
74e8d8bef9SDimitry Andric   gotBaseSymInGotPlt = false;
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric   // Align to the 2 MiB page size (known as a superpage or huge page).
770b57cec5SDimitry Andric   // FreeBSD automatically promotes 2 MiB-aligned allocations.
780b57cec5SDimitry Andric   defaultImageBase = 0x200000;
790b57cec5SDimitry Andric 
800b57cec5SDimitry Andric   needsThunks = true;
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
840b57cec5SDimitry Andric                             const uint8_t *loc) const {
850b57cec5SDimitry Andric   switch (type) {
8685868e8aSDimitry Andric   case R_AARCH64_ABS16:
8785868e8aSDimitry Andric   case R_AARCH64_ABS32:
8885868e8aSDimitry Andric   case R_AARCH64_ABS64:
8985868e8aSDimitry Andric   case R_AARCH64_ADD_ABS_LO12_NC:
9085868e8aSDimitry Andric   case R_AARCH64_LDST128_ABS_LO12_NC:
9185868e8aSDimitry Andric   case R_AARCH64_LDST16_ABS_LO12_NC:
9285868e8aSDimitry Andric   case R_AARCH64_LDST32_ABS_LO12_NC:
9385868e8aSDimitry Andric   case R_AARCH64_LDST64_ABS_LO12_NC:
9485868e8aSDimitry Andric   case R_AARCH64_LDST8_ABS_LO12_NC:
9585868e8aSDimitry Andric   case R_AARCH64_MOVW_SABS_G0:
9685868e8aSDimitry Andric   case R_AARCH64_MOVW_SABS_G1:
9785868e8aSDimitry Andric   case R_AARCH64_MOVW_SABS_G2:
9885868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G0:
9985868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G0_NC:
10085868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G1:
10185868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G1_NC:
10285868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G2:
10385868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G2_NC:
10485868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G3:
10585868e8aSDimitry Andric     return R_ABS;
1060b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADR_PAGE21:
1070b57cec5SDimitry Andric     return R_AARCH64_TLSDESC_PAGE;
1080b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_LD64_LO12:
1090b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADD_LO12:
1100b57cec5SDimitry Andric     return R_TLSDESC;
1110b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_CALL:
1120b57cec5SDimitry Andric     return R_TLSDESC_CALL;
1130b57cec5SDimitry Andric   case R_AARCH64_TLSLE_ADD_TPREL_HI12:
1140b57cec5SDimitry Andric   case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
1150b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
1160b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
1170b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
1180b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
1190b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
12085868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G0:
12185868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
12285868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G1:
12385868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
12485868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G2:
125e8d8bef9SDimitry Andric     return R_TPREL;
1260b57cec5SDimitry Andric   case R_AARCH64_CALL26:
1270b57cec5SDimitry Andric   case R_AARCH64_CONDBR19:
1280b57cec5SDimitry Andric   case R_AARCH64_JUMP26:
1290b57cec5SDimitry Andric   case R_AARCH64_TSTBR14:
1305ffd83dbSDimitry Andric   case R_AARCH64_PLT32:
1310b57cec5SDimitry Andric     return R_PLT_PC;
1320b57cec5SDimitry Andric   case R_AARCH64_PREL16:
1330b57cec5SDimitry Andric   case R_AARCH64_PREL32:
1340b57cec5SDimitry Andric   case R_AARCH64_PREL64:
1350b57cec5SDimitry Andric   case R_AARCH64_ADR_PREL_LO21:
1360b57cec5SDimitry Andric   case R_AARCH64_LD_PREL_LO19:
13785868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G0:
13885868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G0_NC:
13985868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G1:
14085868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G1_NC:
14185868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G2:
14285868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G2_NC:
14385868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G3:
1440b57cec5SDimitry Andric     return R_PC;
1450b57cec5SDimitry Andric   case R_AARCH64_ADR_PREL_PG_HI21:
1460b57cec5SDimitry Andric   case R_AARCH64_ADR_PREL_PG_HI21_NC:
1470b57cec5SDimitry Andric     return R_AARCH64_PAGE_PC;
1480b57cec5SDimitry Andric   case R_AARCH64_LD64_GOT_LO12_NC:
1490b57cec5SDimitry Andric   case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
1500b57cec5SDimitry Andric     return R_GOT;
151e8d8bef9SDimitry Andric   case R_AARCH64_LD64_GOTPAGE_LO15:
152e8d8bef9SDimitry Andric     return R_AARCH64_GOT_PAGE;
1530b57cec5SDimitry Andric   case R_AARCH64_ADR_GOT_PAGE:
1540b57cec5SDimitry Andric   case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
1550b57cec5SDimitry Andric     return R_AARCH64_GOT_PAGE_PC;
1560b57cec5SDimitry Andric   case R_AARCH64_NONE:
1570b57cec5SDimitry Andric     return R_NONE;
1580b57cec5SDimitry Andric   default:
15985868e8aSDimitry Andric     error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
16085868e8aSDimitry Andric           ") against symbol " + toString(s));
16185868e8aSDimitry Andric     return R_NONE;
1620b57cec5SDimitry Andric   }
1630b57cec5SDimitry Andric }
1640b57cec5SDimitry Andric 
165e8d8bef9SDimitry Andric RelExpr AArch64::adjustTlsExpr(RelType type, RelExpr expr) const {
1660b57cec5SDimitry Andric   if (expr == R_RELAX_TLS_GD_TO_IE) {
1670b57cec5SDimitry Andric     if (type == R_AARCH64_TLSDESC_ADR_PAGE21)
1680b57cec5SDimitry Andric       return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC;
1690b57cec5SDimitry Andric     return R_RELAX_TLS_GD_TO_IE_ABS;
1700b57cec5SDimitry Andric   }
1710b57cec5SDimitry Andric   return expr;
1720b57cec5SDimitry Andric }
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric bool AArch64::usesOnlyLowPageBits(RelType type) const {
1750b57cec5SDimitry Andric   switch (type) {
1760b57cec5SDimitry Andric   default:
1770b57cec5SDimitry Andric     return false;
1780b57cec5SDimitry Andric   case R_AARCH64_ADD_ABS_LO12_NC:
1790b57cec5SDimitry Andric   case R_AARCH64_LD64_GOT_LO12_NC:
1800b57cec5SDimitry Andric   case R_AARCH64_LDST128_ABS_LO12_NC:
1810b57cec5SDimitry Andric   case R_AARCH64_LDST16_ABS_LO12_NC:
1820b57cec5SDimitry Andric   case R_AARCH64_LDST32_ABS_LO12_NC:
1830b57cec5SDimitry Andric   case R_AARCH64_LDST64_ABS_LO12_NC:
1840b57cec5SDimitry Andric   case R_AARCH64_LDST8_ABS_LO12_NC:
1850b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADD_LO12:
1860b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_LD64_LO12:
1870b57cec5SDimitry Andric   case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
1880b57cec5SDimitry Andric     return true;
1890b57cec5SDimitry Andric   }
1900b57cec5SDimitry Andric }
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric RelType AArch64::getDynRel(RelType type) const {
1930b57cec5SDimitry Andric   if (type == R_AARCH64_ABS64)
1940b57cec5SDimitry Andric     return type;
1950b57cec5SDimitry Andric   return R_AARCH64_NONE;
1960b57cec5SDimitry Andric }
1970b57cec5SDimitry Andric 
198fe6060f1SDimitry Andric int64_t AArch64::getImplicitAddend(const uint8_t *buf, RelType type) const {
199fe6060f1SDimitry Andric   switch (type) {
200fe6060f1SDimitry Andric   case R_AARCH64_TLSDESC:
201fe6060f1SDimitry Andric     return read64(buf + 8);
202*298c3e8dSDimitry Andric   case R_AARCH64_NONE:
203*298c3e8dSDimitry Andric     return 0;
204*298c3e8dSDimitry Andric   case R_AARCH64_PREL32:
205*298c3e8dSDimitry Andric     return SignExtend64<32>(read32(buf));
206*298c3e8dSDimitry Andric   case R_AARCH64_ABS64:
207*298c3e8dSDimitry Andric   case R_AARCH64_PREL64:
208*298c3e8dSDimitry Andric     return read64(buf);
209fe6060f1SDimitry Andric   default:
210fe6060f1SDimitry Andric     internalLinkerError(getErrorLocation(buf),
211fe6060f1SDimitry Andric                         "cannot read addend for relocation " + toString(type));
212fe6060f1SDimitry Andric     return 0;
213fe6060f1SDimitry Andric   }
214fe6060f1SDimitry Andric }
215fe6060f1SDimitry Andric 
2160b57cec5SDimitry Andric void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const {
217fe6060f1SDimitry Andric   write64(buf, in.plt->getVA());
2180b57cec5SDimitry Andric }
2190b57cec5SDimitry Andric 
2200b57cec5SDimitry Andric void AArch64::writePltHeader(uint8_t *buf) const {
2210b57cec5SDimitry Andric   const uint8_t pltData[] = {
2220b57cec5SDimitry Andric       0xf0, 0x7b, 0xbf, 0xa9, // stp    x16, x30, [sp,#-16]!
2230b57cec5SDimitry Andric       0x10, 0x00, 0x00, 0x90, // adrp   x16, Page(&(.plt.got[2]))
2240b57cec5SDimitry Andric       0x11, 0x02, 0x40, 0xf9, // ldr    x17, [x16, Offset(&(.plt.got[2]))]
2250b57cec5SDimitry Andric       0x10, 0x02, 0x00, 0x91, // add    x16, x16, Offset(&(.plt.got[2]))
2260b57cec5SDimitry Andric       0x20, 0x02, 0x1f, 0xd6, // br     x17
2270b57cec5SDimitry Andric       0x1f, 0x20, 0x03, 0xd5, // nop
2280b57cec5SDimitry Andric       0x1f, 0x20, 0x03, 0xd5, // nop
2290b57cec5SDimitry Andric       0x1f, 0x20, 0x03, 0xd5  // nop
2300b57cec5SDimitry Andric   };
2310b57cec5SDimitry Andric   memcpy(buf, pltData, sizeof(pltData));
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric   uint64_t got = in.gotPlt->getVA();
2340b57cec5SDimitry Andric   uint64_t plt = in.plt->getVA();
2355ffd83dbSDimitry Andric   relocateNoSym(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
2360b57cec5SDimitry Andric                 getAArch64Page(got + 16) - getAArch64Page(plt + 4));
2375ffd83dbSDimitry Andric   relocateNoSym(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
2385ffd83dbSDimitry Andric   relocateNoSym(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
2390b57cec5SDimitry Andric }
2400b57cec5SDimitry Andric 
241480093f4SDimitry Andric void AArch64::writePlt(uint8_t *buf, const Symbol &sym,
242480093f4SDimitry Andric                        uint64_t pltEntryAddr) const {
2430b57cec5SDimitry Andric   const uint8_t inst[] = {
2440b57cec5SDimitry Andric       0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
2450b57cec5SDimitry Andric       0x11, 0x02, 0x40, 0xf9, // ldr  x17, [x16, Offset(&(.plt.got[n]))]
2460b57cec5SDimitry Andric       0x10, 0x02, 0x00, 0x91, // add  x16, x16, Offset(&(.plt.got[n]))
2470b57cec5SDimitry Andric       0x20, 0x02, 0x1f, 0xd6  // br   x17
2480b57cec5SDimitry Andric   };
2490b57cec5SDimitry Andric   memcpy(buf, inst, sizeof(inst));
2500b57cec5SDimitry Andric 
251480093f4SDimitry Andric   uint64_t gotPltEntryAddr = sym.getGotPltVA();
2525ffd83dbSDimitry Andric   relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
2530b57cec5SDimitry Andric                 getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
2545ffd83dbSDimitry Andric   relocateNoSym(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
2555ffd83dbSDimitry Andric   relocateNoSym(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
2560b57cec5SDimitry Andric }
2570b57cec5SDimitry Andric 
2580b57cec5SDimitry Andric bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
259480093f4SDimitry Andric                          uint64_t branchAddr, const Symbol &s,
260480093f4SDimitry Andric                          int64_t a) const {
261480093f4SDimitry Andric   // If s is an undefined weak symbol and does not have a PLT entry then it
262480093f4SDimitry Andric   // will be resolved as a branch to the next instruction.
263480093f4SDimitry Andric   if (s.isUndefWeak() && !s.isInPlt())
264480093f4SDimitry Andric     return false;
2650b57cec5SDimitry Andric   // ELF for the ARM 64-bit architecture, section Call and Jump relocations
2660b57cec5SDimitry Andric   // only permits range extension thunks for R_AARCH64_CALL26 and
2670b57cec5SDimitry Andric   // R_AARCH64_JUMP26 relocation types.
2685ffd83dbSDimitry Andric   if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
2695ffd83dbSDimitry Andric       type != R_AARCH64_PLT32)
2700b57cec5SDimitry Andric     return false;
271480093f4SDimitry Andric   uint64_t dst = expr == R_PLT_PC ? s.getPltVA() : s.getVA(a);
2720b57cec5SDimitry Andric   return !inBranchRange(type, branchAddr, dst);
2730b57cec5SDimitry Andric }
2740b57cec5SDimitry Andric 
2750b57cec5SDimitry Andric uint32_t AArch64::getThunkSectionSpacing() const {
2760b57cec5SDimitry Andric   // See comment in Arch/ARM.cpp for a more detailed explanation of
2770b57cec5SDimitry Andric   // getThunkSectionSpacing(). For AArch64 the only branches we are permitted to
2780b57cec5SDimitry Andric   // Thunk have a range of +/- 128 MiB
2790b57cec5SDimitry Andric   return (128 * 1024 * 1024) - 0x30000;
2800b57cec5SDimitry Andric }
2810b57cec5SDimitry Andric 
2820b57cec5SDimitry Andric bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
2835ffd83dbSDimitry Andric   if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
2845ffd83dbSDimitry Andric       type != R_AARCH64_PLT32)
2850b57cec5SDimitry Andric     return true;
2860b57cec5SDimitry Andric   // The AArch64 call and unconditional branch instructions have a range of
2875ffd83dbSDimitry Andric   // +/- 128 MiB. The PLT32 relocation supports a range up to +/- 2 GiB.
2885ffd83dbSDimitry Andric   uint64_t range =
2895ffd83dbSDimitry Andric       type == R_AARCH64_PLT32 ? (UINT64_C(1) << 31) : (128 * 1024 * 1024);
2900b57cec5SDimitry Andric   if (dst > src) {
2910b57cec5SDimitry Andric     // Immediate of branch is signed.
2920b57cec5SDimitry Andric     range -= 4;
2930b57cec5SDimitry Andric     return dst - src <= range;
2940b57cec5SDimitry Andric   }
2950b57cec5SDimitry Andric   return src - dst <= range;
2960b57cec5SDimitry Andric }
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric static void write32AArch64Addr(uint8_t *l, uint64_t imm) {
2990b57cec5SDimitry Andric   uint32_t immLo = (imm & 0x3) << 29;
3000b57cec5SDimitry Andric   uint32_t immHi = (imm & 0x1FFFFC) << 3;
3010b57cec5SDimitry Andric   uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3);
3020b57cec5SDimitry Andric   write32le(l, (read32le(l) & ~mask) | immLo | immHi);
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric 
3050b57cec5SDimitry Andric // Return the bits [Start, End] from Val shifted Start bits.
3060b57cec5SDimitry Andric // For instance, getBits(0xF0, 4, 8) returns 0xF.
3070b57cec5SDimitry Andric static uint64_t getBits(uint64_t val, int start, int end) {
3080b57cec5SDimitry Andric   uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1;
3090b57cec5SDimitry Andric   return (val >> start) & mask;
3100b57cec5SDimitry Andric }
3110b57cec5SDimitry Andric 
3120b57cec5SDimitry Andric static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric // Update the immediate field in a AARCH64 ldr, str, and add instruction.
3150b57cec5SDimitry Andric static void or32AArch64Imm(uint8_t *l, uint64_t imm) {
3160b57cec5SDimitry Andric   or32le(l, (imm & 0xFFF) << 10);
3170b57cec5SDimitry Andric }
3180b57cec5SDimitry Andric 
31985868e8aSDimitry Andric // Update the immediate field in an AArch64 movk, movn or movz instruction
32085868e8aSDimitry Andric // for a signed relocation, and update the opcode of a movn or movz instruction
32185868e8aSDimitry Andric // to match the sign of the operand.
32285868e8aSDimitry Andric static void writeSMovWImm(uint8_t *loc, uint32_t imm) {
32385868e8aSDimitry Andric   uint32_t inst = read32le(loc);
32485868e8aSDimitry Andric   // Opcode field is bits 30, 29, with 10 = movz, 00 = movn and 11 = movk.
32585868e8aSDimitry Andric   if (!(inst & (1 << 29))) {
32685868e8aSDimitry Andric     // movn or movz.
32785868e8aSDimitry Andric     if (imm & 0x10000) {
32885868e8aSDimitry Andric       // Change opcode to movn, which takes an inverted operand.
32985868e8aSDimitry Andric       imm ^= 0xFFFF;
33085868e8aSDimitry Andric       inst &= ~(1 << 30);
33185868e8aSDimitry Andric     } else {
33285868e8aSDimitry Andric       // Change opcode to movz.
33385868e8aSDimitry Andric       inst |= 1 << 30;
33485868e8aSDimitry Andric     }
33585868e8aSDimitry Andric   }
33685868e8aSDimitry Andric   write32le(loc, inst | ((imm & 0xFFFF) << 5));
33785868e8aSDimitry Andric }
33885868e8aSDimitry Andric 
3395ffd83dbSDimitry Andric void AArch64::relocate(uint8_t *loc, const Relocation &rel,
3405ffd83dbSDimitry Andric                        uint64_t val) const {
3415ffd83dbSDimitry Andric   switch (rel.type) {
3420b57cec5SDimitry Andric   case R_AARCH64_ABS16:
3430b57cec5SDimitry Andric   case R_AARCH64_PREL16:
3445ffd83dbSDimitry Andric     checkIntUInt(loc, val, 16, rel);
345fe6060f1SDimitry Andric     write16(loc, val);
3460b57cec5SDimitry Andric     break;
3470b57cec5SDimitry Andric   case R_AARCH64_ABS32:
3480b57cec5SDimitry Andric   case R_AARCH64_PREL32:
3495ffd83dbSDimitry Andric     checkIntUInt(loc, val, 32, rel);
350fe6060f1SDimitry Andric     write32(loc, val);
3515ffd83dbSDimitry Andric     break;
3525ffd83dbSDimitry Andric   case R_AARCH64_PLT32:
3535ffd83dbSDimitry Andric     checkInt(loc, val, 32, rel);
354fe6060f1SDimitry Andric     write32(loc, val);
3550b57cec5SDimitry Andric     break;
3560b57cec5SDimitry Andric   case R_AARCH64_ABS64:
3570b57cec5SDimitry Andric   case R_AARCH64_PREL64:
358fe6060f1SDimitry Andric     write64(loc, val);
3590b57cec5SDimitry Andric     break;
3600b57cec5SDimitry Andric   case R_AARCH64_ADD_ABS_LO12_NC:
3610b57cec5SDimitry Andric     or32AArch64Imm(loc, val);
3620b57cec5SDimitry Andric     break;
3630b57cec5SDimitry Andric   case R_AARCH64_ADR_GOT_PAGE:
3640b57cec5SDimitry Andric   case R_AARCH64_ADR_PREL_PG_HI21:
3650b57cec5SDimitry Andric   case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
3660b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADR_PAGE21:
3675ffd83dbSDimitry Andric     checkInt(loc, val, 33, rel);
3680b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
3690b57cec5SDimitry Andric   case R_AARCH64_ADR_PREL_PG_HI21_NC:
3700b57cec5SDimitry Andric     write32AArch64Addr(loc, val >> 12);
3710b57cec5SDimitry Andric     break;
3720b57cec5SDimitry Andric   case R_AARCH64_ADR_PREL_LO21:
3735ffd83dbSDimitry Andric     checkInt(loc, val, 21, rel);
3740b57cec5SDimitry Andric     write32AArch64Addr(loc, val);
3750b57cec5SDimitry Andric     break;
3760b57cec5SDimitry Andric   case R_AARCH64_JUMP26:
3770b57cec5SDimitry Andric     // Normally we would just write the bits of the immediate field, however
3780b57cec5SDimitry Andric     // when patching instructions for the cpu errata fix -fix-cortex-a53-843419
3790b57cec5SDimitry Andric     // we want to replace a non-branch instruction with a branch immediate
3800b57cec5SDimitry Andric     // instruction. By writing all the bits of the instruction including the
3810b57cec5SDimitry Andric     // opcode and the immediate (0 001 | 01 imm26) we can do this
3820b57cec5SDimitry Andric     // transformation by placing a R_AARCH64_JUMP26 relocation at the offset of
3830b57cec5SDimitry Andric     // the instruction we want to patch.
3840b57cec5SDimitry Andric     write32le(loc, 0x14000000);
3850b57cec5SDimitry Andric     LLVM_FALLTHROUGH;
3860b57cec5SDimitry Andric   case R_AARCH64_CALL26:
3875ffd83dbSDimitry Andric     checkInt(loc, val, 28, rel);
3880b57cec5SDimitry Andric     or32le(loc, (val & 0x0FFFFFFC) >> 2);
3890b57cec5SDimitry Andric     break;
3900b57cec5SDimitry Andric   case R_AARCH64_CONDBR19:
3910b57cec5SDimitry Andric   case R_AARCH64_LD_PREL_LO19:
3925ffd83dbSDimitry Andric     checkAlignment(loc, val, 4, rel);
3935ffd83dbSDimitry Andric     checkInt(loc, val, 21, rel);
3940b57cec5SDimitry Andric     or32le(loc, (val & 0x1FFFFC) << 3);
3950b57cec5SDimitry Andric     break;
3960b57cec5SDimitry Andric   case R_AARCH64_LDST8_ABS_LO12_NC:
3970b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
3980b57cec5SDimitry Andric     or32AArch64Imm(loc, getBits(val, 0, 11));
3990b57cec5SDimitry Andric     break;
4000b57cec5SDimitry Andric   case R_AARCH64_LDST16_ABS_LO12_NC:
4010b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
4025ffd83dbSDimitry Andric     checkAlignment(loc, val, 2, rel);
4030b57cec5SDimitry Andric     or32AArch64Imm(loc, getBits(val, 1, 11));
4040b57cec5SDimitry Andric     break;
4050b57cec5SDimitry Andric   case R_AARCH64_LDST32_ABS_LO12_NC:
4060b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
4075ffd83dbSDimitry Andric     checkAlignment(loc, val, 4, rel);
4080b57cec5SDimitry Andric     or32AArch64Imm(loc, getBits(val, 2, 11));
4090b57cec5SDimitry Andric     break;
4100b57cec5SDimitry Andric   case R_AARCH64_LDST64_ABS_LO12_NC:
4110b57cec5SDimitry Andric   case R_AARCH64_LD64_GOT_LO12_NC:
4120b57cec5SDimitry Andric   case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
4130b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
4140b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_LD64_LO12:
4155ffd83dbSDimitry Andric     checkAlignment(loc, val, 8, rel);
4160b57cec5SDimitry Andric     or32AArch64Imm(loc, getBits(val, 3, 11));
4170b57cec5SDimitry Andric     break;
4180b57cec5SDimitry Andric   case R_AARCH64_LDST128_ABS_LO12_NC:
4190b57cec5SDimitry Andric   case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
4205ffd83dbSDimitry Andric     checkAlignment(loc, val, 16, rel);
4210b57cec5SDimitry Andric     or32AArch64Imm(loc, getBits(val, 4, 11));
4220b57cec5SDimitry Andric     break;
423e8d8bef9SDimitry Andric   case R_AARCH64_LD64_GOTPAGE_LO15:
424e8d8bef9SDimitry Andric     checkAlignment(loc, val, 8, rel);
425e8d8bef9SDimitry Andric     or32AArch64Imm(loc, getBits(val, 3, 14));
426e8d8bef9SDimitry Andric     break;
42785868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G0:
4285ffd83dbSDimitry Andric     checkUInt(loc, val, 16, rel);
42985868e8aSDimitry Andric     LLVM_FALLTHROUGH;
4300b57cec5SDimitry Andric   case R_AARCH64_MOVW_UABS_G0_NC:
4310b57cec5SDimitry Andric     or32le(loc, (val & 0xFFFF) << 5);
4320b57cec5SDimitry Andric     break;
43385868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G1:
4345ffd83dbSDimitry Andric     checkUInt(loc, val, 32, rel);
43585868e8aSDimitry Andric     LLVM_FALLTHROUGH;
4360b57cec5SDimitry Andric   case R_AARCH64_MOVW_UABS_G1_NC:
4370b57cec5SDimitry Andric     or32le(loc, (val & 0xFFFF0000) >> 11);
4380b57cec5SDimitry Andric     break;
43985868e8aSDimitry Andric   case R_AARCH64_MOVW_UABS_G2:
4405ffd83dbSDimitry Andric     checkUInt(loc, val, 48, rel);
44185868e8aSDimitry Andric     LLVM_FALLTHROUGH;
4420b57cec5SDimitry Andric   case R_AARCH64_MOVW_UABS_G2_NC:
4430b57cec5SDimitry Andric     or32le(loc, (val & 0xFFFF00000000) >> 27);
4440b57cec5SDimitry Andric     break;
4450b57cec5SDimitry Andric   case R_AARCH64_MOVW_UABS_G3:
4460b57cec5SDimitry Andric     or32le(loc, (val & 0xFFFF000000000000) >> 43);
4470b57cec5SDimitry Andric     break;
44885868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G0:
44985868e8aSDimitry Andric   case R_AARCH64_MOVW_SABS_G0:
45085868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G0:
4515ffd83dbSDimitry Andric     checkInt(loc, val, 17, rel);
45285868e8aSDimitry Andric     LLVM_FALLTHROUGH;
45385868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G0_NC:
45485868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
45585868e8aSDimitry Andric     writeSMovWImm(loc, val);
45685868e8aSDimitry Andric     break;
45785868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G1:
45885868e8aSDimitry Andric   case R_AARCH64_MOVW_SABS_G1:
45985868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G1:
4605ffd83dbSDimitry Andric     checkInt(loc, val, 33, rel);
46185868e8aSDimitry Andric     LLVM_FALLTHROUGH;
46285868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G1_NC:
46385868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
46485868e8aSDimitry Andric     writeSMovWImm(loc, val >> 16);
46585868e8aSDimitry Andric     break;
46685868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G2:
46785868e8aSDimitry Andric   case R_AARCH64_MOVW_SABS_G2:
46885868e8aSDimitry Andric   case R_AARCH64_TLSLE_MOVW_TPREL_G2:
4695ffd83dbSDimitry Andric     checkInt(loc, val, 49, rel);
47085868e8aSDimitry Andric     LLVM_FALLTHROUGH;
47185868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G2_NC:
47285868e8aSDimitry Andric     writeSMovWImm(loc, val >> 32);
47385868e8aSDimitry Andric     break;
47485868e8aSDimitry Andric   case R_AARCH64_MOVW_PREL_G3:
47585868e8aSDimitry Andric     writeSMovWImm(loc, val >> 48);
47685868e8aSDimitry Andric     break;
4770b57cec5SDimitry Andric   case R_AARCH64_TSTBR14:
4785ffd83dbSDimitry Andric     checkInt(loc, val, 16, rel);
4790b57cec5SDimitry Andric     or32le(loc, (val & 0xFFFC) << 3);
4800b57cec5SDimitry Andric     break;
4810b57cec5SDimitry Andric   case R_AARCH64_TLSLE_ADD_TPREL_HI12:
4825ffd83dbSDimitry Andric     checkUInt(loc, val, 24, rel);
4830b57cec5SDimitry Andric     or32AArch64Imm(loc, val >> 12);
4840b57cec5SDimitry Andric     break;
4850b57cec5SDimitry Andric   case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
4860b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADD_LO12:
4870b57cec5SDimitry Andric     or32AArch64Imm(loc, val);
4880b57cec5SDimitry Andric     break;
489fe6060f1SDimitry Andric   case R_AARCH64_TLSDESC:
490fe6060f1SDimitry Andric     // For R_AARCH64_TLSDESC the addend is stored in the second 64-bit word.
491fe6060f1SDimitry Andric     write64(loc + 8, val);
492fe6060f1SDimitry Andric     break;
4930b57cec5SDimitry Andric   default:
49485868e8aSDimitry Andric     llvm_unreachable("unknown relocation");
4950b57cec5SDimitry Andric   }
4960b57cec5SDimitry Andric }
4970b57cec5SDimitry Andric 
4985ffd83dbSDimitry Andric void AArch64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
4995ffd83dbSDimitry Andric                              uint64_t val) const {
5000b57cec5SDimitry Andric   // TLSDESC Global-Dynamic relocation are in the form:
5010b57cec5SDimitry Andric   //   adrp    x0, :tlsdesc:v             [R_AARCH64_TLSDESC_ADR_PAGE21]
5020b57cec5SDimitry Andric   //   ldr     x1, [x0, #:tlsdesc_lo12:v  [R_AARCH64_TLSDESC_LD64_LO12]
5030b57cec5SDimitry Andric   //   add     x0, x0, :tlsdesc_los:v     [R_AARCH64_TLSDESC_ADD_LO12]
5040b57cec5SDimitry Andric   //   .tlsdesccall                       [R_AARCH64_TLSDESC_CALL]
5050b57cec5SDimitry Andric   //   blr     x1
5060b57cec5SDimitry Andric   // And it can optimized to:
5070b57cec5SDimitry Andric   //   movz    x0, #0x0, lsl #16
5080b57cec5SDimitry Andric   //   movk    x0, #0x10
5090b57cec5SDimitry Andric   //   nop
5100b57cec5SDimitry Andric   //   nop
5115ffd83dbSDimitry Andric   checkUInt(loc, val, 32, rel);
5120b57cec5SDimitry Andric 
5135ffd83dbSDimitry Andric   switch (rel.type) {
5140b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADD_LO12:
5150b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_CALL:
5160b57cec5SDimitry Andric     write32le(loc, 0xd503201f); // nop
5170b57cec5SDimitry Andric     return;
5180b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADR_PAGE21:
5190b57cec5SDimitry Andric     write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz
5200b57cec5SDimitry Andric     return;
5210b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_LD64_LO12:
5220b57cec5SDimitry Andric     write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk
5230b57cec5SDimitry Andric     return;
5240b57cec5SDimitry Andric   default:
5250b57cec5SDimitry Andric     llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
5260b57cec5SDimitry Andric   }
5270b57cec5SDimitry Andric }
5280b57cec5SDimitry Andric 
5295ffd83dbSDimitry Andric void AArch64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
5305ffd83dbSDimitry Andric                              uint64_t val) const {
5310b57cec5SDimitry Andric   // TLSDESC Global-Dynamic relocation are in the form:
5320b57cec5SDimitry Andric   //   adrp    x0, :tlsdesc:v             [R_AARCH64_TLSDESC_ADR_PAGE21]
5330b57cec5SDimitry Andric   //   ldr     x1, [x0, #:tlsdesc_lo12:v  [R_AARCH64_TLSDESC_LD64_LO12]
5340b57cec5SDimitry Andric   //   add     x0, x0, :tlsdesc_los:v     [R_AARCH64_TLSDESC_ADD_LO12]
5350b57cec5SDimitry Andric   //   .tlsdesccall                       [R_AARCH64_TLSDESC_CALL]
5360b57cec5SDimitry Andric   //   blr     x1
5370b57cec5SDimitry Andric   // And it can optimized to:
5380b57cec5SDimitry Andric   //   adrp    x0, :gottprel:v
5390b57cec5SDimitry Andric   //   ldr     x0, [x0, :gottprel_lo12:v]
5400b57cec5SDimitry Andric   //   nop
5410b57cec5SDimitry Andric   //   nop
5420b57cec5SDimitry Andric 
5435ffd83dbSDimitry Andric   switch (rel.type) {
5440b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADD_LO12:
5450b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_CALL:
5460b57cec5SDimitry Andric     write32le(loc, 0xd503201f); // nop
5470b57cec5SDimitry Andric     break;
5480b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_ADR_PAGE21:
5490b57cec5SDimitry Andric     write32le(loc, 0x90000000); // adrp
5505ffd83dbSDimitry Andric     relocateNoSym(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val);
5510b57cec5SDimitry Andric     break;
5520b57cec5SDimitry Andric   case R_AARCH64_TLSDESC_LD64_LO12:
5530b57cec5SDimitry Andric     write32le(loc, 0xf9400000); // ldr
5545ffd83dbSDimitry Andric     relocateNoSym(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val);
5550b57cec5SDimitry Andric     break;
5560b57cec5SDimitry Andric   default:
5570b57cec5SDimitry Andric     llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
5580b57cec5SDimitry Andric   }
5590b57cec5SDimitry Andric }
5600b57cec5SDimitry Andric 
5615ffd83dbSDimitry Andric void AArch64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
5625ffd83dbSDimitry Andric                              uint64_t val) const {
5635ffd83dbSDimitry Andric   checkUInt(loc, val, 32, rel);
5640b57cec5SDimitry Andric 
5655ffd83dbSDimitry Andric   if (rel.type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
5660b57cec5SDimitry Andric     // Generate MOVZ.
5670b57cec5SDimitry Andric     uint32_t regNo = read32le(loc) & 0x1f;
5680b57cec5SDimitry Andric     write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5));
5690b57cec5SDimitry Andric     return;
5700b57cec5SDimitry Andric   }
5715ffd83dbSDimitry Andric   if (rel.type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
5720b57cec5SDimitry Andric     // Generate MOVK.
5730b57cec5SDimitry Andric     uint32_t regNo = read32le(loc) & 0x1f;
5740b57cec5SDimitry Andric     write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5));
5750b57cec5SDimitry Andric     return;
5760b57cec5SDimitry Andric   }
5770b57cec5SDimitry Andric   llvm_unreachable("invalid relocation for TLS IE to LE relaxation");
5780b57cec5SDimitry Andric }
5790b57cec5SDimitry Andric 
5800b57cec5SDimitry Andric // AArch64 may use security features in variant PLT sequences. These are:
5810b57cec5SDimitry Andric // Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target
5820b57cec5SDimitry Andric // Indicator (BTI) introduced in armv8.5-a. The additional instructions used
5830b57cec5SDimitry Andric // in the variant Plt sequences are encoded in the Hint space so they can be
5840b57cec5SDimitry Andric // deployed on older architectures, which treat the instructions as a nop.
5850b57cec5SDimitry Andric // PAC and BTI can be combined leading to the following combinations:
5860b57cec5SDimitry Andric // writePltHeader
5870b57cec5SDimitry Andric // writePltHeaderBti (no PAC Header needed)
5880b57cec5SDimitry Andric // writePlt
5890b57cec5SDimitry Andric // writePltBti (BTI only)
5900b57cec5SDimitry Andric // writePltPac (PAC only)
5910b57cec5SDimitry Andric // writePltBtiPac (BTI and PAC)
5920b57cec5SDimitry Andric //
5930b57cec5SDimitry Andric // When PAC is enabled the dynamic loader encrypts the address that it places
5940b57cec5SDimitry Andric // in the .got.plt using the pacia1716 instruction which encrypts the value in
5950b57cec5SDimitry Andric // x17 using the modifier in x16. The static linker places autia1716 before the
5960b57cec5SDimitry Andric // indirect branch to x17 to authenticate the address in x17 with the modifier
5970b57cec5SDimitry Andric // in x16. This makes it more difficult for an attacker to modify the value in
5980b57cec5SDimitry Andric // the .got.plt.
5990b57cec5SDimitry Andric //
6000b57cec5SDimitry Andric // When BTI is enabled all indirect branches must land on a bti instruction.
6010b57cec5SDimitry Andric // The static linker must place a bti instruction at the start of any PLT entry
6020b57cec5SDimitry Andric // that may be the target of an indirect branch. As the PLT entries call the
6030b57cec5SDimitry Andric // lazy resolver indirectly this must have a bti instruction at start. In
6040b57cec5SDimitry Andric // general a bti instruction is not needed for a PLT entry as indirect calls
6050b57cec5SDimitry Andric // are resolved to the function address and not the PLT entry for the function.
6060b57cec5SDimitry Andric // There are a small number of cases where the PLT address can escape, such as
6070b57cec5SDimitry Andric // taking the address of a function or ifunc via a non got-generating
6080b57cec5SDimitry Andric // relocation, and a shared library refers to that symbol.
6090b57cec5SDimitry Andric //
6100b57cec5SDimitry Andric // We use the bti c variant of the instruction which permits indirect branches
6110b57cec5SDimitry Andric // (br) via x16/x17 and indirect function calls (blr) via any register. The ABI
6120b57cec5SDimitry Andric // guarantees that all indirect branches from code requiring BTI protection
6130b57cec5SDimitry Andric // will go via x16/x17
6140b57cec5SDimitry Andric 
6150b57cec5SDimitry Andric namespace {
6160b57cec5SDimitry Andric class AArch64BtiPac final : public AArch64 {
6170b57cec5SDimitry Andric public:
6180b57cec5SDimitry Andric   AArch64BtiPac();
6190b57cec5SDimitry Andric   void writePltHeader(uint8_t *buf) const override;
620480093f4SDimitry Andric   void writePlt(uint8_t *buf, const Symbol &sym,
621480093f4SDimitry Andric                 uint64_t pltEntryAddr) const override;
6220b57cec5SDimitry Andric 
6230b57cec5SDimitry Andric private:
6240b57cec5SDimitry Andric   bool btiHeader; // bti instruction needed in PLT Header
6250b57cec5SDimitry Andric   bool btiEntry;  // bti instruction needed in PLT Entry
6260b57cec5SDimitry Andric   bool pacEntry;  // autia1716 instruction needed in PLT Entry
6270b57cec5SDimitry Andric };
6280b57cec5SDimitry Andric } // namespace
6290b57cec5SDimitry Andric 
6300b57cec5SDimitry Andric AArch64BtiPac::AArch64BtiPac() {
6310b57cec5SDimitry Andric   btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI);
6320b57cec5SDimitry Andric   // A BTI (Branch Target Indicator) Plt Entry is only required if the
6330b57cec5SDimitry Andric   // address of the PLT entry can be taken by the program, which permits an
6340b57cec5SDimitry Andric   // indirect jump to the PLT entry. This can happen when the address
6350b57cec5SDimitry Andric   // of the PLT entry for a function is canonicalised due to the address of
6360b57cec5SDimitry Andric   // the function in an executable being taken by a shared library.
6370b57cec5SDimitry Andric   // FIXME: There is a potential optimization to omit the BTI if we detect
6380b57cec5SDimitry Andric   // that the address of the PLT entry isn't taken.
6395ffd83dbSDimitry Andric   // The PAC PLT entries require dynamic loader support and this isn't known
6405ffd83dbSDimitry Andric   // from properties in the objects, so we use the command line flag.
6410b57cec5SDimitry Andric   btiEntry = btiHeader && !config->shared;
6425ffd83dbSDimitry Andric   pacEntry = config->zPacPlt;
6430b57cec5SDimitry Andric 
644480093f4SDimitry Andric   if (btiEntry || pacEntry) {
6450b57cec5SDimitry Andric     pltEntrySize = 24;
646480093f4SDimitry Andric     ipltEntrySize = 24;
647480093f4SDimitry Andric   }
6480b57cec5SDimitry Andric }
6490b57cec5SDimitry Andric 
6500b57cec5SDimitry Andric void AArch64BtiPac::writePltHeader(uint8_t *buf) const {
6510b57cec5SDimitry Andric   const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
6520b57cec5SDimitry Andric   const uint8_t pltData[] = {
6530b57cec5SDimitry Andric       0xf0, 0x7b, 0xbf, 0xa9, // stp    x16, x30, [sp,#-16]!
6540b57cec5SDimitry Andric       0x10, 0x00, 0x00, 0x90, // adrp   x16, Page(&(.plt.got[2]))
6550b57cec5SDimitry Andric       0x11, 0x02, 0x40, 0xf9, // ldr    x17, [x16, Offset(&(.plt.got[2]))]
6560b57cec5SDimitry Andric       0x10, 0x02, 0x00, 0x91, // add    x16, x16, Offset(&(.plt.got[2]))
6570b57cec5SDimitry Andric       0x20, 0x02, 0x1f, 0xd6, // br     x17
6580b57cec5SDimitry Andric       0x1f, 0x20, 0x03, 0xd5, // nop
6590b57cec5SDimitry Andric       0x1f, 0x20, 0x03, 0xd5  // nop
6600b57cec5SDimitry Andric   };
6610b57cec5SDimitry Andric   const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
6620b57cec5SDimitry Andric 
6630b57cec5SDimitry Andric   uint64_t got = in.gotPlt->getVA();
6640b57cec5SDimitry Andric   uint64_t plt = in.plt->getVA();
6650b57cec5SDimitry Andric 
6660b57cec5SDimitry Andric   if (btiHeader) {
6670b57cec5SDimitry Andric     // PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C
6680b57cec5SDimitry Andric     // instruction.
6690b57cec5SDimitry Andric     memcpy(buf, btiData, sizeof(btiData));
6700b57cec5SDimitry Andric     buf += sizeof(btiData);
6710b57cec5SDimitry Andric     plt += sizeof(btiData);
6720b57cec5SDimitry Andric   }
6730b57cec5SDimitry Andric   memcpy(buf, pltData, sizeof(pltData));
6740b57cec5SDimitry Andric 
6755ffd83dbSDimitry Andric   relocateNoSym(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
6760b57cec5SDimitry Andric                 getAArch64Page(got + 16) - getAArch64Page(plt + 8));
6775ffd83dbSDimitry Andric   relocateNoSym(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
6785ffd83dbSDimitry Andric   relocateNoSym(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
6790b57cec5SDimitry Andric   if (!btiHeader)
6800b57cec5SDimitry Andric     // We didn't add the BTI c instruction so round out size with NOP.
6810b57cec5SDimitry Andric     memcpy(buf + sizeof(pltData), nopData, sizeof(nopData));
6820b57cec5SDimitry Andric }
6830b57cec5SDimitry Andric 
684480093f4SDimitry Andric void AArch64BtiPac::writePlt(uint8_t *buf, const Symbol &sym,
685480093f4SDimitry Andric                              uint64_t pltEntryAddr) const {
6860b57cec5SDimitry Andric   // The PLT entry is of the form:
6870b57cec5SDimitry Andric   // [btiData] addrInst (pacBr | stdBr) [nopData]
6880b57cec5SDimitry Andric   const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
6890b57cec5SDimitry Andric   const uint8_t addrInst[] = {
6900b57cec5SDimitry Andric       0x10, 0x00, 0x00, 0x90,  // adrp x16, Page(&(.plt.got[n]))
6910b57cec5SDimitry Andric       0x11, 0x02, 0x40, 0xf9,  // ldr  x17, [x16, Offset(&(.plt.got[n]))]
6920b57cec5SDimitry Andric       0x10, 0x02, 0x00, 0x91   // add  x16, x16, Offset(&(.plt.got[n]))
6930b57cec5SDimitry Andric   };
6940b57cec5SDimitry Andric   const uint8_t pacBr[] = {
6950b57cec5SDimitry Andric       0x9f, 0x21, 0x03, 0xd5,  // autia1716
6960b57cec5SDimitry Andric       0x20, 0x02, 0x1f, 0xd6   // br   x17
6970b57cec5SDimitry Andric   };
6980b57cec5SDimitry Andric   const uint8_t stdBr[] = {
6990b57cec5SDimitry Andric       0x20, 0x02, 0x1f, 0xd6,  // br   x17
7000b57cec5SDimitry Andric       0x1f, 0x20, 0x03, 0xd5   // nop
7010b57cec5SDimitry Andric   };
7020b57cec5SDimitry Andric   const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
7030b57cec5SDimitry Andric 
7040b57cec5SDimitry Andric   if (btiEntry) {
7050b57cec5SDimitry Andric     memcpy(buf, btiData, sizeof(btiData));
7060b57cec5SDimitry Andric     buf += sizeof(btiData);
7070b57cec5SDimitry Andric     pltEntryAddr += sizeof(btiData);
7080b57cec5SDimitry Andric   }
7090b57cec5SDimitry Andric 
710480093f4SDimitry Andric   uint64_t gotPltEntryAddr = sym.getGotPltVA();
7110b57cec5SDimitry Andric   memcpy(buf, addrInst, sizeof(addrInst));
7125ffd83dbSDimitry Andric   relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
7135ffd83dbSDimitry Andric                 getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
7145ffd83dbSDimitry Andric   relocateNoSym(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
7155ffd83dbSDimitry Andric   relocateNoSym(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
7160b57cec5SDimitry Andric 
7170b57cec5SDimitry Andric   if (pacEntry)
7180b57cec5SDimitry Andric     memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr));
7190b57cec5SDimitry Andric   else
7200b57cec5SDimitry Andric     memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr));
7210b57cec5SDimitry Andric   if (!btiEntry)
7220b57cec5SDimitry Andric     // We didn't add the BTI c instruction so round out size with NOP.
7230b57cec5SDimitry Andric     memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData));
7240b57cec5SDimitry Andric }
7250b57cec5SDimitry Andric 
7260b57cec5SDimitry Andric static TargetInfo *getTargetInfo() {
7270b57cec5SDimitry Andric   if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI |
7280b57cec5SDimitry Andric                              GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) {
7290b57cec5SDimitry Andric     static AArch64BtiPac t;
7300b57cec5SDimitry Andric     return &t;
7310b57cec5SDimitry Andric   }
7320b57cec5SDimitry Andric   static AArch64 t;
7330b57cec5SDimitry Andric   return &t;
7340b57cec5SDimitry Andric }
7350b57cec5SDimitry Andric 
7365ffd83dbSDimitry Andric TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
737