Cygwin: mkimport: implement AArch64 +/-4GB relocations
authorRadek Bartoň <radek.barton@microsoft.com>
Sat, 19 Jul 2025 17:17:12 +0000 (19:17 +0200)
committerJeremy Drake <cygwin@jdrake.com>
Fri, 25 Jul 2025 23:20:02 +0000 (16:20 -0700)
Based on https://sourceware.org/pipermail/cygwin-patches/2025q3/014154.html
suggestion, this patch implements +/-4GB relocations for AArch64 in the mkimport
script by using adrp and ldr instructions. This change required update
in winsup/cygwin/mm/malloc_wrapper.cc where those instructions are
decoded to get target import address.

Signed-off-by: Radek Bartoň <radek.barton@microsoft.com>
winsup/cygwin/mm/malloc_wrapper.cc
winsup/cygwin/scripts/mkimport

index 863d3089cfff58350560d804b5038b47f138f6d3..9444cb4c84618e0585ff951810df93c8d632e884 100644 (file)
@@ -51,16 +51,32 @@ import_address (void *imp)
   __try
     {
 #if defined(__aarch64__)
-      // If opcode is an adr instruction.
-      uint32_t opcode = *(uint32_t *) imp;
-      if ((opcode & 0x9f000000) == 0x10000000)
+      /* If first three instructions of the imp are:
+          adrp x16, X
+          ldr x16, [x16, #:lo12:X]
+          br x16
+        References:
+          - https://www.scs.stanford.edu/~zyedidia/arm64/adrp.html
+          - https://www.scs.stanford.edu/~zyedidia/arm64/ldr_imm_gen.html
+          - https://www.scs.stanford.edu/~zyedidia/arm64/br.html
+        NOTE: This implementation assumes that the relocation table is made of
+        those specific AArch64 instructions as generated by the
+        winsup/cygwin/scripts/mkimport script. Please, keep it in sync. */
+      uint32_t opcode1 = *((uint32_t *) imp);
+      uint32_t opcode2 = *(((uint32_t *) imp) + 1);
+      uint32_t opcode3 = *(((uint32_t *) imp) + 2);
+      if (((opcode1 & 0x9f00001f) == 0x90000010) &&
+         ((opcode2 & 0xffc003ff) == 0xf9400210) &&
+         (opcode3 == 0xd61f0200))
        {
-         uint32_t immhi = (opcode >> 5) & 0x7ffff;
-         uint32_t immlo = (opcode >> 29) & 0x3;
-         int64_t sign_extend = (0l - (immhi >> 18)) << 21;
-         int64_t imm = sign_extend | (immhi << 2) | immlo;
-         uintptr_t jmpto = *(uintptr_t *) ((uint8_t *) imp + imm);
-         return (void *) jmpto;
+         uint32_t immhi = (opcode1 >> 5) & 0x7ffff;
+         uint32_t immlo = (opcode1 >> 29) & 0x3;
+         uint32_t imm12 = ((opcode2 >> 10) & 0xfff) * 8; // 64 bit scale
+         int64_t sign_extend = (0l - ((int64_t) immhi >> 32)) << 33; // sign extend from 33 to 64 bits
+         int64_t imm = sign_extend | (((immhi << 2) | immlo) << 12);
+         int64_t base = (int64_t) imp & ~0xfff;
+         uintptr_t* jmpto = (uintptr_t *) (base + imm + imm12);
+         return (void *) *jmpto;
        }
 #else
       if (*((uint16_t *) imp) == 0x25ff)
index 0c1bcafbf8b17d9a1a9a3fcb17a4bfa61e0d31f5..5583099bb12235b462105f30a594808e2e36bde6 100755 (executable)
@@ -73,8 +73,11 @@ EOF
        .extern $imp_sym
        .global $glob_sym
 $glob_sym:
-       adr x16, $imp_sym
-       ldr x16, [x16]
+       # NOTE: Using instructions that are used by MSVC and LLVM. Binutils are
+       # using adrp/add/ldr-0-offset though. Please, keep it in sync with
+       # import_address implementation in winsup/cygwin/mm/malloc_wrapper.cc.
+       adrp x16, $imp_sym
+       ldr x16, [x16, #:lo12:$imp_sym]
        br x16
 EOF
        } else {
This page took 0.036389 seconds and 5 git commands to generate.