Relocation

nipper發表於2024-04-07

1. 為什麼需要relocation

  當一個執行程式所在的二進位制檔案需要從一個地方搬到另一個地方時,檔案中的靜態變數,函式識別符號等所在的地址就需要進行重定位

  將需要地址重定位操作的官方術語成為fixup。

2. Relocation 具體操作

  比如UEFI中使用的PE檔案,當某一個Driver需要從Flash中 搬/載入 到Volatile Memory(RAM)中時,需要將PE檔案中的Relocation table也更新一下,這樣才能確保執行跳轉,拿取靜態變數時,拿到reloaction之後的值。

  PE:檔案格式:upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg(關注SizeOfBaseRelocationTable及BaseRelocationTable)

3. 相關工具

  查詢dll檔案中的所有需要重定位的資訊(Reloaction table的資訊)的工具: VS Studio: dumpbin.exe XX.dll /RELOCATIONS

4. 示例:UEFI EDK2中PE檔案的reloaction code:

簡單來說時如下flow:

adjust = DestinationAddress/ImageAddress - PE.hdr.BaseAddress;
fixup[0,1,2,,,n] = BaseRelocationTable[1,2,,,SizeofBaseRelocation];
fixup[0,1,2,,,n] = fixup[0,1,2,,,,n]+adjust;
1014行計算 需要adjust的值。
1020行拿到 PE檔案中的SizeOfBaseRelocationTable和BaseRelocationTable。
1085迴圈遍歷RelocationTable,並進行Fixup。
/**
925    Applies relocation fixups to a PE/COFF image that was loaded with PeCoffLoaderLoadImage().
926  
927    If the DestinationAddress field of ImageContext is 0, then use the ImageAddress field of
928    ImageContext as the relocation base address.  Otherwise, use the DestinationAddress field
929    of ImageContext as the relocation base address.  The caller must allocate the relocation
930    fixup log buffer and fill in the FixupData field of ImageContext prior to calling this function.
931  
932    The ImageRead, Handle, PeCoffHeaderOffset,  IsTeImage, Machine, ImageType, ImageAddress,
933    ImageSize, DestinationAddress, RelocationsStripped, SectionAlignment, SizeOfHeaders,
934    DebugDirectoryEntryRva, EntryPoint, FixupDataSize, CodeView, PdbPointer, and FixupData of
935    the ImageContext structure must be valid prior to invoking this service.
936  
937    If ImageContext is NULL, then ASSERT().
938  
939    Note that if the platform does not maintain coherency between the instruction cache(s) and the data
940    cache(s) in hardware, then the caller is responsible for performing cache maintenance operations
941    prior to transferring control to a PE/COFF image that is loaded using this library.
942  
943    @param  ImageContext        The pointer to the image context structure that describes the PE/COFF
944                                image that is being relocated.
945  
946    @retval RETURN_SUCCESS      The PE/COFF image was relocated.
947                                Extended status information is in the ImageError field of ImageContext.
948    @retval RETURN_LOAD_ERROR   The image in not a valid PE/COFF image.
949                                Extended status information is in the ImageError field of ImageContext.
950    @retval RETURN_UNSUPPORTED  A relocation record type is not supported.
951                                Extended status information is in the ImageError field of ImageContext.
952  
953  **/
954  RETURN_STATUS
955  EFIAPI
956  PeCoffLoaderRelocateImage (
957    IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
958    )
959  {
960    RETURN_STATUS                        Status;
961    EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
962    EFI_IMAGE_DATA_DIRECTORY             *RelocDir;
963    UINT64                               Adjust;
964    EFI_IMAGE_BASE_RELOCATION            *RelocBaseOrg;
965    EFI_IMAGE_BASE_RELOCATION            *RelocBase;
966    EFI_IMAGE_BASE_RELOCATION            *RelocBaseEnd;
967    UINT16                               *Reloc;
968    UINT16                               *RelocEnd;
969    CHAR8                                *Fixup;
970    CHAR8                                *FixupBase;
971    UINT16                               *Fixup16;
972    UINT32                               *Fixup32;
973    UINT64                               *Fixup64;
974    CHAR8                                *FixupData;
975    PHYSICAL_ADDRESS                     BaseAddress;
976    UINT32                               NumberOfRvaAndSizes;
977    UINT32                               TeStrippedOffset;
978  
979    ASSERT (ImageContext != NULL);
980  
981    //
982    // Assume success
983    //
984    ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
985  
986    //
987    // If there are no relocation entries, then we are done
988    //
989    if (ImageContext->RelocationsStripped) {
990      // Applies additional environment specific actions to relocate fixups
991      // to a PE/COFF image if needed
992      PeCoffLoaderRelocateImageExtraAction (ImageContext);
993      return RETURN_SUCCESS;
994    }
995  
996    //
997    // If the destination address is not 0, use that rather than the
998    // image address as the relocation target.
999    //
1000    if (ImageContext->DestinationAddress != 0) {
1001      BaseAddress = ImageContext->DestinationAddress;
1002    } else {
1003      BaseAddress = ImageContext->ImageAddress;
1004    }
1005  
1006    if (!(ImageContext->IsTeImage)) {
1007      Hdr.Pe32         = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)ImageContext->ImageAddress + ImageContext->PeCoffHeaderOffset);
1008      TeStrippedOffset = 0;
1009  
1010      if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
1011        //
1012        // Use PE32 offset
1013        //
1014        Adjust = (UINT64)BaseAddress - Hdr.Pe32->OptionalHeader.ImageBase;
1015        if (Adjust != 0) {
1016          Hdr.Pe32->OptionalHeader.ImageBase = (UINT32)BaseAddress;
1017        }
1018  
1019        NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;
1020        RelocDir            = &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
1021      } else {
1022        //
1023        // Use PE32+ offset
1024        //
1025        Adjust = (UINT64)BaseAddress - Hdr.Pe32Plus->OptionalHeader.ImageBase;
1026        if (Adjust != 0) {
1027          Hdr.Pe32Plus->OptionalHeader.ImageBase = (UINT64)BaseAddress;
1028        }
1029  
1030        NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
1031        RelocDir            = &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
1032      }
1033  
1034      //
1035      // Find the relocation block
1036      // Per the PE/COFF spec, you can't assume that a given data directory
1037      // is present in the image. You have to check the NumberOfRvaAndSizes in
1038      // the optional header to verify a desired directory entry is there.
1039      //
1040      if ((NumberOfRvaAndSizes < EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC)) {
1041        RelocDir = NULL;
1042      }
1043    } else {
1044      Hdr.Te           = (EFI_TE_IMAGE_HEADER *)(UINTN)(ImageContext->ImageAddress);
1045      TeStrippedOffset = (UINT32)Hdr.Te->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER);
1046      Adjust           = (UINT64)(BaseAddress - (Hdr.Te->ImageBase + TeStrippedOffset));
1047      if (Adjust != 0) {
1048        Hdr.Te->ImageBase = (UINT64)(BaseAddress - TeStrippedOffset);
1049      }
1050  
1051      //
1052      // Find the relocation block
1053      //
1054      RelocDir = &Hdr.Te->DataDirectory[0];
1055    }
1056  
1057    if ((RelocDir != NULL) && (RelocDir->Size > 0)) {
1058      RelocBase    = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress, TeStrippedOffset);
1059      RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *)PeCoffLoaderImageAddress (
1060                                                    ImageContext,
1061                                                    RelocDir->VirtualAddress + RelocDir->Size - 1,
1062                                                    TeStrippedOffset
1063                                                    );
1064      if ((RelocBase == NULL) || (RelocBaseEnd == NULL) || ((UINTN)RelocBaseEnd < (UINTN)RelocBase)) {
1065        ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
1066        return RETURN_LOAD_ERROR;
1067      }
1068    } else {
1069      //
1070      // Set base and end to bypass processing below.
1071      //
1072      RelocBase = RelocBaseEnd = NULL;
1073    }
1074  
1075    RelocBaseOrg = RelocBase;
1076  
1077    //
1078    // If Adjust is not zero, then apply fix ups to the image
1079    //
1080    if (Adjust != 0) {
1081      //
1082      // Run the relocation information and apply the fixups
1083      //
1084      FixupData = ImageContext->FixupData;
1085      while ((UINTN)RelocBase < (UINTN)RelocBaseEnd) {
1086        Reloc = (UINT16 *)((CHAR8 *)RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
1087        //
1088        // Add check for RelocBase->SizeOfBlock field.
1089        //
1090        if (RelocBase->SizeOfBlock == 0) {
1091          ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
1092          return RETURN_LOAD_ERROR;
1093        }
1094  
1095        if ((UINTN)RelocBase > MAX_ADDRESS - RelocBase->SizeOfBlock) {
1096          ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
1097          return RETURN_LOAD_ERROR;
1098        }
1099  
1100        RelocEnd = (UINT16 *)((CHAR8 *)RelocBase + RelocBase->SizeOfBlock);
1101        if ((UINTN)RelocEnd > (UINTN)RelocBaseOrg + RelocDir->Size) {
1102          ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
1103          return RETURN_LOAD_ERROR;
1104        }
1105  
1106        FixupBase = PeCoffLoaderImageAddress (ImageContext, RelocBase->VirtualAddress, TeStrippedOffset);
1107        if (FixupBase == NULL) {
1108          ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
1109          return RETURN_LOAD_ERROR;
1110        }
1111  
1112        //
1113        // Run this relocation record
1114        //
1115        while ((UINTN)Reloc < (UINTN)RelocEnd) {
1116          Fixup = PeCoffLoaderImageAddress (ImageContext, RelocBase->VirtualAddress + (*Reloc & 0xFFF), TeStrippedOffset);
1117          if (Fixup == NULL) {
1118            ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
1119            return RETURN_LOAD_ERROR;
1120          }
1121  
1122          switch ((*Reloc) >> 12) {
1123            case EFI_IMAGE_REL_BASED_ABSOLUTE:
1124              break;
1125  
1126            case EFI_IMAGE_REL_BASED_HIGH:
1127              Fixup16  = (UINT16 *)Fixup;
1128              *Fixup16 = (UINT16)(*Fixup16 + ((UINT16)((UINT32)Adjust >> 16)));
1129              if (FixupData != NULL) {
1130                *(UINT16 *)FixupData = *Fixup16;
1131                FixupData            = FixupData + sizeof (UINT16);
1132              }
1133  
1134              break;
1135  
1136            case EFI_IMAGE_REL_BASED_LOW:
1137              Fixup16  = (UINT16 *)Fixup;
1138              *Fixup16 = (UINT16)(*Fixup16 + (UINT16)Adjust);
1139              if (FixupData != NULL) {
1140                *(UINT16 *)FixupData = *Fixup16;
1141                FixupData            = FixupData + sizeof (UINT16);
1142              }
1143  
1144              break;
1145  
1146            case EFI_IMAGE_REL_BASED_HIGHLOW:
1147              Fixup32  = (UINT32 *)Fixup;
1148              *Fixup32 = *Fixup32 + (UINT32)Adjust;
1149              if (FixupData != NULL) {
1150                FixupData            = ALIGN_POINTER (FixupData, sizeof (UINT32));
1151                *(UINT32 *)FixupData = *Fixup32;
1152                FixupData            = FixupData + sizeof (UINT32);
1153              }
1154  
1155              break;
1156  
1157            case EFI_IMAGE_REL_BASED_DIR64:
1158              Fixup64  = (UINT64 *)Fixup;
1159              *Fixup64 = *Fixup64 + (UINT64)Adjust;
1160              if (FixupData != NULL) {
1161                FixupData              = ALIGN_POINTER (FixupData, sizeof (UINT64));
1162                *(UINT64 *)(FixupData) = *Fixup64;
1163                FixupData              = FixupData + sizeof (UINT64);
1164              }
1165  
1166              break;
1167  
1168            default:
1169              //
1170              // The common code does not handle some of the stranger IPF relocations
1171              // PeCoffLoaderRelocateImageEx () adds support for these complex fixups
1172              // on IPF and is a No-Op on other architectures.
1173              //
1174              Status = PeCoffLoaderRelocateImageEx (Reloc, Fixup, &FixupData, Adjust);
1175              if (RETURN_ERROR (Status)) {
1176                ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
1177                return Status;
1178              }
1179          }
1180  
1181          //
1182          // Next relocation record
1183          //
1184          Reloc += 1;
1185        }
1186  
1187        //
1188        // Next reloc block
1189        //
1190        RelocBase = (EFI_IMAGE_BASE_RELOCATION *)RelocEnd;
1191      }
1192  
1193      ASSERT ((UINTN)FixupData <= (UINTN)ImageContext->FixupData + ImageContext->FixupDataSize);
1194  
1195      //
1196      // Adjust the EntryPoint to match the linked-to address
1197      //
1198      if (ImageContext->DestinationAddress != 0) {
1199        ImageContext->EntryPoint -= (UINT64)ImageContext->ImageAddress;
1200        ImageContext->EntryPoint += (UINT64)ImageContext->DestinationAddress;
1201      }
1202    }
1203  
1204    // Applies additional environment specific actions to relocate fixups
1205    // to a PE/COFF image if needed
1206    PeCoffLoaderRelocateImageExtraAction (ImageContext);
1207  
1208    return RETURN_SUCCESS;
1209  }
1210