[NTDLL/LDR]

- Rewrite TLS part of the ntdll loader. Old TLS-related messy, duplicated and hard to maintain code is gone.
- Disclaimer: All new ldr-rewrite code is almost entirely based on Alex's patch made in 2006, with my improvements, bugfixes and adapting existing codepaths.
- File naming convention: Files of the ldr*.c mask are considered as a new rewritten loader code. All other files in ntdll/ldr directory are old code which soon may be deprecated.

svn path=/trunk/; revision=51051
This commit is contained in:
Aleksey Bragin 2011-03-14 22:06:25 +00:00
parent 123516873b
commit 6d38425b3f
5 changed files with 332 additions and 202 deletions

View file

@ -8,6 +8,28 @@
/* INCLUDES ******************************************************************/
extern BOOLEAN ShowSnaps;
typedef struct _LDRP_TLS_DATA
{
LIST_ENTRY TlsLinks;
IMAGE_TLS_DIRECTORY TlsDirectory;
} LDRP_TLS_DATA, *PLDRP_TLS_DATA;
typedef BOOL
(NTAPI *PDLLMAIN_FUNC)(HANDLE hInst,
ULONG ul_reason_for_call,
LPVOID lpReserved);
/* ldrinit.c */
NTSTATUS NTAPI LdrpInitializeTls(VOID);
NTSTATUS NTAPI LdrpAllocateTls(VOID);
VOID NTAPI LdrpFreeTls(VOID);
VOID NTAPI LdrpTlsCallback(PVOID BaseAddress, ULONG Reason);
BOOLEAN NTAPI LdrpCallDllEntry(PDLLMAIN_FUNC EntryPoint, PVOID BaseAddress, ULONG Reason, PVOID Context);
/* FIXME: Cleanup this mess */
typedef NTSTATUS (NTAPI *PEPFUNC)(PPEB);
NTSTATUS LdrMapSections(HANDLE ProcessHandle,
@ -23,10 +45,6 @@ PEPFUNC LdrPEStartup (PVOID ImageBase,
HANDLE SectionHandle,
PLDR_DATA_TABLE_ENTRY* Module,
PWSTR FullDosName);
typedef BOOL
(NTAPI *PDLLMAIN_FUNC)(HANDLE hInst,
ULONG ul_reason_for_call,
LPVOID lpReserved);
extern HANDLE WindowsApiPort;

View file

@ -20,6 +20,16 @@ HKEY Wow64ExecOptionsKey;
UNICODE_STRING ImageExecOptionsString = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options");
UNICODE_STRING Wow64OptionsString = RTL_CONSTANT_STRING(L"");
//RTL_BITMAP TlsBitMap;
//RTL_BITMAP TlsExpansionBitMap;
//RTL_BITMAP FlsBitMap;
BOOLEAN LdrpImageHasTls;
LIST_ENTRY LdrpTlsList;
ULONG LdrpNumberOfTlsEntries;
ULONG LdrpNumberOfProcessors;
BOOLEAN ShowSnaps;
/* FUNCTIONS *****************************************************************/
/*
@ -317,4 +327,167 @@ LdrQueryImageFileExecutionOptions(IN PUNICODE_STRING SubKey,
FALSE);
}
NTSTATUS
NTAPI
LdrpInitializeTls(VOID)
{
PLIST_ENTRY NextEntry, ListHead;
PLDR_DATA_TABLE_ENTRY LdrEntry;
PIMAGE_TLS_DIRECTORY TlsDirectory;
PLDRP_TLS_DATA TlsData;
ULONG Size;
/* Initialize the TLS List */
InitializeListHead(&LdrpTlsList);
/* Loop all the modules */
ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
NextEntry = ListHead->Flink;
while (ListHead != NextEntry)
{
/* Get the entry */
LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
NextEntry = NextEntry->Flink;
/* Get the TLS directory */
TlsDirectory = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_TLS,
&Size);
/* Check if we have a directory */
if (!TlsDirectory) continue;
/* Check if the image has TLS */
if (!LdrpImageHasTls) LdrpImageHasTls = TRUE;
/* Show debug message */
if (ShowSnaps)
{
DPRINT1("LDR: Tls Found in %wZ at %p\n",
&LdrEntry->BaseDllName,
TlsDirectory);
}
/* Allocate an entry */
TlsData = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(LDRP_TLS_DATA));
if (!TlsData) return STATUS_NO_MEMORY;
/* Lock the DLL and mark it for TLS Usage */
LdrEntry->LoadCount = -1;
LdrEntry->TlsIndex = -1;
/* Save the cached TLS data */
TlsData->TlsDirectory = *TlsDirectory;
InsertTailList(&LdrpTlsList, &TlsData->TlsLinks);
/* Update the index */
*(PLONG)TlsData->TlsDirectory.AddressOfIndex = LdrpNumberOfTlsEntries;
TlsData->TlsDirectory.Characteristics = LdrpNumberOfTlsEntries++;
}
/* Done setting up TLS, allocate entries */
return LdrpAllocateTls();
}
NTSTATUS
NTAPI
LdrpAllocateTls(VOID)
{
PTEB Teb = NtCurrentTeb();
PLIST_ENTRY NextEntry, ListHead;
PLDRP_TLS_DATA TlsData;
ULONG TlsDataSize;
PVOID *TlsVector;
/* Check if we have any entries */
if (LdrpNumberOfTlsEntries)
return 0;
/* Allocate the vector array */
TlsVector = RtlAllocateHeap(RtlGetProcessHeap(),
0,
LdrpNumberOfTlsEntries * sizeof(PVOID));
if (!TlsVector) return STATUS_NO_MEMORY;
Teb->ThreadLocalStoragePointer = TlsVector;
/* Loop the TLS Array */
ListHead = &LdrpTlsList;
NextEntry = ListHead->Flink;
while (NextEntry != ListHead)
{
/* Get the entry */
TlsData = CONTAINING_RECORD(NextEntry, LDRP_TLS_DATA, TlsLinks);
NextEntry = NextEntry->Flink;
/* Allocate this vector */
TlsDataSize = TlsData->TlsDirectory.EndAddressOfRawData -
TlsData->TlsDirectory.StartAddressOfRawData;
TlsVector[TlsData->TlsDirectory.Characteristics] = RtlAllocateHeap(RtlGetProcessHeap(),
0,
TlsDataSize);
if (!TlsVector[TlsData->TlsDirectory.Characteristics])
{
/* Out of memory */
return STATUS_NO_MEMORY;
}
/* Show debug message */
if (ShowSnaps)
{
DPRINT1("LDR: TlsVector %x Index %d = %x copied from %x to %x\n",
TlsVector,
TlsData->TlsDirectory.Characteristics,
&TlsVector[TlsData->TlsDirectory.Characteristics],
TlsData->TlsDirectory.StartAddressOfRawData,
TlsVector[TlsData->TlsDirectory.Characteristics]);
}
/* Copy the data */
RtlCopyMemory(TlsVector[TlsData->TlsDirectory.Characteristics],
(PVOID)TlsData->TlsDirectory.StartAddressOfRawData,
TlsDataSize);
}
/* Done */
return STATUS_SUCCESS;
}
VOID
NTAPI
LdrpFreeTls(VOID)
{
PLIST_ENTRY ListHead, NextEntry;
PLDRP_TLS_DATA TlsData;
PVOID *TlsVector;
PTEB Teb = NtCurrentTeb();
/* Get a pointer to the vector array */
TlsVector = Teb->ThreadLocalStoragePointer;
if (!TlsVector) return;
/* Loop through it */
ListHead = &LdrpTlsList;
NextEntry = ListHead->Flink;
while (NextEntry != ListHead)
{
TlsData = CONTAINING_RECORD(NextEntry, LDRP_TLS_DATA, TlsLinks);
NextEntry = NextEntry->Flink;
/* Free each entry */
if (TlsVector[TlsData->TlsDirectory.Characteristics])
{
RtlFreeHeap(RtlGetProcessHeap(),
0,
TlsVector[TlsData->TlsDirectory.Characteristics]);
}
}
/* Free the array itself */
RtlFreeHeap(RtlGetProcessHeap(),
0,
TlsVector);
}
/* EOF */

View file

@ -0,0 +1,83 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS NT User-Mode Library
* FILE: dll/ntdll/ldr/ldrutils.c
* PURPOSE: Internal Loader Utility Functions
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
* Aleksey Bragin (aleksey@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <ntdll.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
/* FUNCTIONS *****************************************************************/
BOOLEAN
NTAPI
LdrpCallDllEntry(PDLLMAIN_FUNC EntryPoint,
PVOID BaseAddress,
ULONG Reason,
PVOID Context)
{
/* Call the entry */
return EntryPoint(BaseAddress, Reason, Context);
}
VOID
NTAPI
LdrpTlsCallback(PVOID BaseAddress, ULONG Reason)
{
PIMAGE_TLS_DIRECTORY TlsDirectory;
PIMAGE_TLS_CALLBACK *Array, Callback;
ULONG Size;
/* Get the TLS Directory */
TlsDirectory = RtlImageDirectoryEntryToData(BaseAddress,
TRUE,
IMAGE_DIRECTORY_ENTRY_TLS,
&Size);
/* Protect against invalid pointers */
_SEH2_TRY
{
/* Make sure it's valid and we have an array */
if (TlsDirectory && (Array = (PIMAGE_TLS_CALLBACK *)TlsDirectory->AddressOfCallBacks))
{
/* Display debug */
if (ShowSnaps)
{
DPRINT1("LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p\n",
BaseAddress, TlsDirectory, Array);
}
/* Loop the array */
while (*Array)
{
/* Get the TLS Entrypoint */
Callback = *Array++;
/* Display debug */
if (ShowSnaps)
{
DPRINT1("LDR: Calling Tls Callback Imagebase %p Function %p\n",
BaseAddress, Callback);
}
/* Call it */
LdrpCallDllEntry((PDLLMAIN_FUNC)Callback, BaseAddress, Reason, NULL);
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Do nothing */
}
_SEH2_END;
}
/* EOF */

View file

@ -31,19 +31,7 @@
#define TRACE_LDR(...) if (RtlGetNtGlobalFlags() & FLG_SHOW_LDR_SNAPS) { DbgPrint("(LDR:%s:%d) ",__FILE__,__LINE__); DbgPrint(__VA_ARGS__); }
#endif
typedef struct _TLS_DATA
{
PVOID StartAddressOfRawData;
DWORD TlsDataSize;
DWORD TlsZeroSize;
PIMAGE_TLS_CALLBACK *TlsAddressOfCallBacks;
PLDR_DATA_TABLE_ENTRY Module;
} TLS_DATA, *PTLS_DATA;
static BOOLEAN LdrpDllShutdownInProgress = FALSE;
static PTLS_DATA LdrpTlsArray = NULL;
static ULONG LdrpTlsCount = 0;
static ULONG LdrpTlsSize = 0;
static HANDLE LdrpKnownDllsDirHandle = NULL;
static UNICODE_STRING LdrpKnownDllPath = {0, 0, NULL};
static PLDR_DATA_TABLE_ENTRY LdrpLastModule = NULL;
@ -118,51 +106,6 @@ static __inline LONG LdrpIncrementLoadCount(PLDR_DATA_TABLE_ENTRY Module, BOOLEA
return LoadCount;
}
static __inline VOID LdrpAcquireTlsSlot(PLDR_DATA_TABLE_ENTRY Module, ULONG Size, BOOLEAN Locked)
{
if (!Locked)
{
RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock);
}
Module->TlsIndex = (SHORT)LdrpTlsCount;
LdrpTlsCount++;
LdrpTlsSize += Size;
if (!Locked)
{
RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
}
}
static __inline VOID LdrpTlsCallback(PLDR_DATA_TABLE_ENTRY Module, ULONG dwReason)
{
PIMAGE_TLS_CALLBACK *TlsCallback;
if (Module->TlsIndex != 0xFFFF && Module->LoadCount == LDRP_PROCESS_CREATION_TIME)
{
TlsCallback = LdrpTlsArray[Module->TlsIndex].TlsAddressOfCallBacks;
if (TlsCallback)
{
while (*TlsCallback)
{
TRACE_LDR("%wZ - Calling tls callback at %x\n",
&Module->BaseDllName, *TlsCallback);
(*TlsCallback)(Module->DllBase, dwReason, NULL);
TlsCallback++;
}
}
}
}
static BOOLEAN LdrpCallDllEntry(PLDR_DATA_TABLE_ENTRY Module, DWORD dwReason, PVOID lpReserved)
{
if (!(Module->Flags & LDRP_IMAGE_DLL) ||
Module->EntryPoint == 0)
{
return TRUE;
}
LdrpTlsCallback(Module, dwReason);
return ((PDLLMAIN_FUNC)Module->EntryPoint)(Module->DllBase, dwReason, lpReserved);
}
static PWSTR
LdrpQueryAppPaths(IN PCWSTR ImageName)
{
@ -269,127 +212,6 @@ LdrpQueryAppPaths(IN PCWSTR ImageName)
return Path;
}
static NTSTATUS
LdrpInitializeTlsForThread(VOID)
{
PVOID* TlsPointers;
PTLS_DATA TlsInfo;
PVOID TlsData;
ULONG i;
PTEB Teb = NtCurrentTeb();
DPRINT("LdrpInitializeTlsForThread() called for %wZ\n", &ExeModule->BaseDllName);
Teb->StaticUnicodeString.Length = 0;
Teb->StaticUnicodeString.MaximumLength = sizeof(Teb->StaticUnicodeBuffer);
Teb->StaticUnicodeString.Buffer = Teb->StaticUnicodeBuffer;
if (LdrpTlsCount > 0)
{
TlsPointers = RtlAllocateHeap(RtlGetProcessHeap(),
0,
LdrpTlsCount * sizeof(PVOID) + LdrpTlsSize);
if (TlsPointers == NULL)
{
DPRINT1("failed to allocate thread tls data\n");
return STATUS_NO_MEMORY;
}
TlsData = (PVOID)((ULONG_PTR)TlsPointers + LdrpTlsCount * sizeof(PVOID));
Teb->ThreadLocalStoragePointer = TlsPointers;
TlsInfo = LdrpTlsArray;
for (i = 0; i < LdrpTlsCount; i++, TlsInfo++)
{
TRACE_LDR("Initialize tls data for %wZ\n", &TlsInfo->Module->BaseDllName);
TlsPointers[i] = TlsData;
if (TlsInfo->TlsDataSize)
{
memcpy(TlsData, TlsInfo->StartAddressOfRawData, TlsInfo->TlsDataSize);
TlsData = (PVOID)((ULONG_PTR)TlsData + TlsInfo->TlsDataSize);
}
if (TlsInfo->TlsZeroSize)
{
memset(TlsData, 0, TlsInfo->TlsZeroSize);
TlsData = (PVOID)((ULONG_PTR)TlsData + TlsInfo->TlsZeroSize);
}
}
}
DPRINT("LdrpInitializeTlsForThread() done\n");
return STATUS_SUCCESS;
}
static NTSTATUS
LdrpInitializeTlsForProccess(VOID)
{
PLIST_ENTRY ModuleListHead;
PLIST_ENTRY Entry;
PLDR_DATA_TABLE_ENTRY Module;
PIMAGE_TLS_DIRECTORY TlsDirectory;
PTLS_DATA TlsData;
ULONG Size;
DPRINT("LdrpInitializeTlsForProccess() called for %wZ\n", &ExeModule->BaseDllName);
if (LdrpTlsCount > 0)
{
LdrpTlsArray = RtlAllocateHeap(RtlGetProcessHeap(),
0,
LdrpTlsCount * sizeof(TLS_DATA));
if (LdrpTlsArray == NULL)
{
DPRINT1("Failed to allocate global tls data\n");
return STATUS_NO_MEMORY;
}
ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
Entry = ModuleListHead->Flink;
while (Entry != ModuleListHead)
{
Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (Module->LoadCount == LDRP_PROCESS_CREATION_TIME &&
Module->TlsIndex != 0xFFFF)
{
TlsDirectory = (PIMAGE_TLS_DIRECTORY)
RtlImageDirectoryEntryToData(Module->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_TLS,
&Size);
ASSERT(Module->TlsIndex < LdrpTlsCount);
TlsData = &LdrpTlsArray[Module->TlsIndex];
TlsData->StartAddressOfRawData = (PVOID)TlsDirectory->StartAddressOfRawData;
TlsData->TlsDataSize = TlsDirectory->EndAddressOfRawData - TlsDirectory->StartAddressOfRawData;
TlsData->TlsZeroSize = TlsDirectory->SizeOfZeroFill;
if (TlsDirectory->AddressOfCallBacks)
TlsData->TlsAddressOfCallBacks = (PIMAGE_TLS_CALLBACK *)TlsDirectory->AddressOfCallBacks;
else
TlsData->TlsAddressOfCallBacks = NULL;
TlsData->Module = Module;
#if 0
DbgPrint("TLS directory for %wZ\n", &Module->BaseDllName);
DbgPrint("StartAddressOfRawData: %x\n", TlsDirectory->StartAddressOfRawData);
DbgPrint("EndAddressOfRawData: %x\n", TlsDirectory->EndAddressOfRawData);
DbgPrint("SizeOfRawData: %d\n", TlsDirectory->EndAddressOfRawData - TlsDirectory->StartAddressOfRawData);
DbgPrint("AddressOfIndex: %x\n", TlsDirectory->AddressOfIndex);
DbgPrint("AddressOfCallBacks: %x\n", TlsDirectory->AddressOfCallBacks);
DbgPrint("SizeOfZeroFill: %d\n", TlsDirectory->SizeOfZeroFill);
DbgPrint("Characteristics: %x\n", TlsDirectory->Characteristics);
#endif
/*
* FIXME:
* Is this region allways writable ?
*/
*(PULONG)TlsDirectory->AddressOfIndex = Module->TlsIndex;
}
Entry = Entry->Flink;
}
}
DPRINT("LdrpInitializeTlsForProccess() done\n");
return STATUS_SUCCESS;
}
VOID
LdrpInitLoader(VOID)
{
@ -2093,11 +1915,6 @@ Success:
}
}
if (TlsDirectory && TlsSize > 0)
{
LdrpAcquireTlsSlot(Module, TlsSize, FALSE);
}
if (Module->EntryPointActivationContext) RtlDeactivateActivationContext( 0, cookie );
return STATUS_SUCCESS;
@ -2216,14 +2033,14 @@ PEPFUNC LdrPEStartup (PVOID ImageBase,
}
DPRINT("Fixup done\n");
RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
Status = LdrpInitializeTlsForProccess();
Status = LdrpInitializeTls();
if (NT_SUCCESS(Status))
{
Status = LdrpAttachProcess();
}
if (NT_SUCCESS(Status))
{
LdrpTlsCallback(*Module, DLL_PROCESS_ATTACH);
LdrpTlsCallback((*Module)->DllBase, DLL_PROCESS_ATTACH);
}
@ -2768,10 +2585,22 @@ LdrpDetachProcess(BOOLEAN UnloadAll)
{
TRACE_LDR("Unload %wZ - Calling entry point at %x\n",
&Module->BaseDllName, Module->EntryPoint);
LdrpCallDllEntry(Module,
/* Check if it has TLS */
if (Module->TlsIndex)
{
/* Call TLS */
LdrpTlsCallback(Module->DllBase, DLL_PROCESS_ATTACH);
}
if ((Module->Flags & LDRP_IMAGE_DLL) && Module->EntryPoint)
{
LdrpCallDllEntry(Module->EntryPoint,
Module->DllBase,
DLL_PROCESS_DETACH,
(PVOID)(Module->LoadCount == LDRP_PROCESS_CREATION_TIME ? 1 : 0));
}
}
else
{
TRACE_LDR("Unload %wZ\n", &Module->BaseDllName);
@ -2855,7 +2684,19 @@ LdrpAttachProcess(VOID)
Module->Flags |= LDRP_LOAD_IN_PROGRESS;
TRACE_LDR("%wZ loaded - Calling init routine at %x for process attaching\n",
&Module->BaseDllName, Module->EntryPoint);
Result = LdrpCallDllEntry(Module, DLL_PROCESS_ATTACH, (PVOID)(Module->LoadCount == LDRP_PROCESS_CREATION_TIME ? 1 : 0));
/* Check if it has TLS */
if (Module->TlsIndex && FALSE/*Context*/)
{
/* Call TLS */
LdrpTlsCallback(Module->DllBase, DLL_PROCESS_ATTACH);
}
if ((Module->Flags & LDRP_IMAGE_DLL) && Module->EntryPoint)
Result = LdrpCallDllEntry(Module->EntryPoint, Module->DllBase, DLL_PROCESS_ATTACH, (PVOID)(Module->LoadCount == LDRP_PROCESS_CREATION_TIME ? 1 : 0));
else
Result = TRUE;
if (!Result)
{
Status = STATUS_DLL_INIT_FAILED;
@ -2915,7 +2756,7 @@ LdrpAttachThread (VOID)
RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock);
Status = LdrpInitializeTlsForThread();
Status = LdrpAllocateTls();
if (NT_SUCCESS(Status))
{
@ -2931,14 +2772,23 @@ LdrpAttachThread (VOID)
{
TRACE_LDR("%wZ - Calling entry point at %x for thread attaching\n",
&Module->BaseDllName, Module->EntryPoint);
LdrpCallDllEntry(Module, DLL_THREAD_ATTACH, NULL);
/* Check if it has TLS */
if (Module->TlsIndex)
{
/* Call TLS */
LdrpTlsCallback(Module->DllBase, DLL_THREAD_ATTACH);
}
if ((Module->Flags & LDRP_IMAGE_DLL) && Module->EntryPoint)
LdrpCallDllEntry(Module->EntryPoint, Module->DllBase, DLL_THREAD_ATTACH, NULL);
}
Entry = Entry->Flink;
}
Entry = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Flink;
Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
LdrpTlsCallback(Module, DLL_THREAD_ATTACH);
LdrpTlsCallback(Module->DllBase, DLL_THREAD_ATTACH);
}
RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock);
@ -2976,18 +2826,23 @@ LdrShutdownThread (VOID)
{
TRACE_LDR("%wZ - Calling entry point at %x for thread detaching\n",
&Module->BaseDllName, Module->EntryPoint);
LdrpCallDllEntry(Module, DLL_THREAD_DETACH, NULL);
/* Check if it has TLS */
if (Module->TlsIndex)
{
/* Call TLS */
LdrpTlsCallback(Module->DllBase, DLL_THREAD_DETACH);
}
if ((Module->Flags & LDRP_IMAGE_DLL) && Module->EntryPoint)
LdrpCallDllEntry(Module->EntryPoint, Module->DllBase, DLL_THREAD_DETACH, NULL);
}
Entry = Entry->Blink;
}
/* Free TLS */
LdrpFreeTls();
RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock);
if (LdrpTlsArray)
{
RtlFreeHeap (RtlGetProcessHeap(), 0, NtCurrentTeb()->ThreadLocalStoragePointer);
}
DPRINT("LdrShutdownThread() done\n");
return STATUS_SUCCESS;

View file

@ -48,6 +48,7 @@
</directory>
<directory name="ldr">
<file>ldrinit.c</file>
<file>ldrutils.c</file>
<file>startup.c</file>
<file>utils.c</file>
<file>actctx.c</file>