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 */ 138 NTSTATUS 139 NTAPI 140 RtlpImageNtHeaderEx( 141 _In_ ULONG Flags, 142 _In_ PVOID Base, 143 _In_ ULONG64 Size, 144 _Out_ PIMAGE_NT_HEADERS *OutHeaders) 145 { 146 PIMAGE_NT_HEADERS NtHeaders; 147 PIMAGE_DOS_HEADER DosHeader; 148 BOOLEAN WantsRangeCheck; 149 ULONG NtHeaderOffset; 150 151 /* You must want NT Headers, no? */ 152 if (OutHeaders == NULL) 153 { 154 DPRINT1("OutHeaders is NULL\n"); 155 return STATUS_INVALID_PARAMETER; 156 } 157 158 /* Assume failure */ 159 *OutHeaders = NULL; 160 161 /* Validate Flags */ 162 if (Flags & ~RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK) 163 { 164 DPRINT1("Invalid flags: 0x%lx\n", Flags); 165 return STATUS_INVALID_PARAMETER; 166 } 167 168 /* Validate base */ 169 if ((Base == NULL) || (Base == (PVOID)-1)) 170 { 171 DPRINT1("Invalid base address: %p\n", Base); 172 return STATUS_INVALID_PARAMETER; 173 } 174 175 /* Check if the caller wants range checks */ 176 WantsRangeCheck = !(Flags & RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK); 177 if (WantsRangeCheck) 178 { 179 /* Make sure the image size is at least big enough for the DOS header */ 180 if (Size < sizeof(IMAGE_DOS_HEADER)) 181 { 182 DPRINT1("Size too small\n"); 183 return STATUS_INVALID_IMAGE_FORMAT; 184 } 185 } 186 187 /* Check if the DOS Signature matches */ 188 DosHeader = Base; 189 if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) 190 { 191 /* Not a valid COFF */ 192 DPRINT1("Invalid image DOS signature!\n"); 193 return STATUS_INVALID_IMAGE_FORMAT; 194 } 195 196 /* Get the offset to the NT headers (and copy from LONG to ULONG) */ 197 NtHeaderOffset = DosHeader->e_lfanew; 198 199 /* The offset must not be larger than 256MB, as a hard-coded check. 200 In Windows this check is only done in user mode, not in kernel mode, 201 but it shouldn't harm to have it anyway. Note that without this check, 202 other overflow checks would become necessary! */ 203 if (NtHeaderOffset >= (256 * 1024 * 1024)) 204 { 205 /* Fail */ 206 DPRINT1("NT headers offset is larger than 256MB!\n"); 207 return STATUS_INVALID_IMAGE_FORMAT; 208 } 209 210 /* Check if the caller wants validation */ 211 if (WantsRangeCheck) 212 { 213 /* Make sure the file header fits into the size */ 214 if ((NtHeaderOffset + 215 RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS, FileHeader)) >= Size) 216 { 217 /* Fail */ 218 DPRINT1("NT headers beyond image size!\n"); 219 return STATUS_INVALID_IMAGE_FORMAT; 220 } 221 } 222 223 /* Now get a pointer to the NT Headers */ 224 NtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)Base + NtHeaderOffset); 225 226 /* Check if the mapping is in user space */ 227 if (Base <= MmHighestUserAddress) 228 { 229 /* Make sure we don't overflow into kernel space */ 230 if ((PVOID)(NtHeaders + 1) > MmHighestUserAddress) 231 { 232 DPRINT1("Image overflows from user space into kernel space!\n"); 233 return STATUS_INVALID_IMAGE_FORMAT; 234 } 235 } 236 237 /* Verify the PE Signature */ 238 if (NtHeaders->Signature != IMAGE_NT_SIGNATURE) 239 { 240 /* Fail */ 241 DPRINT1("Invalid image NT signature!\n"); 242 return STATUS_INVALID_IMAGE_FORMAT; 243 } 244 245 /* Now return success and the NT header */ 246 *OutHeaders = NtHeaders; 247 return STATUS_SUCCESS; 248 } 249 250 /* 251 * @implemented 252 */ 253 PIMAGE_NT_HEADERS 254 NTAPI 255 RtlImageNtHeader(IN PVOID Base) 256 { 257 PIMAGE_NT_HEADERS NtHeader; 258 259 /* Call the new API */ 260 RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK, 261 Base, 262 0, 263 &NtHeader); 264 return NtHeader; 265 } 266 267 /* 268 * @implemented 269 */ 270 PVOID 271 NTAPI 272 RtlImageDirectoryEntryToData( 273 PVOID BaseAddress, 274 BOOLEAN MappedAsImage, 275 USHORT Directory, 276 PULONG Size) 277 { 278 PIMAGE_NT_HEADERS NtHeader; 279 ULONG Va; 280 281 /* Magic flag for non-mapped images. */ 282 if ((ULONG_PTR)BaseAddress & 1) 283 { 284 BaseAddress = (PVOID)((ULONG_PTR)BaseAddress & ~1); 285 MappedAsImage = FALSE; 286 } 287 288 NtHeader = RtlImageNtHeader(BaseAddress); 289 if (NtHeader == NULL) 290 return NULL; 291 292 if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) 293 { 294 PIMAGE_OPTIONAL_HEADER64 OptionalHeader = (PIMAGE_OPTIONAL_HEADER64)&NtHeader->OptionalHeader; 295 296 if (Directory >= SWAPD(OptionalHeader->NumberOfRvaAndSizes)) 297 return NULL; 298 299 Va = SWAPD(OptionalHeader->DataDirectory[Directory].VirtualAddress); 300 if (Va == 0) 301 return NULL; 302 303 *Size = SWAPD(OptionalHeader->DataDirectory[Directory].Size); 304 305 if (MappedAsImage || Va < SWAPD(OptionalHeader->SizeOfHeaders)) 306 return (PVOID)((ULONG_PTR)BaseAddress + Va); 307 } 308 else 309 { 310 PIMAGE_OPTIONAL_HEADER32 OptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&NtHeader->OptionalHeader; 311 312 if (Directory >= SWAPD(OptionalHeader->NumberOfRvaAndSizes)) 313 return NULL; 314 315 Va = SWAPD(OptionalHeader->DataDirectory[Directory].VirtualAddress); 316 if (Va == 0) 317 return NULL; 318 319 *Size = SWAPD(OptionalHeader->DataDirectory[Directory].Size); 320 321 if (MappedAsImage || Va < SWAPD(OptionalHeader->SizeOfHeaders)) 322 return (PVOID)((ULONG_PTR)BaseAddress + Va); 323 } 324 325 /* Image mapped as ordinary file, we must find raw pointer */ 326 return RtlImageRvaToVa(NtHeader, BaseAddress, Va, NULL); 327 } 328 329 /* 330 * @implemented 331 */ 332 PIMAGE_SECTION_HEADER 333 NTAPI 334 RtlImageRvaToSection( 335 PIMAGE_NT_HEADERS NtHeader, 336 PVOID BaseAddress, 337 ULONG Rva) 338 { 339 PIMAGE_SECTION_HEADER Section; 340 ULONG Va; 341 ULONG Count; 342 343 Count = SWAPW(NtHeader->FileHeader.NumberOfSections); 344 Section = IMAGE_FIRST_SECTION(NtHeader); 345 346 while (Count--) 347 { 348 Va = SWAPD(Section->VirtualAddress); 349 if ((Va <= Rva) && (Rva < Va + SWAPD(Section->SizeOfRawData))) 350 return Section; 351 Section++; 352 } 353 354 return NULL; 355 } 356 357 /* 358 * @implemented 359 */ 360 PVOID 361 NTAPI 362 RtlImageRvaToVa( 363 PIMAGE_NT_HEADERS NtHeader, 364 PVOID BaseAddress, 365 ULONG Rva, 366 PIMAGE_SECTION_HEADER *SectionHeader) 367 { 368 PIMAGE_SECTION_HEADER Section = NULL; 369 370 if (SectionHeader) 371 Section = *SectionHeader; 372 373 if ((Section == NULL) || 374 (Rva < SWAPD(Section->VirtualAddress)) || 375 (Rva >= SWAPD(Section->VirtualAddress) + SWAPD(Section->SizeOfRawData))) 376 { 377 Section = RtlImageRvaToSection(NtHeader, BaseAddress, Rva); 378 if (Section == NULL) 379 return NULL; 380 381 if (SectionHeader) 382 *SectionHeader = Section; 383 } 384 385 return (PVOID)((ULONG_PTR)BaseAddress + Rva + 386 (ULONG_PTR)SWAPD(Section->PointerToRawData) - 387 (ULONG_PTR)SWAPD(Section->VirtualAddress)); 388 } 389 390 PIMAGE_BASE_RELOCATION 391 NTAPI 392 LdrProcessRelocationBlockLongLong( 393 IN ULONG_PTR Address, 394 IN ULONG Count, 395 IN PUSHORT TypeOffset, 396 IN LONGLONG Delta) 397 { 398 SHORT Offset; 399 USHORT Type; 400 ULONG i; 401 PUSHORT ShortPtr; 402 PULONG LongPtr; 403 PULONGLONG LongLongPtr; 404 405 for (i = 0; i < Count; i++) 406 { 407 Offset = SWAPW(*TypeOffset) & 0xFFF; 408 Type = SWAPW(*TypeOffset) >> 12; 409 ShortPtr = (PUSHORT)(RVA(Address, Offset)); 410 /* 411 * Don't relocate within the relocation section itself. 412 * GCC/LD generates sometimes relocation records for the relocation section. 413 * This is a bug in GCC/LD. 414 * Fix for it disabled, since it was only in ntoskrnl and not in ntdll 415 */ 416 /* 417 if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir || 418 (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd) 419 {*/ 420 switch (Type) 421 { 422 /* case IMAGE_REL_BASED_SECTION : */ 423 /* case IMAGE_REL_BASED_REL32 : */ 424 case IMAGE_REL_BASED_ABSOLUTE: 425 break; 426 427 case IMAGE_REL_BASED_HIGH: 428 *ShortPtr = HIWORD(MAKELONG(0, *ShortPtr) + (Delta & 0xFFFFFFFF)); 429 break; 430 431 case IMAGE_REL_BASED_LOW: 432 *ShortPtr = SWAPW(*ShortPtr) + LOWORD(Delta & 0xFFFF); 433 break; 434 435 case IMAGE_REL_BASED_HIGHLOW: 436 LongPtr = (PULONG)RVA(Address, Offset); 437 *LongPtr = SWAPD(*LongPtr) + (Delta & 0xFFFFFFFF); 438 break; 439 440 case IMAGE_REL_BASED_DIR64: 441 LongLongPtr = (PUINT64)RVA(Address, Offset); 442 *LongLongPtr = SWAPQ(*LongLongPtr) + Delta; 443 break; 444 445 case IMAGE_REL_BASED_HIGHADJ: 446 case IMAGE_REL_BASED_MIPS_JMPADDR: 447 default: 448 DPRINT1("Unknown/unsupported fixup type %hu.\n", Type); 449 DPRINT1("Address %p, Current %u, Count %u, *TypeOffset %x\n", 450 (PVOID)Address, i, Count, SWAPW(*TypeOffset)); 451 return (PIMAGE_BASE_RELOCATION)NULL; 452 } 453 454 TypeOffset++; 455 } 456 457 return (PIMAGE_BASE_RELOCATION)TypeOffset; 458 } 459 460 ULONG 461 NTAPI 462 LdrRelocateImage( 463 _In_ PVOID BaseAddress, 464 _In_opt_ PCSTR LoaderName, 465 _In_ ULONG Success, 466 _In_ ULONG Conflict, 467 _In_ ULONG Invalid) 468 { 469 return LdrRelocateImageWithBias(BaseAddress, 0, LoaderName, Success, Conflict, Invalid); 470 } 471 472 ULONG 473 NTAPI 474 LdrRelocateImageWithBias( 475 _In_ PVOID BaseAddress, 476 _In_ LONGLONG AdditionalBias, 477 _In_opt_ PCSTR LoaderName, 478 _In_ ULONG Success, 479 _In_ ULONG Conflict, 480 _In_ ULONG Invalid) 481 { 482 PIMAGE_NT_HEADERS NtHeaders; 483 PIMAGE_DATA_DIRECTORY RelocationDDir; 484 PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd; 485 ULONG Count; 486 ULONG_PTR Address; 487 PUSHORT TypeOffset; 488 LONGLONG Delta; 489 490 UNREFERENCED_PARAMETER(LoaderName); 491 492 NtHeaders = RtlImageNtHeader(BaseAddress); 493 494 if (NtHeaders == NULL) 495 return Invalid; 496 497 if (SWAPW(NtHeaders->FileHeader.Characteristics) & IMAGE_FILE_RELOCS_STRIPPED) 498 { 499 return Conflict; 500 } 501 502 RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 503 504 if (SWAPD(RelocationDDir->VirtualAddress) == 0 || SWAPD(RelocationDDir->Size) == 0) 505 { 506 return Success; 507 } 508 509 Delta = (ULONG_PTR)BaseAddress - SWAPD(NtHeaders->OptionalHeader.ImageBase) + AdditionalBias; 510 RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseAddress + SWAPD(RelocationDDir->VirtualAddress)); 511 RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + SWAPD(RelocationDDir->Size)); 512 513 while (RelocationDir < RelocationEnd && 514 SWAPW(RelocationDir->SizeOfBlock) > 0) 515 { 516 Count = (SWAPW(RelocationDir->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); 517 Address = (ULONG_PTR)RVA(BaseAddress, SWAPD(RelocationDir->VirtualAddress)); 518 TypeOffset = (PUSHORT)(RelocationDir + 1); 519 520 RelocationDir = LdrProcessRelocationBlockLongLong(Address, 521 Count, 522 TypeOffset, 523 Delta); 524 525 if (RelocationDir == NULL) 526 { 527 DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n"); 528 return Invalid; 529 } 530 } 531 532 return Success; 533 } 534 535 /* EOF */ 536