Leaked source code of windows server 2003
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.

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