#include <kernel/kernel.h>
#include <kernel/proc.h>
#include <kernel/vmm.h>
#include <stdlib.h>
#include <string.h>
#include <kernel/thread.h>
#include <kernel/ramdisk.h>
#include <kernel/handle.h>
#include <os/os.h>
typedef struct CModulePe CModulePe;
//! A structure containing all the data used by a PE module
struct CModulePe
{
//! IModule vtable
IModule mod;
//! IPager vtable
IPager pager;
//! Process which loaded this module
process_t* proc;
//! Reference count of this module
dword refs,
//! Address at which the module was loaded
base,
//! Address at which the module should be loaded
pref_base,
//! Entry point of the module
entry;
//! true if imports have been performed
bool imported;
//! Pointer to the start of the image data in the ramdisk
byte* image_data;
//! Base file name of the module
wchar_t name[20];
};
#undef THIS
#define THIS ((CModulePe*) this)
HRESULT STDCALL CModulePe_QueryInterface(IModule* this, REFIID iid, void ** ppvObject)
{
//_cputws(L"CModulePe_QueryInterface\n");
if (InlineIsEqualGUID(iid, &IID_IUnknown) ||
InlineIsEqualGUID(iid, &IID_IModule))
{
this->vtbl->AddRef(this);
*ppvObject = &THIS->mod;
return S_OK;
}
else if (InlineIsEqualGUID(iid, &IID_IPager))
{
this->vtbl->AddRef(this);
*ppvObject = &THIS->pager;
return S_OK;
}
else
return E_FAIL;
}
ULONG STDCALL CModulePe_AddRef(IModule* this)
{
return ++THIS->refs;
}
ULONG STDCALL CModulePe_Release(IModule* this)
{
if (THIS->refs == 0)
{
hndFree(THIS);
return 0;
}
else
return THIS->refs--;
}
const void* STDCALL CModulePe_GetEntryPoint(IModule* this)
{
return (const void*) THIS->entry;
}
const void* STDCALL CModulePe_GetBase(IModule* this)
{
return (const void*) THIS->base;
}
const void* STDCALL CModulePe_GetProcAddress(IModule* this, const char* proc)
{
#define adr(p) ((const void*) (THIS->base + (addr_t) p))
const IMAGE_DOS_HEADER* dos_head;
IMAGE_PE_HEADERS* header;
const IMAGE_DATA_DIRECTORY* directories;
const IMAGE_EXPORT_DIRECTORY* exp;
dos_head = (IMAGE_DOS_HEADER*) THIS->base;
header = (IMAGE_PE_HEADERS*) ((char *) dos_head + dos_head->e_lfanew);
directories = header->OptionalHeader.DataDirectory;
if (directories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
return NULL;
exp = (const IMAGE_EXPORT_DIRECTORY*)
(THIS->base + directories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
{
const word *ordinal_table = adr(exp->AddressOfNameOrdinals);
const dword *function_table = adr(exp->AddressOfFunctions);
const dword *name_table = exp->AddressOfNames ? adr(exp->AddressOfNames) : 0;
size_t i;
unsigned unused_slots = 0; /* functions with an RVA of 0 seem to be unused */
for (i = 0; i < exp->NumberOfFunctions; i++)
{
const DWORD addr = function_table[i];
if (!addr) /* skip unused slots */
{
++unused_slots;
continue;
}
/* if names provided, list all names of this
* export ordinal
*/
if (name_table)
{
size_t n;
int found = 0;
for (n = 0; n < exp->NumberOfNames; n++)
{
if (ordinal_table[n] == i)
{
if (stricmp(adr(name_table[n]), proc) == 0)
return (const void*) (addr + THIS->base);
++found;
}
}
}
/* entry point */
//if (addr >= section_base_virtual && addr < section_base_virtual + section_length)
/* forwarder */
//wprintf(L"%S", adr(addr));
//else
/* normal export */
//wprintf(L"%6lx\n", addr);
}
if (unused_slots)
wprintf(L"\t-- there are %u unused slots --\n", unused_slots);
}
return NULL;
#undef adr
}
HRESULT STDCALL CModulePe_GetName(IModule* this, wchar_t* name, size_t max)
{
wcscpy(name, THIS->name);
return S_OK;
}
#undef THIS
#define THIS ((CModulePe*) (this - 1))
HRESULT STDCALL CModulePe_Pager_QueryInterface(IPager* this, REFIID iid, void ** ppvObject)
{
return THIS->mod.vtbl->QueryInterface(&THIS->mod, iid, ppvObject);
}
ULONG STDCALL CModulePe_Pager_AddRef(IPager* this)
{
return THIS->mod.vtbl->AddRef(&THIS->mod);
}
ULONG STDCALL CModulePe_Pager_Release(IPager* this)
{
return THIS->mod.vtbl->Release(&THIS->mod);
}
#define adr(p) ((const void*) (this->base + (addr_t) p))
HRESULT peImport(CModulePe* this,
const IMAGE_DATA_DIRECTORY *directories,
void *section_data,
dword section_start_virtual, dword section_length)
{
const IMAGE_IMPORT_DESCRIPTOR * imp;
const IMAGE_THUNK_DATA *import_entry;
IMAGE_THUNK_DATA *mapped_entry;
const char* name;
enum { bound_none, bound_old, bound_new } bound;
wchar_t temp[256];
IModule* mod;
dword args[3];
if (directories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0)
return S_OK;
imp = (const IMAGE_IMPORT_DESCRIPTOR*) (this->base +
directories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
this->imported = true;
for (; imp->Name; imp++)
{
name = (const char*) adr(imp->Name);
if (imp->TimeDateStamp == ~0UL)
bound = bound_new;
else if (imp->TimeDateStamp)
bound = bound_old;
else
bound = bound_none;
if (imp->u.OriginalFirstThunk)
{
import_entry = adr(imp->u.OriginalFirstThunk);
mapped_entry = (IMAGE_THUNK_DATA*) adr(imp->FirstThunk);
}
else
{
_cputws(L"\t(hint table missing, probably Borland bug)");
import_entry = adr(imp->FirstThunk);
mapped_entry = 0;
bound = bound_none;
}
mbstowcs(temp, name, countof(temp));
//wprintf(L"Loading library %s...", temp);
mod = modLoadPe(this->proc, temp, 0);
//if (mod)
//wprintf(L"done\n");
if (!mod)
{
wprintf(L"Failed to load %s\n", temp);
THIS->imported = false;
exception(current, NULL, EXCEPTION_MISSING_DLL, (dword) name);
return S_OK;
//return E_FAIL;
}
args[0] = (dword) mod->vtbl->GetBase(mod);
args[1] = 0;
args[2] = 0;
thrCall(current, (void*) mod->vtbl->GetEntryPoint(mod), args, sizeof(args));
{
int count, nextforwarder = bound==bound_old ? imp->ForwarderChain : -1;
for (count = 0; import_entry->u1.Ordinal; count++, import_entry++,
/*bound ?*/ mapped_entry++/* : 0*/) // xxx - what is this?
{
if (IMAGE_SNAP_BY_ORDINAL(import_entry->u1.Ordinal))
wprintf(L"\t%6lu %20S", IMAGE_ORDINAL(import_entry->u1.Ordinal),"<ordinal>");
else
{
const IMAGE_IMPORT_BY_NAME *name_import = adr(import_entry->u1.AddressOfData);
//wprintf(L"\t%6u %20S\n", name_import->Hint, name_import->Name);
//mapped_entry->u1.Function = this->dummy_import;
mapped_entry->u1.Function =
(dword*) mod->vtbl->GetProcAddress(mod, name_import->Name);
if (mapped_entry->u1.Function == NULL)
{
wprintf(L"Failed to access %S from %s\n", name_import->Name, temp);
THIS->imported = false;
exception(current, NULL, EXCEPTION_MISSING_IMPORT, (dword) name_import->Name);
return S_OK;
//return E_FAIL;
}
//wprintf(L"GetProcAddress returns %p\n", mapped_entry->u1.Function);
}
if (bound)
{
if (count != nextforwarder)
wprintf(L"\t0x%12lx\n", (unsigned long)mapped_entry->u1.Function);
else
{
wprintf(L"\t%12S\n", L" --> forward");
nextforwarder = (int)mapped_entry->u1.ForwarderString;
}
//else
//_cputws(L"\n");
}
}
}
}
return S_OK;
}
#undef adr
HRESULT peRelocateSection(CModulePe* this,
const IMAGE_DATA_DIRECTORY *directories,
void *section_data,
dword section_start_virtual, dword section_length)
{
const IMAGE_BASE_RELOCATION* rel;
bool valid, found = false;
dword* addr;
int adjust = this->base - this->pref_base;
if (directories[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress == 0 ||
adjust == 0)
return S_OK;
//wprintf(L"peRelocateSection: adjust = %d (%x)\n", adjust, adjust);
rel = (const IMAGE_BASE_RELOCATION*) (this->base +
directories[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while (rel->VirtualAddress)
{
const unsigned long reloc_num = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(word);
unsigned i;
const word *ad = (void *)(rel + 1);
valid = rel->VirtualAddress >= section_start_virtual &&
rel->VirtualAddress < section_start_virtual + section_length;
if (valid)
{
wprintf(L"\t%lu relocations starting at 0x%04lx\n", reloc_num, rel->VirtualAddress);
found = true;
}
for (i = 0; i < reloc_num; i++, ad++)
if (valid)
{
const wchar_t *type;
switch (*ad >> 12)
{
case IMAGE_REL_BASED_ABSOLUTE:
type = L"nop";
break;
case IMAGE_REL_BASED_HIGH:
type = L"fix high";
break;
case IMAGE_REL_BASED_LOW:
type = L"fix low";
break;
case IMAGE_REL_BASED_HIGHLOW:
addr = (dword*) ((*ad & 0xfffU) + rel->VirtualAddress + this->base);
//wprintf(L"hl(%p) %x = %x + %x\n",
//addr, *addr + adjust, *addr, adjust);
*addr += adjust;
type = L"fix hilo";
break;
case IMAGE_REL_BASED_HIGHADJ:
type = L"fix highadj";
break;
case IMAGE_REL_BASED_MIPS_JMPADDR:
type = L"jmpaddr";
break;
case IMAGE_REL_BASED_SECTION:
type = L"section";
break;
case IMAGE_REL_BASED_REL32:
type = L"fix rel32";
break;
default:
type = L"???";
break;
}
if (*ad >> 12 != IMAGE_REL_BASED_ABSOLUTE &&
*ad >> 12 != IMAGE_REL_BASED_HIGHLOW)
wprintf(L"Relocation not implemented: offset 0x%03x (%s)\n", *ad & 0xfffU, type);
}
rel = (void *)ad;
}
return S_OK;
}
HRESULT STDCALL CModulePe_ValidatePage(IPager* this, const void* virt)
{
static int level;
const IMAGE_DOS_HEADER* dos_head;
IMAGE_PE_HEADERS* header;
addr_t page;
int i, section, pages;
void *sect_virt;
IMAGE_SECTION_HEADER *section_header, *sections;
dword priv;
HRESULT hr;
module_info_t* info;
level++;
wprintf(L"[%d] CModulePe_ValidatePage(%s, %p)...", level, THIS->name, virt);
priv = THIS->proc->level;
if (memTranslate(THIS->proc->page_dir, (void*) THIS->base) == 0)
{
/* it's the headers (hopefully) */
sect_virt = vmmAlloc(THIS->proc, 1, THIS->base,
priv | MEM_COMMIT | MEM_ZERO | MEM_READ | MEM_WRITE);
if (!sect_virt)
return E_FAIL;
memcpy(sect_virt, THIS->image_data, PAGE_SIZE);
_cputws(L"headers\n");
}
section = -1;
dos_head = (IMAGE_DOS_HEADER*) THIS->base;
page = ((addr_t) virt & -PAGE_SIZE) - THIS->base;
header = (IMAGE_PE_HEADERS*) ((char *) dos_head + dos_head->e_lfanew);
if ((addr_t) virt < THIS->base ||
(addr_t) virt > THIS->base + header->OptionalHeader.SizeOfImage)
{
//wprintf(L"outside %x - %x\n", THIS->base, THIS->base + header->OptionalHeader.SizeOfImage);
_cputws(L"no\n");
level--;
return E_FAIL;
}
if (memTranslate(THIS->proc->page_dir, (void*) (page + THIS->base)))
{
wprintf(L"CModulePe_ValidatePage: page at %x already mapped\n", page + THIS->base);
level--;
return S_OK;
}
sections = IMAGE_FIRST_SECTION(header);
for (i = 0; i < header->FileHeader.NumberOfSections; i++)
{
/*wprintf(L"%8S\t%x -> %x\n", header->section_header[i].Name,
header->section_header[i].VirtualAddress,
header->section_header[i].VirtualAddress +
header->section_header[i].Misc.VirtualSize);*/
if (page >= sections[i].VirtualAddress &&
(page < sections[i].VirtualAddress +
sections[i].Misc.VirtualSize ||
((sections[i].Characteristics &
IMAGE_SCN_CNT_UNINITIALIZED_DATA) && section == -1)))
{
/* the bss seems to be stuck onto the end of the data section,
at least with the Microsoft linker */
section = i;
break;
}
}
if (section == -1)
{
wprintf(L"CModulePe_ValidatePage: no section found at %x\n", THIS->base + page);
level--;
return E_FAIL;
}
wprintf(L"ok\n");
section_header = sections + section;
sect_virt = (void*) (section_header->VirtualAddress + THIS->base);
priv = THIS->proc->level | MEM_COMMIT /*| MEM_ZERO*/;
if (section_header->Characteristics & IMAGE_SCN_MEM_READ)
priv |= MEM_READ;
if (section_header->Characteristics & IMAGE_SCN_MEM_WRITE)
priv |= MEM_WRITE;
//if (section_header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
//pages = header->opt_head.SizeOfUninitializedData / PAGE_SIZE + 1;
//else
pages = section_header->Misc.VirtualSize / PAGE_SIZE + 1;
wprintf(L"[%d] Paging in section at %p (%d page(s))\n", level, sect_virt, pages);
/*phys = memAlloc(pages);
if (!phys)
return E_FAIL;
memMap(THIS->proc->page_dir, (addr_t) sect_virt, (addr_t) phys, pages, priv);
for (i = 0; i < pages; i++)
invalidate_page((byte*) sect_virt + i * PAGE_SIZE);
//if (section_header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
i386_lmemset32((addr_t) phys, 0, pages * PAGE_SIZE);*/
if (!vmmAlloc(THIS->proc, pages, (addr_t) sect_virt, priv))
return E_FAIL;
//else
memcpy(sect_virt, THIS->image_data + section_header->PointerToRawData,
section_header->SizeOfRawData);
if (!THIS->imported)
{
hr = peImport(THIS, header->OptionalHeader.DataDirectory, sect_virt,
section_header->VirtualAddress,
section_header->SizeOfRawData);
if (FAILED(hr))
return hr;
}
hr = peRelocateSection(THIS, header->OptionalHeader.DataDirectory, sect_virt,
section_header->VirtualAddress,
section_header->SizeOfRawData);
if (FAILED(hr))
return hr;
if (strncmp(section_header->Name, ".info", 8) == 0)
{
info = (module_info_t*) sect_virt;
info->loader_version = 0x0100;
info->next = NULL;
info->base = THIS->base;
info->length = header->OptionalHeader.SizeOfImage;
wcscpy(info->name, THIS->name);
if (THIS->proc->module_last)
THIS->proc->module_last->next = info;
THIS->proc->module_last = info;
if (!THIS->proc->info->module_first)
THIS->proc->info->module_first = info;
}
wprintf(L"[%d] CModulePe: done\n", level);
level--;
return S_OK;
}
const struct IModuleVtbl CModulePe_IModule_vtbl =
{
CModulePe_QueryInterface,
CModulePe_AddRef,
CModulePe_Release,
CModulePe_GetEntryPoint,
CModulePe_GetBase,
CModulePe_GetProcAddress,
CModulePe_GetName
};
const struct IPagerVtbl CModulePe_IPager_vtbl =
{
CModulePe_Pager_QueryInterface,
CModulePe_Pager_AddRef,
CModulePe_Pager_Release,
CModulePe_ValidatePage
};
IModule* modLoadPeMemory(process_t* proc, const wchar_t* file, const void* ptr, dword base)
{
const IMAGE_DOS_HEADER* dos_head;
const IMAGE_PE_HEADERS *header;
CModulePe* mod;
dos_head = (IMAGE_DOS_HEADER*) ptr;
//wprintf(L"file = %s, header = %p\n", file, dos_head);
if (dos_head->e_magic != IMAGE_DOS_SIGNATURE)
{
wprintf(L"unknown type of file: %c%c%c%c%c%c (ptr = %p)\n",
((byte*) ptr)[0],
((byte*) ptr)[1],
((byte*) ptr)[2],
((byte*) ptr)[3],
((byte*) ptr)[4],
((byte*) ptr)[5],
dos_head, ptr);
return NULL;
}
header = (const IMAGE_PE_HEADERS*) ((byte*) dos_head + dos_head->e_lfanew);
if (header->Signature != IMAGE_NT_SIGNATURE)
{
switch ((unsigned short)header->Signature)
{
case IMAGE_DOS_SIGNATURE:
_cputws(L"(MS-DOS signature)");
break;
case IMAGE_OS2_SIGNATURE:
_cputws(L"(Win16 or OS/2 signature)");
break;
case IMAGE_OS2_SIGNATURE_LE:
_cputws(L"(Win16, OS/2 or VxD signature)");
break;
default:
_cputws(L"(unknown signature, probably MS-DOS)");
break;
}
return NULL;
}
mod = hndAlloc(sizeof(CModulePe), proc);
if (!mod)
return NULL;
mod->refs = 0;
mod->mod.vtbl = &CModulePe_IModule_vtbl;
mod->pager.vtbl = &CModulePe_IPager_vtbl;
wcscpy(mod->name, file);
mod->base = NULL;
mod->image_data = (byte*) ptr;
mod->pref_base = header->OptionalHeader.ImageBase;
if (base)
mod->base = base;
else
mod->base = header->OptionalHeader.ImageBase;
/*wprintf(L"%s: headers = %p, base = %x, prefered base = %x\n",
file, header, base, header->OptionalHeader.ImageBase);
wprintf(L"sizeof(FileHeader) = %d (should be 20)\n", sizeof(header->FileHeader));
wprintf(L"offsetof(OptionalHeader) = %d (should be 24)\n",
(byte*) &header->OptionalHeader - (byte*) header);*/
mod->proc = proc;
mod->entry = mod->base + header->OptionalHeader.AddressOfEntryPoint;
wcscpy(mod->name, file);
mod->imported = false;
proc->modules[proc->num_modules] = &mod->mod;
proc->num_modules++;
return &mod->mod;
}
//! Loads the module specified into the kernel, preferably at the
//! specified base address.
/*!
* \param proc The process into which the module will be loaded. At
* time, the function may not be executing in the context of this
* process (such as when the module is being used as the core
* executable).
* \param file The name of the file containing the module.
* \param base The base address where the module should be loaded. If
* this is NULL then the module's preferred load address may be used.
* \return S_OK if the module was loaded successfuly, or a failure code.
*/
IModule* modLoadPe(process_t* proc, const wchar_t* file, dword base)
{
void* ptr;
int i;
wchar_t name[20];
for (i = 0; i < proc->num_modules; i++)
{
proc->modules[i]->vtbl->GetName(proc->modules[i], name, countof(name));
if (wcsicmp(name, file) == 0)
{
//wprintf(L"%s: already loaded\n", file);
return proc->modules[i];
}
}
ptr = ramOpen(file);
if (!ptr)
{
//wprintf(L"%s not found in ramdisk\n", file);
return NULL;
}
return modLoadPeMemory(proc, file, ptr, base);
}