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 RtlImageNtHeaderEx( 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 (Directory >= SWAPD(NtHeader->OptionalHeader.NumberOfRvaAndSizes)) 293 return NULL; 294 295 Va = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].VirtualAddress); 296 if (Va == 0) 297 return NULL; 298 299 *Size = SWAPD(NtHeader->OptionalHeader.DataDirectory[Directory].Size); 300 301 if (MappedAsImage || Va < SWAPD(NtHeader->OptionalHeader.SizeOfHeaders)) 302 return (PVOID)((ULONG_PTR)BaseAddress + Va); 303 304 /* Image mapped as ordinary file, we must find raw pointer */ 305 return RtlImageRvaToVa(NtHeader, BaseAddress, Va, NULL); 306 } 307 308 /* 309 * @implemented 310 */ 311 PIMAGE_SECTION_HEADER 312 NTAPI 313 RtlImageRvaToSection( 314 PIMAGE_NT_HEADERS NtHeader, 315 PVOID BaseAddress, 316 ULONG Rva) 317 { 318 PIMAGE_SECTION_HEADER Section; 319 ULONG Va; 320 ULONG Count; 321 322 Count = SWAPW(NtHeader->FileHeader.NumberOfSections); 323 Section = IMAGE_FIRST_SECTION(NtHeader); 324 325 while (Count--) 326 { 327 Va = SWAPD(Section->VirtualAddress); 328 if ((Va <= Rva) && (Rva < Va + SWAPD(Section->SizeOfRawData))) 329 return Section; 330 Section++; 331 } 332 333 return NULL; 334 } 335 336 /* 337 * @implemented 338 */ 339 PVOID 340 NTAPI 341 RtlImageRvaToVa( 342 PIMAGE_NT_HEADERS NtHeader, 343 PVOID BaseAddress, 344 ULONG Rva, 345 PIMAGE_SECTION_HEADER *SectionHeader) 346 { 347 PIMAGE_SECTION_HEADER Section = NULL; 348 349 if (SectionHeader) 350 Section = *SectionHeader; 351 352 if ((Section == NULL) || 353 (Rva < SWAPD(Section->VirtualAddress)) || 354 (Rva >= SWAPD(Section->VirtualAddress) + SWAPD(Section->SizeOfRawData))) 355 { 356 Section = RtlImageRvaToSection(NtHeader, BaseAddress, Rva); 357 if (Section == NULL) 358 return NULL; 359 360 if (SectionHeader) 361 *SectionHeader = Section; 362 } 363 364 return (PVOID)((ULONG_PTR)BaseAddress + Rva + 365 (ULONG_PTR)SWAPD(Section->PointerToRawData) - 366 (ULONG_PTR)SWAPD(Section->VirtualAddress)); 367 } 368 369 PIMAGE_BASE_RELOCATION 370 NTAPI 371 LdrProcessRelocationBlockLongLong( 372 IN ULONG_PTR Address, 373 IN ULONG Count, 374 IN PUSHORT TypeOffset, 375 IN LONGLONG Delta) 376 { 377 SHORT Offset; 378 USHORT Type; 379 ULONG i; 380 PUSHORT ShortPtr; 381 PULONG LongPtr; 382 PULONGLONG LongLongPtr; 383 384 for (i = 0; i < Count; i++) 385 { 386 Offset = SWAPW(*TypeOffset) & 0xFFF; 387 Type = SWAPW(*TypeOffset) >> 12; 388 ShortPtr = (PUSHORT)(RVA(Address, Offset)); 389 /* 390 * Don't relocate within the relocation section itself. 391 * GCC/LD generates sometimes relocation records for the relocation section. 392 * This is a bug in GCC/LD. 393 * Fix for it disabled, since it was only in ntoskrnl and not in ntdll 394 */ 395 /* 396 if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir || 397 (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd) 398 {*/ 399 switch (Type) 400 { 401 /* case IMAGE_REL_BASED_SECTION : */ 402 /* case IMAGE_REL_BASED_REL32 : */ 403 case IMAGE_REL_BASED_ABSOLUTE: 404 break; 405 406 case IMAGE_REL_BASED_HIGH: 407 *ShortPtr = HIWORD(MAKELONG(0, *ShortPtr) + (Delta & 0xFFFFFFFF)); 408 break; 409 410 case IMAGE_REL_BASED_LOW: 411 *ShortPtr = SWAPW(*ShortPtr) + LOWORD(Delta & 0xFFFF); 412 break; 413 414 case IMAGE_REL_BASED_HIGHLOW: 415 LongPtr = (PULONG)RVA(Address, Offset); 416 *LongPtr = SWAPD(*LongPtr) + (Delta & 0xFFFFFFFF); 417 break; 418 419 case IMAGE_REL_BASED_DIR64: 420 LongLongPtr = (PUINT64)RVA(Address, Offset); 421 *LongLongPtr = SWAPQ(*LongLongPtr) + Delta; 422 break; 423 424 case IMAGE_REL_BASED_HIGHADJ: 425 case IMAGE_REL_BASED_MIPS_JMPADDR: 426 default: 427 DPRINT1("Unknown/unsupported fixup type %hu.\n", Type); 428 DPRINT1("Address %p, Current %u, Count %u, *TypeOffset %x\n", 429 (PVOID)Address, i, Count, SWAPW(*TypeOffset)); 430 return (PIMAGE_BASE_RELOCATION)NULL; 431 } 432 433 TypeOffset++; 434 } 435 436 return (PIMAGE_BASE_RELOCATION)TypeOffset; 437 } 438 439 ULONG 440 NTAPI 441 LdrRelocateImage( 442 IN PVOID BaseAddress, 443 IN PCCH LoaderName, 444 IN ULONG Success, 445 IN ULONG Conflict, 446 IN ULONG Invalid) 447 { 448 return LdrRelocateImageWithBias(BaseAddress, 0, LoaderName, Success, Conflict, Invalid); 449 } 450 451 ULONG 452 NTAPI 453 LdrRelocateImageWithBias( 454 IN PVOID BaseAddress, 455 IN LONGLONG AdditionalBias, 456 IN PCCH LoaderName, 457 IN ULONG Success, 458 IN ULONG Conflict, 459 IN ULONG Invalid) 460 { 461 PIMAGE_NT_HEADERS NtHeaders; 462 PIMAGE_DATA_DIRECTORY RelocationDDir; 463 PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd; 464 ULONG Count; 465 ULONG_PTR Address; 466 PUSHORT TypeOffset; 467 LONGLONG Delta; 468 469 NtHeaders = RtlImageNtHeader(BaseAddress); 470 471 if (NtHeaders == NULL) 472 return Invalid; 473 474 if (SWAPW(NtHeaders->FileHeader.Characteristics) & IMAGE_FILE_RELOCS_STRIPPED) 475 { 476 return Conflict; 477 } 478 479 RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 480 481 if (SWAPD(RelocationDDir->VirtualAddress) == 0 || SWAPD(RelocationDDir->Size) == 0) 482 { 483 return Success; 484 } 485 486 Delta = (ULONG_PTR)BaseAddress - SWAPD(NtHeaders->OptionalHeader.ImageBase) + AdditionalBias; 487 RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseAddress + SWAPD(RelocationDDir->VirtualAddress)); 488 RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + SWAPD(RelocationDDir->Size)); 489 490 while (RelocationDir < RelocationEnd && 491 SWAPW(RelocationDir->SizeOfBlock) > 0) 492 { 493 Count = (SWAPW(RelocationDir->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); 494 Address = (ULONG_PTR)RVA(BaseAddress, SWAPD(RelocationDir->VirtualAddress)); 495 TypeOffset = (PUSHORT)(RelocationDir + 1); 496 497 RelocationDir = LdrProcessRelocationBlockLongLong(Address, 498 Count, 499 TypeOffset, 500 Delta); 501 502 if (RelocationDir == NULL) 503 { 504 DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n"); 505 return Invalid; 506 } 507 } 508 509 return Success; 510 } 511 512 /* EOF */ 513