1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/rtl/image.c 5 * PURPOSE: Image handling functions 6 * Relocate functions were previously located in 7 * ntoskrnl/ldr/loader.c and 8 * dll/ntdll/ldr/utils.c files 9 * PROGRAMMER: Eric Kohl + original authors from loader.c and utils.c file 10 * Aleksey Bragin 11 */ 12 13 /* INCLUDES *****************************************************************/ 14 15 #include <rtl.h> 16 17 #define NDEBUG 18 #include <debug.h> 19 20 #define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m))) 21 22 /* FUNCTIONS *****************************************************************/ 23 24 FORCEINLINE 25 USHORT 26 ChkSum(ULONG Sum, PUSHORT Src, ULONG Len) 27 { 28 ULONG i; 29 30 for (i=0; i<Len; i++) 31 { 32 /* Sum up the current word */ 33 Sum += Src[i]; 34 35 /* Sum up everything above the low word as a carry */ 36 Sum = (Sum & 0xFFFF) + (Sum >> 16); 37 } 38 39 /* Apply carry one more time and clamp to the USHORT */ 40 return (Sum + (Sum >> 16)) & 0xFFFF; 41 } 42 43 BOOLEAN 44 NTAPI 45 LdrVerifyMappedImageMatchesChecksum( 46 IN PVOID BaseAddress, 47 IN SIZE_T ImageSize, 48 IN ULONG FileLength) 49 { 50 #if 0 51 PIMAGE_NT_HEADERS Header; 52 PUSHORT Ptr; 53 ULONG Sum; 54 ULONG CalcSum; 55 ULONG HeaderSum; 56 ULONG i; 57 58 // HACK: Ignore calls with ImageSize=0. Should be fixed by new MM. 59 if (ImageSize == 0) return TRUE; 60 61 /* Get NT header to check if it's an image at all */ 62 Header = RtlImageNtHeader(BaseAddress); 63 if (!Header) return FALSE; 64 65 /* Get checksum to match */ 66 HeaderSum = Header->OptionalHeader.CheckSum; 67 68 /* Zero checksum seems to be accepted */ 69 if (HeaderSum == 0) return TRUE; 70 71 /* Calculate the checksum */ 72 Sum = 0; 73 Ptr = (PUSHORT) BaseAddress; 74 for (i = 0; i < ImageSize / sizeof (USHORT); i++) 75 { 76 Sum += (ULONG)*Ptr; 77 if (HIWORD(Sum) != 0) 78 { 79 Sum = LOWORD(Sum) + HIWORD(Sum); 80 } 81 Ptr++; 82 } 83 84 if (ImageSize & 1) 85 { 86 Sum += (ULONG)*((PUCHAR)Ptr); 87 if (HIWORD(Sum) != 0) 88 { 89 Sum = LOWORD(Sum) + HIWORD(Sum); 90 } 91 } 92 93 CalcSum = (USHORT)(LOWORD(Sum) + HIWORD(Sum)); 94 95 /* Subtract image checksum from calculated checksum. */ 96 /* fix low word of checksum */ 97 if (LOWORD(CalcSum) >= LOWORD(HeaderSum)) 98 { 99 CalcSum -= LOWORD(HeaderSum); 100 } 101 else 102 { 103 CalcSum = ((LOWORD(CalcSum) - LOWORD(HeaderSum)) & 0xFFFF) - 1; 104 } 105 106 /* Fix high word of checksum */ 107 if (LOWORD(CalcSum) >= HIWORD(HeaderSum)) 108 { 109 CalcSum -= HIWORD(HeaderSum); 110 } 111 else 112 { 113 CalcSum = ((LOWORD(CalcSum) - HIWORD(HeaderSum)) & 0xFFFF) - 1; 114 } 115 116 /* Add file length */ 117 CalcSum += ImageSize; 118 119 if (CalcSum != HeaderSum) 120 DPRINT1("Image %p checksum mismatches! 0x%x != 0x%x, ImageSize %x, FileLen %x\n", BaseAddress, CalcSum, HeaderSum, ImageSize, FileLength); 121 122 return (BOOLEAN)(CalcSum == HeaderSum); 123 #else 124 /* 125 * FIXME: Warning, this violates the PE standard and makes ReactOS drivers 126 * and other system code when normally on Windows they would not, since 127 * we do not write the checksum in them. 128 * Our compilers should be made to write out the checksum and this function 129 * should be enabled as to reject badly checksummed code. 130 */ 131 return TRUE; 132 #endif 133 } 134 135 /* 136 * @implemented 137 * @note This needs SEH (See https://jira.reactos.org/browse/CORE-14857) 138 */ 139 NTSTATUS 140 NTAPI 141 RtlImageNtHeaderEx( 142 _In_ ULONG Flags, 143 _In_ PVOID Base, 144 _In_ ULONG64 Size, 145 _Out_ PIMAGE_NT_HEADERS *OutHeaders) 146 { 147 PIMAGE_NT_HEADERS NtHeaders; 148 PIMAGE_DOS_HEADER DosHeader; 149 BOOLEAN WantsRangeCheck; 150 ULONG NtHeaderOffset; 151 152 /* You must want NT Headers, no? */ 153 if (OutHeaders == NULL) 154 { 155 DPRINT1("OutHeaders is NULL\n"); 156 return STATUS_INVALID_PARAMETER; 157 } 158 159 /* Assume failure */ 160 *OutHeaders = NULL; 161 162 /* Validate Flags */ 163 if (Flags & ~RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK) 164 { 165 DPRINT1("Invalid flags: 0x%lx\n", Flags); 166 return STATUS_INVALID_PARAMETER; 167 } 168 169 /* Validate base */ 170 if ((Base == NULL) || (Base == (PVOID)-1)) 171 { 172 DPRINT1("Invalid base address: %p\n", Base); 173 return STATUS_INVALID_PARAMETER; 174 } 175 176 /* Check if the caller wants range checks */ 177 WantsRangeCheck = !(Flags & RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK); 178 if (WantsRangeCheck) 179 { 180 /* Make sure the image size is at least big enough for the DOS header */ 181 if (Size < sizeof(IMAGE_DOS_HEADER)) 182 { 183 DPRINT1("Size too small\n"); 184 return STATUS_INVALID_IMAGE_FORMAT; 185 } 186 } 187 188 /* Check if the DOS Signature matches */ 189 DosHeader = Base; 190 if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) 191 { 192 /* Not a valid COFF */ 193 DPRINT1("Invalid image DOS signature!\n"); 194 return STATUS_INVALID_IMAGE_FORMAT; 195 } 196 197 /* Get the offset to the NT headers (and copy from LONG to ULONG) */ 198 NtHeaderOffset = DosHeader->e_lfanew; 199 200 /* The offset must not be larger than 256MB, as a hard-coded check. 201 In Windows this check is only done in user mode, not in kernel mode, 202 but it shouldn't harm to have it anyway. Note that without this check, 203 other overflow checks would become necessary! */ 204 if (NtHeaderOffset >= (256 * 1024 * 1024)) 205 { 206 /* Fail */ 207 DPRINT1("NT headers offset is larger than 256MB!\n"); 208 return STATUS_INVALID_IMAGE_FORMAT; 209 } 210 211 /* Check if the caller wants validation */ 212 if (WantsRangeCheck) 213 { 214 /* Make sure the file header fits into the size */ 215 if ((NtHeaderOffset + 216 RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS, FileHeader)) >= Size) 217 { 218 /* Fail */ 219 DPRINT1("NT headers beyond image size!\n"); 220 return STATUS_INVALID_IMAGE_FORMAT; 221 } 222 } 223 224 /* Now get a pointer to the NT Headers */ 225 NtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)Base + NtHeaderOffset); 226 227 /* Check if the mapping is in user space */ 228 if (Base <= MmHighestUserAddress) 229 { 230 /* Make sure we don't overflow into kernel space */ 231 if ((PVOID)(NtHeaders + 1) > MmHighestUserAddress) 232 { 233 DPRINT1("Image overflows from user space into kernel space!\n"); 234 return STATUS_INVALID_IMAGE_FORMAT; 235 } 236 } 237 238 /* Verify the PE Signature */ 239 if (NtHeaders->Signature != IMAGE_NT_SIGNATURE) 240 { 241 /* Fail */ 242 DPRINT1("Invalid image NT signature!\n"); 243 return STATUS_INVALID_IMAGE_FORMAT; 244 } 245 246 /* Now return success and the NT header */ 247 *OutHeaders = NtHeaders; 248 return STATUS_SUCCESS; 249 } 250 251 /* 252 * @implemented 253 */ 254 PIMAGE_NT_HEADERS 255 NTAPI 256 RtlImageNtHeader(IN PVOID Base) 257 { 258 PIMAGE_NT_HEADERS NtHeader; 259 260 /* Call the new API */ 261 RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK, 262 Base, 263 0, 264 &NtHeader); 265 return NtHeader; 266 } 267 268 /* 269 * @implemented 270 */ 271 PVOID 272 NTAPI 273 RtlImageDirectoryEntryToData( 274 PVOID BaseAddress, 275 BOOLEAN MappedAsImage, 276 USHORT Directory, 277 PULONG Size) 278 { 279 PIMAGE_NT_HEADERS NtHeader; 280 ULONG Va; 281 282 /* Magic flag for non-mapped images. */ 283 if ((ULONG_PTR)BaseAddress & 1) 284 { 285 BaseAddress = (PVOID)((ULONG_PTR)BaseAddress & ~1); 286 MappedAsImage = FALSE; 287 } 288 289 NtHeader = RtlImageNtHeader(BaseAddress); 290 if (NtHeader == NULL) 291 return NULL; 292 293 if (Directory >= SWAPD(NtHeader->OptionalHeader.NumberOfRvaAndSizes)) 294 return NULL; 295 296 Va = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].VirtualAddress); 297 if (Va == 0) 298 return NULL; 299 300 *Size = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].Size); 301 302 if (MappedAsImage || Va < SWAPD(NtHeader->OptionalHeader.SizeOfHeaders)) 303 return (PVOID)((ULONG_PTR)BaseAddress + Va); 304 305 /* Image mapped as ordinary file, we must find raw pointer */ 306 return RtlImageRvaToVa(NtHeader, BaseAddress, Va, NULL); 307 } 308 309 /* 310 * @implemented 311 */ 312 PIMAGE_SECTION_HEADER 313 NTAPI 314 RtlImageRvaToSection( 315 PIMAGE_NT_HEADERS NtHeader, 316 PVOID BaseAddress, 317 ULONG Rva) 318 { 319 PIMAGE_SECTION_HEADER Section; 320 ULONG Va; 321 ULONG Count; 322 323 Count = SWAPW(NtHeader->FileHeader.NumberOfSections); 324 Section = IMAGE_FIRST_SECTION(NtHeader); 325 326 while (Count--) 327 { 328 Va = SWAPD(Section->VirtualAddress); 329 if ((Va <= Rva) && (Rva < Va + SWAPD(Section->SizeOfRawData))) 330 return Section; 331 Section++; 332 } 333 334 return NULL; 335 } 336 337 /* 338 * @implemented 339 */ 340 PVOID 341 NTAPI 342 RtlImageRvaToVa( 343 PIMAGE_NT_HEADERS NtHeader, 344 PVOID BaseAddress, 345 ULONG Rva, 346 PIMAGE_SECTION_HEADER *SectionHeader) 347 { 348 PIMAGE_SECTION_HEADER Section = NULL; 349 350 if (SectionHeader) 351 Section = *SectionHeader; 352 353 if ((Section == NULL) || 354 (Rva < SWAPD(Section->VirtualAddress)) || 355 (Rva >= SWAPD(Section->VirtualAddress) + SWAPD(Section->SizeOfRawData))) 356 { 357 Section = RtlImageRvaToSection(NtHeader, BaseAddress, Rva); 358 if (Section == NULL) 359 return NULL; 360 361 if (SectionHeader) 362 *SectionHeader = Section; 363 } 364 365 return (PVOID)((ULONG_PTR)BaseAddress + Rva + 366 (ULONG_PTR)SWAPD(Section->PointerToRawData) - 367 (ULONG_PTR)SWAPD(Section->VirtualAddress)); 368 } 369 370 PIMAGE_BASE_RELOCATION 371 NTAPI 372 LdrProcessRelocationBlockLongLong( 373 IN ULONG_PTR Address, 374 IN ULONG Count, 375 IN PUSHORT TypeOffset, 376 IN LONGLONG Delta) 377 { 378 SHORT Offset; 379 USHORT Type; 380 ULONG i; 381 PUSHORT ShortPtr; 382 PULONG LongPtr; 383 PULONGLONG LongLongPtr; 384 385 for (i = 0; i < Count; i++) 386 { 387 Offset = SWAPW(*TypeOffset) & 0xFFF; 388 Type = SWAPW(*TypeOffset) >> 12; 389 ShortPtr = (PUSHORT)(RVA(Address, Offset)); 390 /* 391 * Don't relocate within the relocation section itself. 392 * GCC/LD generates sometimes relocation records for the relocation section. 393 * This is a bug in GCC/LD. 394 * Fix for it disabled, since it was only in ntoskrnl and not in ntdll 395 */ 396 /* 397 if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir || 398 (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd) 399 {*/ 400 switch (Type) 401 { 402 /* case IMAGE_REL_BASED_SECTION : */ 403 /* case IMAGE_REL_BASED_REL32 : */ 404 case IMAGE_REL_BASED_ABSOLUTE: 405 break; 406 407 case IMAGE_REL_BASED_HIGH: 408 *ShortPtr = HIWORD(MAKELONG(0, *ShortPtr) + (Delta & 0xFFFFFFFF)); 409 break; 410 411 case IMAGE_REL_BASED_LOW: 412 *ShortPtr = SWAPW(*ShortPtr) + LOWORD(Delta & 0xFFFF); 413 break; 414 415 case IMAGE_REL_BASED_HIGHLOW: 416 LongPtr = (PULONG)RVA(Address, Offset); 417 *LongPtr = SWAPD(*LongPtr) + (Delta & 0xFFFFFFFF); 418 break; 419 420 case IMAGE_REL_BASED_DIR64: 421 LongLongPtr = (PUINT64)RVA(Address, Offset); 422 *LongLongPtr = SWAPQ(*LongLongPtr) + Delta; 423 break; 424 425 case IMAGE_REL_BASED_HIGHADJ: 426 case IMAGE_REL_BASED_MIPS_JMPADDR: 427 default: 428 DPRINT1("Unknown/unsupported fixup type %hu.\n", Type); 429 DPRINT1("Address %p, Current %u, Count %u, *TypeOffset %x\n", 430 (PVOID)Address, i, Count, SWAPW(*TypeOffset)); 431 return (PIMAGE_BASE_RELOCATION)NULL; 432 } 433 434 TypeOffset++; 435 } 436 437 return (PIMAGE_BASE_RELOCATION)TypeOffset; 438 } 439 440 ULONG 441 NTAPI 442 LdrRelocateImage( 443 IN PVOID BaseAddress, 444 IN PCCH LoaderName, 445 IN ULONG Success, 446 IN ULONG Conflict, 447 IN ULONG Invalid) 448 { 449 return LdrRelocateImageWithBias(BaseAddress, 0, LoaderName, Success, Conflict, Invalid); 450 } 451 452 ULONG 453 NTAPI 454 LdrRelocateImageWithBias( 455 IN PVOID BaseAddress, 456 IN LONGLONG AdditionalBias, 457 IN PCCH LoaderName, 458 IN ULONG Success, 459 IN ULONG Conflict, 460 IN ULONG Invalid) 461 { 462 PIMAGE_NT_HEADERS NtHeaders; 463 PIMAGE_DATA_DIRECTORY RelocationDDir; 464 PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd; 465 ULONG Count; 466 ULONG_PTR Address; 467 PUSHORT TypeOffset; 468 LONGLONG Delta; 469 470 NtHeaders = RtlImageNtHeader(BaseAddress); 471 472 if (NtHeaders == NULL) 473 return Invalid; 474 475 if (SWAPW(NtHeaders->FileHeader.Characteristics) & IMAGE_FILE_RELOCS_STRIPPED) 476 { 477 return Conflict; 478 } 479 480 RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 481 482 if (SWAPD(RelocationDDir->VirtualAddress) == 0 || SWAPD(RelocationDDir->Size) == 0) 483 { 484 return Success; 485 } 486 487 Delta = (ULONG_PTR)BaseAddress - SWAPD(NtHeaders->OptionalHeader.ImageBase) + AdditionalBias; 488 RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseAddress + SWAPD(RelocationDDir->VirtualAddress)); 489 RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + SWAPD(RelocationDDir->Size)); 490 491 while (RelocationDir < RelocationEnd && 492 SWAPW(RelocationDir->SizeOfBlock) > 0) 493 { 494 Count = (SWAPW(RelocationDir->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); 495 Address = (ULONG_PTR)RVA(BaseAddress, SWAPD(RelocationDir->VirtualAddress)); 496 TypeOffset = (PUSHORT)(RelocationDir + 1); 497 498 RelocationDir = LdrProcessRelocationBlockLongLong(Address, 499 Count, 500 TypeOffset, 501 Delta); 502 503 if (RelocationDir == NULL) 504 { 505 DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n"); 506 return Invalid; 507 } 508 } 509 510 return Success; 511 } 512 513 /* EOF */ 514