kolibrios/kernel/branches/kolibri_pe/core/pe.c
Sergey Semyonov (Serge) a20b1c888d kolibri_pe: the latest 32-bit version
git-svn-id: svn://kolibrios.org@2971 a494cfbc-eb01-0410-851d-a64ba20cac60
2012-09-04 22:16:57 +00:00

425 lines
11 KiB
C

#include <types.h>
#include <core.h>
#include <spinlock.h>
#include <link.h>
#include <mm.h>
#include <slab.h>
#include <pe.h>
static inline bool IsPowerOf2(u32_t val)
{
if(val == 0)
return false;
return (val & (val - 1)) == 0;
}
static inline void sec_copy(addr_t dst, addr_t src, size_t len)
{
u32_t tmp;
__asm__ __volatile__ (
"shrl $2, %%ecx \n\t"
"rep movsl"
:"=c"(tmp),"=S"(tmp),"=D"(tmp)
:"c"(len),"S"(src),"D"(dst)
:"cc");
};
static inline void sec_clear(addr_t dst, size_t len)
{
u32_t tmp;
__asm__ __volatile__ (
"xorl %%eax, %%eax \n\t"
"rep stosb"
:"=c"(tmp),"=D"(tmp)
:"c"(len),"D"(dst)
:"eax","cc");
};
int __stdcall strncmp(const char *s1, const char *s2, size_t n);
bool link_image(addr_t img_base);
/*
void* __fastcall load_pe(const char *path)
{
md_t *md;
md = load_image(path);
if( md )
return (void*)md->base;
return NULL;
};
*/
bool validate_pe(void *raw, size_t raw_size, bool is_exec)
{
PIMAGE_DOS_HEADER dos;
PIMAGE_NT_HEADERS32 nt;
dos = (PIMAGE_DOS_HEADER)raw;
if( !raw || raw_size < sizeof(IMAGE_DOS_HEADER) )
return false;
if( dos->e_magic != IMAGE_DOS_SIGNATURE || dos->e_lfanew <= 0)
return false;
nt = MakePtr( PIMAGE_NT_HEADERS32, dos, dos->e_lfanew);
if( (addr_t)nt < (addr_t)raw)
return false;
if(nt->Signature != IMAGE_NT_SIGNATURE)
return false;
if(nt->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
return false;
if(is_exec && (nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
return false;
if(nt->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
return false;
if( is_exec && nt->OptionalHeader.ImageBase != 0)
return false;
if(nt->OptionalHeader.SectionAlignment < PAGE_SIZE)
{
if(nt->OptionalHeader.FileAlignment != nt->OptionalHeader.SectionAlignment)
return false;
}
else if(nt->OptionalHeader.SectionAlignment < nt->OptionalHeader.FileAlignment)
return false;
if(!IsPowerOf2(nt->OptionalHeader.SectionAlignment) ||
!IsPowerOf2(nt->OptionalHeader.FileAlignment))
return false;
if(nt->FileHeader.NumberOfSections > 96)
return false;
return true;
}
addr_t __fastcall load_image(const char *path)
{
PIMAGE_DOS_HEADER dos;
PIMAGE_NT_HEADERS32 nt;
// md_t *img_md;
size_t img_size;
addr_t img_base;
count_t img_pages;
size_t raw_size = 0;
void *raw;
DBG("\nload image %s", path);
raw = load_file(path, &raw_size);
DBG(" raw = %x\n", raw);
if( ! raw)
{
DBG("file not found: %s\n", path);
return NULL;
};
if( ! validate_pe(raw, raw_size, false) )
{
DBG("invalid pe file %s\n", path);
mem_free(raw);
return NULL;
}
dos = (PIMAGE_DOS_HEADER)raw;
nt = MakePtr( PIMAGE_NT_HEADERS32, dos, dos->e_lfanew);
img_size = nt->OptionalHeader.SizeOfImage;
// img_md = md_alloc(img_size, PG_SW);
img_base = mem_alloc(img_size, PG_SW);
if( !img_base)
{
mem_free(raw);
return NULL;
};
// img_base = img_md->base;
create_image(img_base, (addr_t)raw, true);
mem_free(raw);
// dos = (PIMAGE_DOS_HEADER)img_base;
// nt = MakePtr( PIMAGE_NT_HEADERS32, dos, dos->e_lfanew);
return img_base;
};
/*
addr_t get_proc_addr(addr_t module, char *name)
{
PIMAGE_DOS_HEADER expdos;
PIMAGE_NT_HEADERS32 expnt;
PIMAGE_EXPORT_DIRECTORY exp;
u32_t *functions;
char **funcname;
int ind;
expdos = (PIMAGE_DOS_HEADER)module;
expnt = MakePtr( PIMAGE_NT_HEADERS32, expdos, expdos->e_lfanew);
exp = MakePtr(PIMAGE_EXPORT_DIRECTORY,module,
expnt->OptionalHeader.DataDirectory[0].VirtualAddress);
functions = MakePtr(DWORD*,exp->AddressOfFunctions,module);
funcname = MakePtr(char**,exp->AddressOfNames,module);
for(ind=0; *funcname;funcname++,ind++)
{
if(!strcmp(name,MakePtr(char*,*funcname,module)))
return functions[ind] + module;
};
return -1;
};
*/
void create_image(addr_t img_base, addr_t raw, bool force_clear)
{
PIMAGE_DOS_HEADER dos;
PIMAGE_NT_HEADERS32 nt;
PIMAGE_SECTION_HEADER img_sec;
u32_t sec_align;
int i;
/* assumed that image is valid */
dos = (PIMAGE_DOS_HEADER)raw;
nt = MakePtr( PIMAGE_NT_HEADERS32, dos, dos->e_lfanew);
sec_copy(img_base, raw, nt->OptionalHeader.SizeOfHeaders);
img_sec = MakePtr(PIMAGE_SECTION_HEADER, nt, sizeof(IMAGE_NT_HEADERS32));
sec_align = nt->OptionalHeader.SectionAlignment;
for(i=0; i< nt->FileHeader.NumberOfSections; i++)
{
addr_t src_ptr;
addr_t dest_ptr;
size_t sec_size;
src_ptr = MakePtr(addr_t, raw, img_sec->PointerToRawData);
dest_ptr = MakePtr(addr_t,img_base, img_sec->VirtualAddress);
if(img_sec->SizeOfRawData)
sec_copy(dest_ptr, src_ptr, img_sec->SizeOfRawData);
if(force_clear)
{
sec_size = (img_sec->Misc.VirtualSize + sec_align -1) & -sec_align;
if(sec_size > img_sec->SizeOfRawData)
sec_clear(dest_ptr + img_sec->SizeOfRawData,
sec_size - img_sec->SizeOfRawData);
};
img_sec++;
};
if(nt->OptionalHeader.DataDirectory[5].Size)
{
PIMAGE_BASE_RELOCATION reloc;
/* FIXME addr_t */
u32_t delta = (u32_t)img_base - nt->OptionalHeader.ImageBase;
reloc = MakePtr(PIMAGE_BASE_RELOCATION, img_base,
nt->OptionalHeader.DataDirectory[5].VirtualAddress);
while ( reloc->SizeOfBlock != 0 )
{
u32_t cnt;
u16_t *entry;
u16_t reltype;
u32_t offs;
cnt = (reloc->SizeOfBlock - sizeof(*reloc))/sizeof(u16_t);
entry = MakePtr( u16_t*, reloc, sizeof(*reloc) );
for ( i=0; i < cnt; i++ )
{
u16_t *p16;
u32_t *p32;
reltype = (*entry & 0xF000) >> 12;
offs = (*entry & 0x0FFF) + reloc->VirtualAddress;
switch(reltype)
{
case 1:
p16 = MakePtr(u16_t*, img_base, offs);
*p16+= (u16_t)(delta>>16);
break;
case 2:
p16 = MakePtr(u16_t*, img_base, offs);
*p16+= (u16_t)delta;
break;
case 3:
p32 = MakePtr(u32_t*, img_base, offs);
*p32+= delta;
}
entry++;
}
reloc = MakePtr(PIMAGE_BASE_RELOCATION, reloc,reloc->SizeOfBlock);
}
};
DBG("\ncreate pe base %x, size %x, %d sections\n\n",img_base,
nt->OptionalHeader.SizeOfImage, nt->FileHeader.NumberOfSections);
};
bool link_image(addr_t img_base)
{
PIMAGE_DOS_HEADER dos;
PIMAGE_NT_HEADERS32 nt;
int warn = 0;
/* assumed that image is valid */
dos = (PIMAGE_DOS_HEADER)img_base;
nt = MakePtr( PIMAGE_NT_HEADERS32, dos, dos->e_lfanew);
if(nt->OptionalHeader.DataDirectory[1].Size)
{
PIMAGE_IMPORT_DESCRIPTOR imp;
imp = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, img_base,
nt->OptionalHeader.DataDirectory[1].VirtualAddress);
while ( 1 )
{
PIMAGE_THUNK_DATA32 thunk;
PIMAGE_DOS_HEADER expdos;
PIMAGE_NT_HEADERS32 expnt;
PIMAGE_EXPORT_DIRECTORY exp;
u32_t *iat;
char *libname;
addr_t *functions;
u16_t *ordinals;
char **funcname;
dll_t *exp_dll;
if ( (imp->TimeDateStamp==0 ) && (imp->Name==0) )
break;
libname=MakePtr(char*,imp->Name, img_base);
DBG("import from %s\n",libname);
exp_dll = find_dll(&core_dll.link, libname);
if(exp_dll != NULL)
{
DBG("find %s\n", exp_dll->img_name);
}
else
{
DBG("can't find %s\n", libname);
return false;
}
exp = exp_dll->img_exp;
functions = MakePtr(DWORD*,exp->AddressOfFunctions,exp_dll->img_base);
ordinals = MakePtr(WORD*, exp->AddressOfNameOrdinals,exp_dll->img_base);
funcname = MakePtr(char**, exp->AddressOfNames,exp_dll->img_base);
thunk = MakePtr(PIMAGE_THUNK_DATA32,
imp->Characteristics, img_base);
iat= MakePtr(DWORD*,imp->FirstThunk, img_base);
while ( 1 ) // Loop forever (or until we break out)
{
PIMAGE_IMPORT_BY_NAME ord;
addr_t addr;
if ( thunk->u1.AddressOfData == 0 )
break;
if ( thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG )
{
// printf(" %4u\n", thunk->u1.Ordinal & 0xFFFF);
break;
}
else
{
ord = MakePtr(PIMAGE_IMPORT_BY_NAME,
thunk->u1.AddressOfData, img_base);
*iat=0;
DBG("import %s", ord->Name);
if(strncmp(ord->Name,
MakePtr(char*,funcname[ord->Hint],exp_dll->img_base),32))
{
int ind;
char **names=funcname;
for(names = funcname,ind = 0;
ind < exp->NumberOfNames; names++,ind++)
{
if(!strncmp(ord->Name,MakePtr(char*,*names,exp_dll->img_base),32))
{
u16_t ordinal;
ordinal = ordinals[ind];
DBG(" \t\tat %x\n", functions[ordinal] + exp_dll->img_base);
*iat = functions[ordinal] + exp_dll->img_base;
break;
};
};
if(ind == exp->NumberOfNames)
{
DBG(" unresolved import %s\n",ord->Name);
warn=1;
};
}
else
{
DBG(" \tat %x\n", functions[ord->Hint] + exp_dll->img_base);
*iat = functions[ord->Hint] + exp_dll->img_base;
};
};
thunk++; // Advance to next thunk
iat++;
}
imp++; // advance to next IMAGE_IMPORT_DESCRIPTOR
};
};
if ( !warn )
return true;
else
return false;
}