379 lines
10 KiB
C++
379 lines
10 KiB
C++
/*
|
|
* Mr. 4th Dimention - Allen Webster
|
|
*
|
|
* 20.11.2015
|
|
*
|
|
* DLL loader declarations for 4coder
|
|
*
|
|
*/
|
|
|
|
// TOP
|
|
|
|
// TODO(allen):
|
|
// Check the relocation table, if it contains anything that
|
|
// is platform specific generate an error to avoid calling
|
|
// into invalid code.
|
|
|
|
i32
|
|
dll_compare(char *a, char *b, i32 len){
|
|
i32 result;
|
|
char *e;
|
|
|
|
result = 0;
|
|
e = a + len;
|
|
for (;a < e && *a == *b; ++a, ++b);
|
|
if (a < e){
|
|
if (*a < *b) result = -1;
|
|
else result = 1;
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
enum DLL_Error{
|
|
dll_err_too_small_for_header = 1,
|
|
dll_err_wrong_MZ_signature,
|
|
dll_err_wrong_DOS_error,
|
|
dll_err_wrong_PE_signature,
|
|
dll_err_unrecognized_bit_signature,
|
|
};
|
|
|
|
b32
|
|
dll_parse_headers(Data file, DLL_Data *dll, i32 *error){
|
|
b32 result;
|
|
i32 pe_offset;
|
|
i32 read_pos;
|
|
|
|
result = 1;
|
|
if (file.size <= sizeof(DOS_Header) + DOS_error_size){
|
|
if (error) *error = dll_err_too_small_for_header;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
dll->dos_header = (DOS_Header*)file.data;
|
|
|
|
if (dll_compare(dll->dos_header->signature, "MZ", 2) != 0){
|
|
if (error) *error = dll_err_wrong_MZ_signature;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
if (file.size <= DOS_error_offset + DOS_error_size){
|
|
if (error) *error = dll_err_too_small_for_header;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
if (dll_compare((char*)(file.data + DOS_error_offset), DOS_error_message,
|
|
sizeof(DOS_error_message) - 1) != 0){
|
|
if (error) *error = dll_err_wrong_DOS_error;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
pe_offset = dll->dos_header->e_lfanew;
|
|
read_pos = pe_offset;
|
|
|
|
if (file.size <= read_pos + PE_header_size){
|
|
if (error) *error = dll_err_too_small_for_header;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
if (dll_compare((char*)(file.data + read_pos),
|
|
PE_header, PE_header_size) != 0){
|
|
if (error) *error = dll_err_wrong_PE_signature;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
read_pos += PE_header_size;
|
|
|
|
if (file.size <= read_pos + sizeof(COFF_Header)){
|
|
if (error) *error = dll_err_too_small_for_header;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
dll->coff_header = (COFF_Header*)(file.data + read_pos);
|
|
read_pos += sizeof(COFF_Header);
|
|
|
|
if (file.size <= read_pos + dll->coff_header->size_of_optional_header){
|
|
if (error) *error = dll_err_too_small_for_header;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
dll->opt_header_32 = (PE_Opt_Header_32Bit*)(file.data + read_pos);
|
|
dll->opt_header_64 = (PE_Opt_Header_64Bit*)(file.data + read_pos);
|
|
read_pos += dll->coff_header->size_of_optional_header;
|
|
|
|
if (dll->opt_header_32->signature != bitsig_32bit &&
|
|
dll->opt_header_32->signature != bitsig_64bit){
|
|
if (error) *error = dll_err_unrecognized_bit_signature;
|
|
result = 0;
|
|
goto dll_parse_end;
|
|
}
|
|
|
|
if (dll->opt_header_32->signature == bitsig_32bit) dll->is_64bit = 0;
|
|
else dll->is_64bit = 1;
|
|
|
|
dll->section_defs = (PE_Section_Definition*)(file.data + read_pos);
|
|
|
|
dll_parse_end:
|
|
return(result);
|
|
}
|
|
|
|
i32
|
|
dll_total_loaded_size(DLL_Data *dll){
|
|
COFF_Header *coff_header;
|
|
PE_Section_Definition *section_def;
|
|
i32 result, section_end, i;
|
|
|
|
coff_header = dll->coff_header;
|
|
section_def = dll->section_defs;
|
|
result = 0;
|
|
|
|
for (i = 0; i < coff_header->number_of_sections; ++i, ++section_def){
|
|
section_end = section_def->loaded_location + section_def->loaded_size;
|
|
if (section_end > result){
|
|
result = section_end;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
b32
|
|
dll_perform_reloc(DLL_Loaded *loaded){
|
|
Data img;
|
|
byte *base;
|
|
Relocation_Block_Header *header;
|
|
Relocation_Block_Entry *entry;
|
|
Data_Directory *data_directory;
|
|
u32 cursor;
|
|
u32 bytes_in_table;
|
|
u32 block_end;
|
|
u32 type;
|
|
u32 offset;
|
|
b32 result;
|
|
b32 highadj_stage;
|
|
|
|
u64 dif64;
|
|
|
|
result = 1;
|
|
img = loaded->img;
|
|
if (loaded->is_64bit){
|
|
data_directory = loaded->opt_header_64->data_directory;
|
|
dif64 = ((u64)img.data - (u64)loaded->opt_header_64->image_base);
|
|
}
|
|
else{
|
|
data_directory = loaded->opt_header_32->data_directory;
|
|
dif64 = ((u64)img.data - (u64)loaded->opt_header_32->image_base);
|
|
}
|
|
data_directory += image_dir_base_reloc_table;
|
|
base = img.data + data_directory->virtual_address;
|
|
bytes_in_table = data_directory->size;
|
|
|
|
highadj_stage = 1;
|
|
|
|
|
|
for (cursor = 0; cursor < bytes_in_table;){
|
|
header = (Relocation_Block_Header*)(base + cursor);
|
|
block_end = cursor + header->block_size;
|
|
cursor += sizeof(Relocation_Block_Header);
|
|
|
|
for (;cursor < block_end;){
|
|
entry = (Relocation_Block_Entry*)(base + cursor);
|
|
cursor += sizeof(Relocation_Block_Entry);
|
|
|
|
type = (u32)(entry->entry & reloc_entry_type_mask) >> reloc_entry_type_shift;
|
|
offset = (u32)(entry->entry & reloc_entry_offset_mask) + header->page_base_offset;
|
|
|
|
switch (type){
|
|
case image_base_absolute: break;
|
|
|
|
case image_base_high:
|
|
case image_base_low:
|
|
case image_base_highlow:
|
|
case image_base_highadj:
|
|
case image_base_arm_mov32a:
|
|
case image_base_arm_mov32t:
|
|
case image_base_mips_jmpaddr16:
|
|
result = 0;
|
|
goto dll_reloc_end;
|
|
|
|
case image_base_dir64:
|
|
*(u64*)(img.data + offset) += dif64;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
dll_reloc_end:
|
|
return(result);
|
|
}
|
|
|
|
b32
|
|
dll_load_sections(Data img, DLL_Loaded *loaded,
|
|
Data file, DLL_Data *dll){
|
|
COFF_Header *coff_header;
|
|
PE_Section_Definition *section_def;
|
|
u32 header_size;
|
|
u32 size;
|
|
u32 i;
|
|
|
|
coff_header = dll->coff_header;
|
|
section_def = dll->section_defs;
|
|
|
|
header_size =
|
|
(u32)((byte*)(section_def + coff_header->number_of_sections) - file.data);
|
|
|
|
memcpy(img.data, file.data, header_size);
|
|
memset(img.data + header_size, 0, img.size - header_size);
|
|
|
|
for (i = 0; i < coff_header->number_of_sections; ++i, ++section_def){
|
|
size = section_def->loaded_size;
|
|
if (size > section_def->disk_size)
|
|
size = section_def->disk_size;
|
|
|
|
memcpy(img.data + section_def->loaded_location,
|
|
file.data + section_def->disk_location,
|
|
size);
|
|
|
|
if (dll_compare(section_def->name, ".text", 5) == 0){
|
|
loaded->text_start = section_def->loaded_location;
|
|
loaded->text_size = section_def->loaded_size;
|
|
}
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
void
|
|
dll_load(Data img, DLL_Loaded *loaded, Data file, DLL_Data *dll){
|
|
Data_Directory *export_dir;
|
|
|
|
dll_load_sections(img, loaded, file, dll);
|
|
loaded->img = img;
|
|
|
|
loaded->dos_header = (DOS_Header*)((byte*)img.data + ((byte*)dll->dos_header - file.data));
|
|
loaded->coff_header = (COFF_Header*)((byte*)img.data + ((byte*)dll->coff_header - file.data));
|
|
|
|
loaded->opt_header_32 = (PE_Opt_Header_32Bit*)
|
|
((byte*)img.data + ((byte*)dll->opt_header_32 - file.data));
|
|
loaded->opt_header_64 = (PE_Opt_Header_64Bit*)
|
|
((byte*)img.data + ((byte*)dll->opt_header_64 - file.data));
|
|
|
|
loaded->section_defs = (PE_Section_Definition*)
|
|
((byte*)img.data + ((byte*)dll->section_defs - file.data));
|
|
|
|
loaded->is_64bit = dll->is_64bit;
|
|
|
|
if (dll->is_64bit){
|
|
export_dir = dll->opt_header_64->data_directory;
|
|
}
|
|
else{
|
|
export_dir = dll->opt_header_32->data_directory;
|
|
}
|
|
export_dir += image_dir_entry_export;
|
|
loaded->export_start = export_dir->virtual_address;
|
|
|
|
dll_perform_reloc(loaded);
|
|
}
|
|
|
|
void*
|
|
dll_load_function(DLL_Loaded *dll, char *func_name, i32 size){
|
|
Data img;
|
|
DLL_Export_Directory_Table *export_dir;
|
|
DLL_Export_Address *address_ptr;
|
|
DLL_Export_Name *name_ptr;
|
|
void *result;
|
|
u32 count, i;
|
|
u32 result_offset;
|
|
u32 ordinal;
|
|
|
|
img = dll->img;
|
|
export_dir = (DLL_Export_Directory_Table*)(img.data + dll->export_start);
|
|
|
|
count = export_dir->number_of_name_pointers;
|
|
name_ptr = (DLL_Export_Name*)(img.data + export_dir->name_pointer_offset);
|
|
|
|
result = 0;
|
|
for (i = 0; i < count; ++i, ++name_ptr){
|
|
if (dll_compare((char*)img.data + name_ptr->name_offset,
|
|
func_name, size) == 0){
|
|
ordinal = ((u16*)(img.data + export_dir->ordinal_offset))[i];
|
|
#if 0
|
|
// NOTE(allen): The MS docs say to do this, but
|
|
// it appears to just be downright incorrect.
|
|
ordinal -= export_dir->ordinal_base;
|
|
#endif
|
|
address_ptr = (DLL_Export_Address*)(img.data + export_dir->address_offset);
|
|
address_ptr += ordinal;
|
|
result_offset = address_ptr->export_offset;
|
|
result = (img.data + result_offset);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
#define MachineCase(x) case x: result = #x; *len = sizeof(#x) - 1; break
|
|
|
|
char*
|
|
dll_machine_type_str(u16 machine, i32 *len){
|
|
char *result;
|
|
i32 extra;
|
|
|
|
if (!len) len = &extra;
|
|
result = 0;
|
|
|
|
switch (machine){
|
|
MachineCase(intel_i386);
|
|
MachineCase(intel_i860);
|
|
|
|
MachineCase(mips_r3000);
|
|
MachineCase(mips_little_endian);
|
|
MachineCase(mips_r10000);
|
|
|
|
MachineCase(old_alpha_axp);
|
|
MachineCase(alpha_axp);
|
|
|
|
MachineCase(hitachi_sh3);
|
|
MachineCase(hitachi_sh3_dsp);
|
|
MachineCase(hitachi_sh4);
|
|
MachineCase(hitachi_sh5);
|
|
|
|
MachineCase(arm_little_endian);
|
|
MachineCase(thumb);
|
|
|
|
MachineCase(matsushita_am33);
|
|
MachineCase(power_pc_little_endian);
|
|
MachineCase(power_pc_with_floating);
|
|
|
|
MachineCase(intel_ia64);
|
|
MachineCase(mips16);
|
|
MachineCase(motorola_68000_series);
|
|
|
|
MachineCase(alpha_axp_64_bit);
|
|
|
|
MachineCase(mips_with_fpu);
|
|
MachineCase(mips16_with_fpu);
|
|
MachineCase(eft_byte_code);
|
|
|
|
MachineCase(amd_amd64);
|
|
MachineCase(mitsubishi_m32r_little_endian);
|
|
MachineCase(clr_pure_msil);
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
#undef MachineCase
|
|
|
|
// BOTTOM
|
|
|