Blame src/exports/pe_get_export_symbol_info.c

feffc7
/*****************************************************************************/
feffc7
/*  pemagination: a (virtual) tour into portable bits and executable bytes   */
740419
/*  Copyright (C) 2013--2020  Z. Gilboa                                      */
feffc7
/*  Released under GPLv2 and GPLv3; see COPYING.PEMAGINE.                    */
feffc7
/*****************************************************************************/
feffc7
feffc7
#include <psxtypes/psxtypes.h>
feffc7
#include <pemagine/pe_consts.h>
feffc7
#include <pemagine/pe_structs.h>
feffc7
#include <pemagine/pemagine.h>
feffc7
#include "pe_impl.h"
feffc7
feffc7
static __inline__ int pe_addr_within_bounds(void * addr, void * bottom, void * top)
feffc7
{
feffc7
	return (((uintptr_t)addr >= (uintptr_t)bottom) && ((uintptr_t)addr < (uintptr_t)top));
feffc7
}
feffc7
3a9844
static void * pe_resolve_forwarder_rva(char * forwarder)
3a9844
{
3a9844
	uint16_t	soname[512];
3a9844
	uint16_t *	wch;
3a9844
	char *		mark;
3a9844
	char *		dot;
3a9844
	void *		base;
3a9844
3a9844
	for (mark=forwarder; *mark; mark++)
3a9844
		(void)0;
3a9844
3a9844
	if (mark - forwarder + 5 > 512)
3a9844
		return 0;
3a9844
3a9844
	for (dot=&mark[-1]; dot>forwarder && *dot!='.'; dot--)
3a9844
		(void)0;
3a9844
3a9844
	if (*dot != '.')
3a9844
		return 0;
3a9844
3a9844
	mark = forwarder;
3a9844
	wch  = soname;
3a9844
3a9844
	for (; mark
3a9844
		*wch++ = *mark++;
3a9844
3a9844
	if (mark < dot)
3a9844
		return 0;
3a9844
3a9844
	*wch++ = '.';
3a9844
	*wch++ = 'd';
3a9844
	*wch++ = 'l';
3a9844
	*wch++ = 'l';
3a9844
	*wch++ = 0;
3a9844
3a9844
	if (!(base = pe_get_module_handle(soname)))
3a9844
		return 0;
3a9844
3a9844
	return pe_get_procedure_address(base,++dot);
3a9844
}
3a9844
feffc7
int pe_get_export_symbol_info(
feffc7
	const void *		base,
feffc7
	const char *		name,
feffc7
	struct pe_export_sym *	sym)
feffc7
{
8c8440
	struct pe_raw_export_hdr * exp_hdr;
feffc7
8860e9
	ssize_t		len;
feffc7
	uint32_t *	rva_offset;
feffc7
	uintptr_t *	addr_offset;
8c8440
	uintptr_t	addr;
feffc7
feffc7
	uint32_t	sec_size;
feffc7
	void *		sec_addr_cap;
feffc7
feffc7
	uint32_t *	fn_addr;
feffc7
	uint32_t *	fn_names;
feffc7
	uint16_t *	fn_ordinals;
feffc7
	uint32_t *	addr_entries;
feffc7
	uint32_t *	name_entries;
feffc7
feffc7
	char *		exp_name;
feffc7
	const char *	src;
feffc7
	const char *	dst;
6b456b
	ssize_t		match;
feffc7
feffc7
	uint32_t	lower;
feffc7
	uint32_t	upper;
feffc7
	uint32_t	idx;
feffc7
8c8440
	/* init */
8c8440
	sym->ordinal_base	= 0;
8c8440
	sym->ordinal		= 0;
8c8440
	sym->addr		= 0;
8c8440
	sym->forwarder_rva	= 0;
8c8440
	sym->name		= 0;
feffc7
	sym->status		= 0;
feffc7
feffc7
	if (!(exp_hdr = pe_get_image_export_hdr_addr(base,&sec_size)))
feffc7
		return -1;
feffc7
feffc7
	sec_addr_cap = pe_va_from_rva(exp_hdr,sec_size);
560637
	rva_offset   = (uint32_t *)(exp_hdr->eh_export_addr_tbl_rva);
feffc7
	fn_addr      = (uint32_t *)pe_va_from_rva(base,*rva_offset);
feffc7
560637
	rva_offset   = (uint32_t *)(exp_hdr->eh_name_ptr_rva);
8c8440
	fn_names     = (uint32_t *)pe_va_from_rva(base,*rva_offset);
feffc7
560637
	rva_offset   = (uint32_t *)(exp_hdr->eh_ordinal_tbl_rva);
8c8440
	fn_ordinals  = (uint16_t *)pe_va_from_rva(base,*rva_offset);
feffc7
560637
	addr_entries = (uint32_t *)exp_hdr->eh_addr_tbl_entries;
560637
	name_entries = (uint32_t *)exp_hdr->eh_num_of_name_ptrs;
feffc7
8c8440
feffc7
	/* by ordinal? */
feffc7
	if ((intptr_t)name < 0x10000) {
560637
		sym->ordinal_base = (uint32_t *)exp_hdr->eh_ordinal_base;
feffc7
feffc7
		/* the array is zero-based, but ordinals are normally one-based... */
feffc7
		if (((intptr_t)name - *sym->ordinal_base + 1) > *addr_entries)
feffc7
			return -1;
feffc7
8c8440
		addr        = ((uintptr_t)name - *sym->ordinal_base) * sizeof(uint32_t);
8c8440
		rva_offset  = (uint32_t *)pe_va_from_rva(fn_addr,addr);
feffc7
		addr_offset = (uintptr_t *)pe_va_from_rva(base,*rva_offset);
feffc7
feffc7
		if (pe_addr_within_bounds(addr_offset,exp_hdr,sec_addr_cap)) {
3a9844
			sym->forwarder_rva = addr_offset;
3a9844
			sym->addr = pe_resolve_forwarder_rva((char *)addr_offset);
3a9844
		} else {
feffc7
			sym->forwarder_rva = 0;
feffc7
			sym->addr = addr_offset;
3a9844
		}
feffc7
feffc7
		return 0;
feffc7
	}
feffc7
feffc7
	if ((len = pe_impl_strlen_ansi(name)) < 0)
feffc7
		return -1;
feffc7
feffc7
	len++;
feffc7
	lower  = 0;
feffc7
	upper  = *name_entries;
feffc7
feffc7
	while (lower < upper) {
8c8440
		idx        = (lower + upper) / 2;
feffc7
		rva_offset = (uint32_t *)pe_va_from_rva(fn_names,idx*sizeof(uint32_t));
feffc7
		exp_name   = (char *)pe_va_from_rva(base,*rva_offset);
14b4d2
feffc7
		src = name;
feffc7
		dst = exp_name;
feffc7
feffc7
		for (match=0; (match
feffc7
				src++;
feffc7
				dst++;
feffc7
		}
feffc7
feffc7
		if (match == len) {
560637
			sym->ordinal_base = (uint32_t *)exp_hdr->eh_ordinal_base;
feffc7
			sym->ordinal      = (uint16_t *)pe_va_from_rva(fn_ordinals,idx*sizeof(uint16_t));
feffc7
feffc7
			rva_offset  = (uint32_t *)pe_va_from_rva(fn_addr,(*sym->ordinal)*sizeof(uint32_t));
feffc7
			addr_offset = (uintptr_t *)pe_va_from_rva(base,*rva_offset);
feffc7
feffc7
			if (pe_addr_within_bounds(addr_offset,exp_hdr,sec_addr_cap)) {
3a9844
				sym->forwarder_rva = addr_offset;
3a9844
				sym->addr = pe_resolve_forwarder_rva((char *)addr_offset);
feffc7
			} else {
feffc7
				sym->forwarder_rva = 0;
feffc7
				sym->addr = addr_offset;
feffc7
			}
feffc7
feffc7
			return 0;
feffc7
		}
feffc7
feffc7
		else {
feffc7
			if (*src > *dst)
feffc7
				lower = idx + 1;
feffc7
			else
feffc7
				upper = idx;
feffc7
		}
feffc7
	}
feffc7
feffc7
	return -1;
feffc7
}