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