Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

602 lines
17 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. ldrutil.c
  5. Abstract:
  6. This module implements utility functions used by the NT loader.
  7. It was forked from the ldrsnap.c source file since ldrsnap.c
  8. was growing enourmous.
  9. Author:
  10. Michael Grier (MGrier) 04-Apr-2001, derived mostly from
  11. Mike O'Leary (mikeol) 23-Mar-1990
  12. Revision History:
  13. --*/
  14. #define LDRDBG 0
  15. #include "ntos.h"
  16. #include <nt.h>
  17. #include <ntrtl.h>
  18. #include <nturtl.h>
  19. #include <ntpsapi.h>
  20. #include <heap.h>
  21. #include "ldrp.h"
  22. #include "sxstypes.h"
  23. #include <limits.h>
  24. #define DLL_EXTENSION L".DLL"
  25. #define DLL_REDIRECTION_LOCAL_SUFFIX L".Local"
  26. #define INVALID_HANDLE_VALUE ((HANDLE) ((LONG_PTR) -1))
  27. BOOLEAN LdrpBreakOnExceptions = FALSE;
  28. PLDR_DATA_TABLE_ENTRY
  29. LdrpAllocateDataTableEntry(
  30. IN PVOID DllBase
  31. )
  32. /*++
  33. Routine Description:
  34. This function allocates an entry in the loader data table. If the
  35. table is going to overflow, then a new table is allocated.
  36. Arguments:
  37. DllBase - Supplies the address of the base of the DLL Image.
  38. be added to the loader data table.
  39. Return Value:
  40. Returns the address of the allocated loader data table entry
  41. --*/
  42. {
  43. PLDR_DATA_TABLE_ENTRY Entry;
  44. PIMAGE_NT_HEADERS NtHeaders;
  45. NtHeaders = RtlImageNtHeader(DllBase);
  46. Entry = NULL;
  47. if ( NtHeaders ) {
  48. Entry = RtlAllocateHeap(LdrpHeap, MAKE_TAG( LDR_TAG ) | HEAP_ZERO_MEMORY, sizeof(*Entry));
  49. if ( Entry ) {
  50. Entry->DllBase = DllBase;
  51. Entry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
  52. Entry->TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
  53. }
  54. }
  55. return Entry;
  56. }
  57. VOID
  58. LdrpDeallocateDataTableEntry(
  59. IN PLDR_DATA_TABLE_ENTRY Entry
  60. )
  61. {
  62. if (Entry != NULL)
  63. RtlFreeHeap(LdrpHeap, 0, Entry);
  64. }
  65. VOID
  66. LdrpFinalizeAndDeallocateDataTableEntry(
  67. IN PLDR_DATA_TABLE_ENTRY Entry
  68. )
  69. {
  70. if (Entry != NULL) {
  71. if ((Entry->EntryPointActivationContext != NULL) &&
  72. (Entry->EntryPointActivationContext != INVALID_HANDLE_VALUE)) {
  73. RtlReleaseActivationContext(Entry->EntryPointActivationContext);
  74. Entry->EntryPointActivationContext = INVALID_HANDLE_VALUE;
  75. }
  76. if (Entry->FullDllName.Buffer != NULL) {
  77. LdrpFreeUnicodeString(&Entry->FullDllName);
  78. RtlInitEmptyUnicodeString(&Entry->FullDllName, NULL, 0);
  79. RtlInitEmptyUnicodeString(&Entry->BaseDllName, NULL, 0);
  80. }
  81. LdrpDeallocateDataTableEntry(Entry);
  82. }
  83. }
  84. NTSTATUS
  85. RtlComputePrivatizedDllName_U(
  86. IN PCUNICODE_STRING DllName,
  87. IN OUT PUNICODE_STRING NewDllNameUnderImageDir,
  88. IN OUT PUNICODE_STRING NewDllNameUnderLocalDir
  89. )
  90. /*++
  91. Routine Description:
  92. This function computes a fully qualified path to a DLL name. It takes
  93. the path of the current process and the base name from DllName and
  94. puts these together. DllName can have '\' or '/' as separator.
  95. Arguments:
  96. DllName - Points to a string that names the library file. This can be
  97. a fully qualified name or just a base name. We will parse for the base
  98. name (the portion after the last '\' or '/' char. Caller guarantees that
  99. DllName->Buffer is not a NULL pointer!
  100. NewDllName - Has fully qualified path based on GetModuleFileNameW(NULL...)
  101. and the base name from above.
  102. Return Value:
  103. NTSTATUS: Currently: STATUS_NO_MEMORY or STATUS_SUCCESS.
  104. --*/
  105. {
  106. LPWSTR p, pp, pp1, pp2;
  107. PWSTR Dot;
  108. LPWSTR pFullImageName;
  109. USHORT cbFullImageNameLength;
  110. USHORT cbFullImagePathLengthWithTrailingSlash, cbDllFileNameLengthWithTrailingNULL;
  111. USHORT cbDllNameUnderImageDir, cbDllNameUnderLocalDir;
  112. ULONG cbStringLength;
  113. PWSTR Cursor = NULL;
  114. NTSTATUS status = STATUS_SUCCESS;
  115. LPWSTR pDllNameUnderImageDir = NULL;
  116. LPWSTR pDllNameUnderLocalDir = NULL;
  117. LPCWSTR pBuf1 = NewDllNameUnderImageDir->Buffer;
  118. LPCWSTR pBuf2 = NewDllNameUnderLocalDir->Buffer;
  119. cbFullImageNameLength = NtCurrentPeb()->ProcessParameters->ImagePathName.Length;
  120. pFullImageName = (PWSTR)NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer;
  121. if (!(NtCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
  122. pFullImageName = (PWSTR)((PCHAR)pFullImageName + (ULONG_PTR)(NtCurrentPeb()->ProcessParameters));
  123. }
  124. ASSERT(pFullImageName != NULL);
  125. // Find the end of the EXE path (start of its base-name) in pp1.
  126. // Size1 is number of bytes;
  127. p = pFullImageName + cbFullImageNameLength/sizeof(WCHAR) - 1; // point to last character of this name
  128. pp1 = NULL;
  129. while (p != pFullImageName) {
  130. if (RTL_IS_PATH_SEPARATOR(*p)) {
  131. pp1 = p + 1;
  132. break;
  133. }
  134. p-- ;
  135. }
  136. // Find the basename portion of the DLL to be loaded in pp2 and the
  137. // last '.' character if present in the basename.
  138. pp2 = DllName->Buffer;
  139. Dot = NULL ;
  140. if (DllName->Length) {
  141. ASSERT(RTL_STRING_IS_NUL_TERMINATED(DllName)); // temporary debugging
  142. p = DllName->Buffer + (DllName->Length>>1) - 1; // point to last char
  143. while (p != DllName->Buffer) {
  144. if (*p == (WCHAR) '.') {
  145. if (!Dot) {
  146. Dot = p ;
  147. }
  148. }
  149. else {
  150. if ((*p == (WCHAR) '\\') || (*p == (WCHAR) '/')) {
  151. pp2 = p + 1;
  152. break;
  153. }
  154. }
  155. p--;
  156. }
  157. }
  158. // Create a fully qualified path to the DLL name (using pp1 and pp2)
  159. // Number of bytes (not including NULL or EXE/process folder)
  160. if (((pp1 - pFullImageName) * sizeof(WCHAR)) > ULONG_MAX) {
  161. DbgPrint("ntdll: wants more than ULONG_MAX bytes \n");
  162. goto Exit;
  163. }
  164. cbStringLength = (ULONG)((pp1 - pFullImageName) * sizeof(WCHAR));
  165. if ( cbStringLength > UNICODE_STRING_MAX_BYTES ) {
  166. status = STATUS_NAME_TOO_LONG;
  167. goto Exit;
  168. }
  169. cbFullImagePathLengthWithTrailingSlash = (USHORT)cbStringLength;
  170. // Number of bytes in base DLL name (including trailing null char)
  171. cbDllFileNameLengthWithTrailingNULL = (USHORT)(DllName->Length + sizeof(WCHAR) - ((pp2 - DllName->Buffer) * sizeof(WCHAR)));
  172. cbStringLength = cbFullImagePathLengthWithTrailingSlash
  173. + cbDllFileNameLengthWithTrailingNULL;
  174. // Allocate room for L".DLL"
  175. if (Dot == NULL)
  176. cbStringLength += sizeof(DLL_EXTENSION) - sizeof(WCHAR);
  177. if ( cbStringLength > UNICODE_STRING_MAX_BYTES ) {
  178. status = STATUS_NAME_TOO_LONG;
  179. goto Exit;
  180. }
  181. cbDllNameUnderImageDir = (USHORT)cbStringLength;
  182. if (cbDllNameUnderImageDir > NewDllNameUnderImageDir->MaximumLength) {
  183. pDllNameUnderImageDir = (*RtlAllocateStringRoutine)(cbDllNameUnderImageDir);
  184. if (pDllNameUnderImageDir == NULL) {
  185. status = STATUS_NO_MEMORY ;
  186. goto Exit;
  187. }
  188. }else
  189. pDllNameUnderImageDir = NewDllNameUnderImageDir->Buffer;
  190. Cursor = pDllNameUnderImageDir;
  191. RtlCopyMemory(Cursor, pFullImageName, cbFullImagePathLengthWithTrailingSlash);
  192. Cursor = pDllNameUnderImageDir + cbFullImagePathLengthWithTrailingSlash / sizeof(WCHAR);
  193. RtlCopyMemory(Cursor, pp2, cbDllFileNameLengthWithTrailingNULL - sizeof(WCHAR)) ;
  194. Cursor += (cbDllFileNameLengthWithTrailingNULL - sizeof(WCHAR)) / sizeof(WCHAR);
  195. if (!Dot) {
  196. // If there is no '.' in the basename add the ".DLL" to it.
  197. //
  198. // The -1 will work just as well as - sizeof(WCHAR) as we are dividing by
  199. // sizeof(WCHAR) and it will be rounded down correctly as Size1 and Size2 are
  200. // even. The -1 could be more optimal than subtracting sizeof(WCHAR)
  201. //
  202. RtlCopyMemory(Cursor, DLL_EXTENSION, sizeof(DLL_EXTENSION));
  203. cbDllFileNameLengthWithTrailingNULL += sizeof(DLL_EXTENSION) - sizeof(WCHAR) ; // Mark base name as being 8 bytes bigger.
  204. } else
  205. *Cursor = L'\0';
  206. cbStringLength = cbFullImageNameLength
  207. + sizeof(DLL_REDIRECTION_LOCAL_SUFFIX) - sizeof(WCHAR) //.local
  208. + sizeof(WCHAR) // "\\"
  209. + cbDllFileNameLengthWithTrailingNULL;
  210. if (cbStringLength > UNICODE_STRING_MAX_BYTES) {
  211. status = STATUS_NAME_TOO_LONG;
  212. goto Exit;
  213. }
  214. cbDllNameUnderLocalDir = (USHORT)cbStringLength;
  215. if ( cbDllNameUnderLocalDir > NewDllNameUnderLocalDir->MaximumLength) {
  216. pDllNameUnderLocalDir = (RtlAllocateStringRoutine)(cbDllNameUnderLocalDir);
  217. if (!pDllNameUnderLocalDir) {
  218. status = STATUS_NO_MEMORY ;
  219. goto Exit;
  220. }
  221. }else
  222. pDllNameUnderLocalDir = NewDllNameUnderLocalDir->Buffer;
  223. Cursor = pDllNameUnderLocalDir;
  224. RtlCopyMemory(Cursor, pFullImageName, cbFullImageNameLength);
  225. Cursor = pDllNameUnderLocalDir + cbFullImageNameLength / sizeof(WCHAR);
  226. RtlCopyMemory(Cursor, DLL_REDIRECTION_LOCAL_SUFFIX, sizeof(DLL_REDIRECTION_LOCAL_SUFFIX) - sizeof(WCHAR)) ;
  227. Cursor += (sizeof(DLL_REDIRECTION_LOCAL_SUFFIX) - sizeof(WCHAR)) / sizeof(WCHAR);
  228. *Cursor = L'\\';
  229. Cursor += 1;
  230. RtlCopyMemory(Cursor,
  231. pDllNameUnderImageDir + cbFullImagePathLengthWithTrailingSlash/sizeof(WCHAR),
  232. cbDllFileNameLengthWithTrailingNULL) ;
  233. NewDllNameUnderImageDir->Buffer = pDllNameUnderImageDir ;
  234. if ( pDllNameUnderImageDir != pBuf1) // if memory is not-reallocated, MaximumLength should be untouched
  235. NewDllNameUnderImageDir->MaximumLength = cbFullImagePathLengthWithTrailingSlash + cbDllFileNameLengthWithTrailingNULL;
  236. NewDllNameUnderImageDir->Length = (USHORT)(cbDllNameUnderImageDir - sizeof(WCHAR));
  237. NewDllNameUnderLocalDir->Buffer = pDllNameUnderLocalDir;
  238. if (pDllNameUnderLocalDir != pBuf2)
  239. NewDllNameUnderLocalDir->MaximumLength = cbDllNameUnderLocalDir;
  240. NewDllNameUnderLocalDir->Length = (USHORT)(cbDllNameUnderLocalDir - sizeof(WCHAR));
  241. status = STATUS_SUCCESS ;
  242. Exit:
  243. if (!NT_SUCCESS(status)) {
  244. if (pDllNameUnderImageDir != pBuf1)
  245. (RtlFreeStringRoutine)(pDllNameUnderImageDir);
  246. if (pDllNameUnderLocalDir != pBuf2)
  247. (RtlFreeStringRoutine)(pDllNameUnderLocalDir);
  248. }
  249. return status;
  250. }
  251. NTSTATUS
  252. LdrpAllocateUnicodeString(
  253. OUT PUNICODE_STRING StringOut,
  254. IN USHORT Length
  255. )
  256. /*++
  257. Routine Description:
  258. This routine allocates space for a UNICODE_STRING from the loader
  259. private heap.
  260. Arguments:
  261. StringOut - Pointer to UNICODE_STRING in which the information about
  262. the allocated string is written. Any previous contents of StringOut
  263. are overwritten and lost.
  264. Length - length, in bytes, of the string which StringOut must be able
  265. to hold.
  266. Return Value:
  267. NTSTATUS indicating success or failure of this function. In general
  268. the only reasons it fails are STATUS_NO_MEMORY when the heap allocation
  269. cannot be performed or STATUS_INVALID_PARAMETER when an invalid parameter
  270. value is passed in.
  271. --*/
  272. {
  273. NTSTATUS st = STATUS_INTERNAL_ERROR; // returned if someone messes up and forgets to otherwise set it
  274. if (StringOut != NULL) {
  275. StringOut->Length = 0;
  276. StringOut->MaximumLength = 0;
  277. StringOut->Buffer = NULL;
  278. }
  279. if ((StringOut == NULL) ||
  280. ((Length % sizeof(WCHAR)) != 0)) {
  281. st = STATUS_INVALID_PARAMETER;
  282. goto Exit;
  283. }
  284. StringOut->Buffer = RtlAllocateHeap(LdrpHeap, 0, Length + sizeof(WCHAR));
  285. if (StringOut->Buffer == NULL) {
  286. st = STATUS_NO_MEMORY;
  287. goto Exit;
  288. }
  289. StringOut->Buffer[Length / sizeof(WCHAR)] = L'\0';
  290. StringOut->Length = 0;
  291. // If the true length of the buffer can be represted in 16 bits, store it; otherwise
  292. // store the biggest number we can.
  293. if (Length != UNICODE_STRING_MAX_BYTES)
  294. StringOut->MaximumLength = Length + sizeof(WCHAR);
  295. else
  296. StringOut->MaximumLength = Length;
  297. st = STATUS_SUCCESS;
  298. Exit:
  299. return st;
  300. }
  301. NTSTATUS
  302. LdrpCopyUnicodeString(
  303. OUT PUNICODE_STRING StringOut,
  304. IN PCUNICODE_STRING StringIn
  305. )
  306. /*++
  307. Routine Description:
  308. This function makes a copy of a unicode string; the important aspect
  309. of it is that the string is allocated from the loader private heap.
  310. Arguments:
  311. StringOut - Pointer to UNICODE_STRING in which the information about
  312. the copied string is written. Any previous contents of StringOut
  313. are overwritten and lost.
  314. StringIn - Pointer to constant UNICODE_STRING which is copied.
  315. Return Value:
  316. NTSTATUS indicating success or failure of this function. In general
  317. the only reason it fails is STATUS_NO_MEMORY when the heap allocation
  318. cannot be performed.
  319. --*/
  320. {
  321. NTSTATUS st = STATUS_INTERNAL_ERROR;
  322. ULONG BytesNeeded = 0;
  323. if (StringOut != NULL) {
  324. StringOut->Length = 0;
  325. StringOut->MaximumLength = 0;
  326. StringOut->Buffer = NULL;
  327. }
  328. if ((StringOut == NULL) ||
  329. (StringIn == NULL)) {
  330. st = STATUS_INVALID_PARAMETER;
  331. goto Exit;
  332. }
  333. st = RtlValidateUnicodeString(0, StringIn);
  334. if (!NT_SUCCESS(st))
  335. goto Exit;
  336. st = LdrpAllocateUnicodeString(StringOut, StringIn->Length);
  337. if (!NT_SUCCESS(st))
  338. goto Exit;
  339. RtlCopyMemory(StringOut->Buffer, StringIn->Buffer, StringIn->Length);
  340. StringOut->Length = StringIn->Length;
  341. st = STATUS_SUCCESS;
  342. Exit:
  343. return st;
  344. }
  345. VOID
  346. LdrpFreeUnicodeString(
  347. IN OUT PUNICODE_STRING StringIn
  348. )
  349. /*++
  350. Routine Description:
  351. This function deallocates a string that was allocated using
  352. LdrpCopyUnicodeString.
  353. Arguments:
  354. String - Pointer to UNICODE_STRING which is to be freed. On exit,
  355. all the members are set to 0/null as appropriate.
  356. Return Value:
  357. None
  358. --*/
  359. {
  360. if (StringIn != NULL) {
  361. if (StringIn->Buffer != NULL) {
  362. RtlFreeHeap(LdrpHeap, 0, StringIn->Buffer);
  363. }
  364. StringIn->Length = 0;
  365. StringIn->MaximumLength = 0;
  366. StringIn->Buffer = NULL;
  367. }
  368. }
  369. VOID
  370. LdrpEnsureLoaderLockIsHeld(
  371. VOID
  372. )
  373. {
  374. BOOLEAN LoaderLockIsHeld =
  375. ((LdrpInLdrInit) ||
  376. ((LdrpShutdownInProgress) &&
  377. (LdrpShutdownThreadId == NtCurrentTeb()->ClientId.UniqueThread)) ||
  378. (LdrpLoaderLock.OwningThread == NtCurrentTeb()->ClientId.UniqueThread));
  379. ASSERT(LoaderLockIsHeld);
  380. if (!LoaderLockIsHeld)
  381. RtlRaiseStatus(STATUS_NOT_LOCKED);
  382. }
  383. int
  384. LdrpGenericExceptionFilter(
  385. IN const struct _EXCEPTION_POINTERS *ExceptionPointers,
  386. IN PCSTR FunctionName
  387. )
  388. /*++
  389. Routine Description:
  390. Exception filter function used in __try block throughout the loader
  391. code instead of just specifying __except(EXCEPTION_EXECUTE_HANDLER).
  392. Arguments:
  393. ExceptionPointers
  394. Pointer to exception information returned by GetExceptionInformation() in the __except()
  395. FunctionName
  396. Name of the function in which the __try block appears.
  397. Return Value:
  398. EXCEPTION_EXECUTE_HANDLER
  399. --*/
  400. {
  401. const ULONG ExceptionCode = ExceptionPointers->ExceptionRecord->ExceptionCode;
  402. DbgPrintEx(
  403. DPFLTR_LDR_ID,
  404. LDR_ERROR_DPFLTR,
  405. "LDR: exception %08lx thrown within function %s\n"
  406. " Exception record: %p\n"
  407. " Context record: %p\n",
  408. ExceptionCode, FunctionName,
  409. ExceptionPointers->ExceptionRecord,
  410. ExceptionPointers->ContextRecord);
  411. #ifdef _X86_
  412. // It would be nice to have a generic context dumper but right now I'm just trying to
  413. // debug X86 and this is the quick thing to do. -mgrier 4/8/2001
  414. DbgPrintEx(
  415. DPFLTR_LDR_ID,
  416. LDR_ERROR_DPFLTR,
  417. " Context->Eip = %p\n"
  418. " Context->Ebp = %p\n"
  419. " Context->Esp = %p\n",
  420. ExceptionPointers->ContextRecord->Eip,
  421. ExceptionPointers->ContextRecord->Ebp,
  422. ExceptionPointers->ContextRecord->Esp);
  423. #endif // _X86_
  424. if (LdrpBreakOnExceptions) {
  425. char Response[2];
  426. for (;;) {
  427. DbgPrint("\n***Exception thrown within loader***\n");
  428. DbgPrompt(
  429. "Break repeatedly, break Once, Ignore, terminate Process or terminate Thread (boipt)? ",
  430. Response,
  431. sizeof(Response));
  432. switch (Response[0]) {
  433. case 'b':
  434. case 'B':
  435. case 'o':
  436. case 'O':
  437. DbgPrint("Execute '.cxr %p' to dump context\n", ExceptionPointers->ContextRecord);
  438. DbgBreakPoint();
  439. if ((Response[0] == 'o') || (Response[0] == 'O'))
  440. return EXCEPTION_EXECUTE_HANDLER;
  441. case 'I':
  442. case 'i':
  443. return EXCEPTION_EXECUTE_HANDLER;
  444. case 'P':
  445. case 'p':
  446. NtTerminateProcess( NtCurrentProcess(), ExceptionCode);
  447. break;
  448. case 'T':
  449. case 't':
  450. NtTerminateThread( NtCurrentThread(), ExceptionCode);
  451. break;
  452. }
  453. }
  454. }
  455. return EXCEPTION_EXECUTE_HANDLER;
  456. }