]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Source/C/GenFw/Elf64Convert.c
BaseTools/GenFw AARCH64: convert ADRP to ADR instructions if binary size allows it
[mirror_edk2.git] / BaseTools / Source / C / GenFw / Elf64Convert.c
index 024a2a0d53573e1b503d039617e8975e2c109b4d..944c94b8f8b404dccdc1e3676c9b034f87896ef0 100644 (file)
@@ -806,26 +806,59 @@ WriteSections64 (
           switch (ELF_R_TYPE(Rel->r_info)) {\r
 \r
           case R_AARCH64_ADR_PREL_PG_HI21:\r
-          case R_AARCH64_ADD_ABS_LO12_NC:\r
-          case R_AARCH64_LDST8_ABS_LO12_NC:\r
-          case R_AARCH64_LDST16_ABS_LO12_NC:\r
-          case R_AARCH64_LDST32_ABS_LO12_NC:\r
-          case R_AARCH64_LDST64_ABS_LO12_NC:\r
-          case R_AARCH64_LDST128_ABS_LO12_NC:\r
             //\r
             // AArch64 PG_H21 relocations are typically paired with ABS_LO12\r
             // relocations, where a PC-relative reference with +/- 4 GB range is\r
             // split into a relative high part and an absolute low part. Since\r
             // the absolute low part represents the offset into a 4 KB page, we\r
+            // either have to convert the ADRP into an ADR instruction, or we\r
+            // need to use a section alignment of at least 4 KB, so that the\r
+            // binary appears at a correct offset at runtime. In any case, we\r
             // have to make sure that the 4 KB relative offsets of both the\r
             // section containing the reference as well as the section to which\r
             // it refers have not been changed during PE/COFF conversion (i.e.,\r
             // in ScanSections64() above).\r
             //\r
+            if (mCoffAlignment < 0x1000) {\r
+              //\r
+              // Attempt to convert the ADRP into an ADR instruction.\r
+              // This is only possible if the symbol is within +/- 1 MB.\r
+              //\r
+              INT64 Offset;\r
+\r
+              // Decode the ADRP instruction\r
+              Offset = (INT32)((*(UINT32 *)Targ & 0xffffe0) << 8);\r
+              Offset = (Offset << (6 - 5)) | ((*(UINT32 *)Targ & 0x60000000) >> (29 - 12));\r
+\r
+              //\r
+              // ADRP offset is relative to the previous page boundary,\r
+              // whereas ADR offset is relative to the instruction itself.\r
+              // So fix up the offset so it points to the page containing\r
+              // the symbol.\r
+              //\r
+              Offset -= (UINTN)(Targ - mCoffFile) & 0xfff;\r
+\r
+              if (Offset < -0x100000 || Offset > 0xfffff) {\r
+                Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s  due to its size (> 1 MB), this module requires 4 KB section alignment.",\r
+                  mInImageName);\r
+                break;\r
+              }\r
+\r
+              // Re-encode the offset as an ADR instruction\r
+              *(UINT32 *)Targ &= 0x1000001f;\r
+              *(UINT32 *)Targ |= ((Offset & 0x1ffffc) << (5 - 2)) | ((Offset & 0x3) << 29);\r
+            }\r
+            /* fall through */\r
+\r
+          case R_AARCH64_ADD_ABS_LO12_NC:\r
+          case R_AARCH64_LDST8_ABS_LO12_NC:\r
+          case R_AARCH64_LDST16_ABS_LO12_NC:\r
+          case R_AARCH64_LDST32_ABS_LO12_NC:\r
+          case R_AARCH64_LDST64_ABS_LO12_NC:\r
+          case R_AARCH64_LDST128_ABS_LO12_NC:\r
             if (((SecShdr->sh_addr ^ SecOffset) & 0xfff) != 0 ||\r
-                ((SymShdr->sh_addr ^ mCoffSectionsOffset[Sym->st_shndx]) & 0xfff) != 0 ||\r
-                mCoffAlignment < 0x1000) {\r
-              Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s AARCH64 small code model requires 4 KB section alignment.",\r
+                ((SymShdr->sh_addr ^ mCoffSectionsOffset[Sym->st_shndx]) & 0xfff) != 0) {\r
+              Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s AARCH64 small code model requires identical ELF and PE/COFF section offsets modulo 4 KB.",\r
                 mInImageName);\r
               break;\r
             }\r