Ejemplo n.º 1
0
void setup_seh() {
#if 1
	/* Get the base of the module.  */
	//u8* __ImageBase = (u8*)GetModuleHandle(NULL);
	/* Current version is always 1 and we are registering an
	exception handler.  */
	unwind_info[0].Version = 1;
	unwind_info[0].Flags = UNW_FLAG_NHANDLER;
	/* We don't use the unwinding info so fill the structure with 0 values.  */
	unwind_info[0].SizeOfProlog = 0;
	unwind_info[0].CountOfCodes = 1;
	unwind_info[0].FrameOffset = 0;
	unwind_info[0].FrameRegister = 0;
	/* Add the exception handler.  */

	unwind_info[0].UnwindCode[0].CodeOffset = 0;
	unwind_info[0].UnwindCode[0].UnwindOp = 2;// UWOP_ALLOC_SMALL;
	unwind_info[0].UnwindCode[0].OpInfo = 0x20 / 8;
	
	//unwind_info[0].ExceptionHandler =
		//(DWORD)((u8 *)__gnat_SEH_error_handler - CodeCache);
	/* Set its scope to the entire program.  */
	Table[0].BeginAddress = 0;// (CodeCache - (u8*)__ImageBase);
	Table[0].EndAddress = /*(CodeCache - (u8*)__ImageBase) +*/ CODE_SIZE;
	Table[0].UnwindData = (DWORD)((u8 *)unwind_info - CodeCache);
	/* Register the unwind information.  */
	RtlAddFunctionTable(Table, 1, (DWORD64)CodeCache);
#endif

	//verify(RtlInstallFunctionTableCallback((unat)CodeCache | 0x3, (DWORD64)CodeCache, CODE_SIZE, seh_callback, 0, 0));
}
Ejemplo n.º 2
0
void PDataManager::RegisterPdata(RUNTIME_FUNCTION* pdataStart, _In_ const ULONG_PTR functionStart, _In_ const ULONG_PTR functionEnd, _Out_ PVOID* pdataTable, ULONG entryCount, ULONG maxEntryCount)
{
    BOOLEAN success = FALSE;
    if (AutoSystemInfo::Data.IsWin8OrLater())
    {
        Assert(pdataTable != NULL);

        // Since we do not expect many thunk functions to be created, we are using 1 table/function
        // for now. This can be optimized further if needed.
        DWORD status = NtdllLibrary::Instance->AddGrowableFunctionTable(pdataTable,
            pdataStart,
            entryCount,
            maxEntryCount,
            /*RangeBase*/ functionStart,
            /*RangeEnd*/ functionEnd);
        success = NT_SUCCESS(status);
        if (success)
        {
            Assert(pdataTable);
        }
    }
    else
    {
        *pdataTable = pdataStart;
        success = RtlAddFunctionTable(pdataStart, entryCount, functionStart);
    }
    Js::Throw::CheckAndThrowOutOfMemory(success);
}
Ejemplo n.º 3
0
    virtual void NotifyFunctionEmitted(const Function &F, void *Code,
                                       size_t Size, const EmittedFunctionDetails &Details)
    {
#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_)
        assert(!jl_in_stackwalk);
        jl_in_stackwalk = 1;
        uintptr_t catchjmp = (uintptr_t)Code+Size;
        *(uint8_t*)(catchjmp+0) = 0x48;
        *(uint8_t*)(catchjmp+1) = 0xb8; // mov RAX, QWORD PTR [...]
        *(uint64_t*)(catchjmp+2) = (uint64_t)&_seh_exception_handler;
        *(uint8_t*)(catchjmp+10) = 0xff;
        *(uint8_t*)(catchjmp+11) = 0xe0; // jmp RAX
        PRUNTIME_FUNCTION tbl = (PRUNTIME_FUNCTION)((catchjmp+12+3)&~(uintptr_t)3);
        uint8_t *UnwindData = (uint8_t*)((((uintptr_t)&tbl[1])+3)&~(uintptr_t)3);
        RUNTIME_FUNCTION fn = {0,(DWORD)Size+13,(DWORD)(intptr_t)(UnwindData-(uint8_t*)Code)};
        tbl[0] = fn;
        UnwindData[0] = 0x09; // version info, UNW_FLAG_EHANDLER
        UnwindData[1] = 4;    // size of prolog (bytes)
        UnwindData[2] = 2;    // count of unwind codes (slots)
        UnwindData[3] = 0x05; // frame register (rbp) = rsp
        UnwindData[4] = 4;    // second instruction
        UnwindData[5] = 0x03; // mov RBP, RSP
        UnwindData[6] = 1;    // first instruction
        UnwindData[7] = 0x50; // push RBP
        *(DWORD*)&UnwindData[8] = (DWORD)(catchjmp-(intptr_t)Code);
        DWORD mod_size = (DWORD)(size_t)(&UnwindData[8]-(uint8_t*)Code);
        if (!SymLoadModuleEx(GetCurrentProcess(), NULL, NULL, NULL, (DWORD64)Code, mod_size, NULL, SLMFLAG_VIRTUAL)) {
            static int warned = 0;
            if (!warned) {
                JL_PRINTF(JL_STDERR, "WARNING: failed to insert function info for backtrace\n");
                warned = 1;
            }
        }
        else {
            if (!SymAddSymbol(GetCurrentProcess(), (ULONG64)Code, F.getName().data(), (DWORD64)Code, mod_size, 0)) {
                JL_PRINTF(JL_STDERR, "WARNING: failed to insert function name into debug info\n");
            }
            if (!RtlAddFunctionTable(tbl,1,(DWORD64)Code)) {
                JL_PRINTF(JL_STDERR, "WARNING: failed to insert function stack unwind info\n");
            }
        }
        jl_in_stackwalk = 0;

        FuncInfo tmp = {&F, Size, std::string(), std::string(), tbl, Details.LineStarts};
#else
        FuncInfo tmp = {&F, Size, std::string(F.getName().data()), std::string(), Details.LineStarts};
#endif
        info[(size_t)(Code)] = tmp;
    }
Ejemplo n.º 4
0
/* static */
void XDataAllocator::Register(XDataAllocation * xdataInfo, ULONG_PTR functionStart, DWORD functionSize)
{
#ifdef _WIN32
    ULONG_PTR baseAddress = functionStart;
    xdataInfo->pdata.BeginAddress = (DWORD)(functionStart - baseAddress);
    xdataInfo->pdata.EndAddress = (DWORD)(xdataInfo->pdata.BeginAddress + functionSize);
    xdataInfo->pdata.UnwindInfoAddress = (DWORD)((intptr_t)xdataInfo->address - baseAddress);

    BOOLEAN success = FALSE;
    if (AutoSystemInfo::Data.IsWin8OrLater())
    {
        DWORD status = NtdllLibrary::Instance->AddGrowableFunctionTable(&xdataInfo->functionTable,
            &xdataInfo->pdata,
            /*MaxEntryCount*/ 1,
            /*Valid entry count*/ 1,
            /*RangeBase*/ functionStart,
            /*RangeEnd*/ functionStart + functionSize);
        success = NT_SUCCESS(status);
        if (success)
        {
            Assert(xdataInfo->functionTable != nullptr);
        }
    }
    else
    {
        success = RtlAddFunctionTable(&xdataInfo->pdata, 1, functionStart);
    }
    Js::Throw::CheckAndThrowOutOfMemory(success);

#if DBG
    // Validate that the PDATA registration succeeded
    ULONG64            imageBase = 0;
    RUNTIME_FUNCTION  *runtimeFunction = RtlLookupFunctionEntry((DWORD64)functionStart, &imageBase, nullptr);
    Assert(runtimeFunction != NULL);
#endif

#else  // !_WIN32
    Assert(ReadHead(xdataInfo->address));  // should be non-empty .eh_frame
    __register_frame(xdataInfo->address);
#endif
}
Ejemplo n.º 5
0
int
__mingw_init_ehandler (void)
{
  static int was_here = 0;
  size_t e = 0;
  PIMAGE_SECTION_HEADER pSec;
  PBYTE _ImageBase = _GetPEImageBase ();
  
  if (was_here || !_ImageBase)
    return was_here;
  was_here = 1;
  if (_FindPESectionByName (".pdata") != NULL)
    return 1;

  /* Allocate # of e tables and entries.  */
  memset (emu_pdata, 0, sizeof (RUNTIME_FUNCTION) * MAX_PDATA_ENTRIES);
  memset (emu_xdata, 0, sizeof (UNWIND_INFO) * MAX_PDATA_ENTRIES);
    
  e = 0;
  /* Fill tables and entries.  */
  while (e < MAX_PDATA_ENTRIES && (pSec = _FindPESectionExec (e)) != NULL)
    {
      emu_xdata[e].VersionAndFlags = 9; /* UNW_FLAG_EHANDLER | UNW_VERSION */
      emu_xdata[e].AddressOfExceptionHandler =
	(DWORD)(size_t) ((LPBYTE)__mingw_SEH_error_handler - _ImageBase);
      emu_pdata[e].BeginAddress = pSec->VirtualAddress;
      emu_pdata[e].EndAddress = pSec->VirtualAddress + pSec->Misc.VirtualSize;
      emu_pdata[e].UnwindData =
	(DWORD)(size_t)((LPBYTE)&emu_xdata[e] - _ImageBase);
      ++e;
    }
#ifdef _DEBUG_CRT
  if (!e || e > MAX_PDATA_ENTRIES)
    abort ();
#endif
  /* RtlAddFunctionTable.  */
  if (e != 0)
    RtlAddFunctionTable (emu_pdata, e, (DWORD64)_ImageBase);
  return 1;
}
Ejemplo n.º 6
0
	void registerSEHUnwindInfo(uintptr textLoadAddress,uintptr xdataLoadAddress,uintptr pdataLoadAddress,size_t pdataNumBytes)
	{
		// Use the smallest address of the 3 segments as the base address of the image.
		// This assumes that the segments are all loaded less than 2GB away from each other!
		auto imageBaseAddress = std::min(textLoadAddress,std::min(pdataLoadAddress,xdataLoadAddress));

		// The LLVM COFF dynamic loader doesn't handle the image-relative relocations used by the pdata section,
		// and overwrites those values with o: https://github.com/llvm-mirror/llvm/blob/e84d8c12d5157a926db15976389f703809c49aa5/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldCOFFX86_64.h#L96
		// This works around that by making a copy of the RUNTIME_FUNCTION structs and doing a manual relocation
		// with some assumptions about the relocations.
		const uint32 numFunctions = (uint32)(pdataNumBytes / sizeof(RUNTIME_FUNCTION));
		auto functions = reinterpret_cast<RUNTIME_FUNCTION*>(pdataLoadAddress);
		auto functionsCopy = new RUNTIME_FUNCTION[numFunctions];
		uintptr currentFunctionTextLoadAddr = textLoadAddress;
		for(uint32 functionIndex = 0;functionIndex < numFunctions;++functionIndex)
		{
			const auto& function = functions[functionIndex];
			auto& functionCopy = functionsCopy[functionIndex];

			// BeginAddress and EndAddress are relative to the start of the function, so BeginAddress should be 0 and EndAddress the length of the function's code.
			functionCopy.BeginAddress = (uint32)(currentFunctionTextLoadAddr + function.BeginAddress - imageBaseAddress);
			functionCopy.EndAddress = (uint32)(currentFunctionTextLoadAddr + function.EndAddress - imageBaseAddress);

			// UnwindInfoAddress is an offset relative to the start of the xdata section.
			functionCopy.UnwindInfoAddress = (uint32)(xdataLoadAddress + function.UnwindInfoAddress - imageBaseAddress);

			// Assume that the next function immediately follows the current function at the next 16-byte aligned address.
			currentFunctionTextLoadAddr += (function.EndAddress + 15) & ~15;
		}

		// Register our manually fixed up copy of the function table.
		if(!RtlAddFunctionTable(functionsCopy,numFunctions,imageBaseAddress))
		{
			throw;
		}
	}
Ejemplo n.º 7
0
// this function constructs the so-called pre-trampoline, this pre-trampoline
// determines if a hook should really be executed. An example will be the
// easiest; imagine we have a hook on CreateProcessInternalW() and on
// NtCreateProcessEx() (this is actually the case currently), now, if all goes
// well, a call to CreateProcess() will call CreateProcessInternalW() followed
// by a call to NtCreateProcessEx(). Because we already hook the higher-level
// API CreateProcessInternalW() it is not really useful to us to log the
// information retrieved in the NtCreateProcessEx() function as well,
// therefore, because one is called by the other, we can tell the hooking
// engine "once inside a hook, don't hook further API calls" by setting the
// allow_hook_recursion flag to false. The example above is what happens when
// the hook recursion is not allowed.
static void hook_create_pre_tramp(hook_t *h)
{
	unsigned char *p;
	unsigned int off;

	unsigned char pre_tramp1[] = {
#if DISABLE_HOOK_CONTENT
		0xff, 0x25, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#endif
		// push rax/rcx/rdx/rbx
		0x50, 0x51, 0x52, 0x53,
		// push r8, r9, r10, r11
		0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53,
		// call $+0
		0xe8, 0x00, 0x00, 0x00, 0x00,
		// pop r8
		0x41, 0x58,
		// sub r8, 17
		0x49, 0x83, 0xe8, 0x11,
		// mov r8, qword ptr [rsp+0x40]
		// 0x4c, 0x8b, 0x44, 0x24, 0x40,
		// lea rdx, [rsp+0x40]
		0x48, 0x8d, 0x54, 0x24, 0x40,
		// mov ecx, h->allow_hook_recursion
		0xb9, h->allow_hook_recursion, 0x00, 0x00, 0x00,
		// sub rsp, 0x28
		0x48, 0x83, 0xec, 0x28,
		// call enter_hook, returns 0 if we should call the original func, otherwise 1 if we should call our New_ version
		0xff, 0x15, 0x02, 0x00, 0x00, 0x00,
		// jmp $+8
		0xeb, 0x08,
		// address of enter_hook
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};
	unsigned char pre_tramp2[] = {
		// test eax, eax
		0x85, 0xc0,
		// jnz 0x1e
		0x75, 0x1e,
		// add rsp, 0x28
		0x48, 0x83, 0xc4, 0x28,
		// pop r11, r10, r9, r8
		0x41, 0x5b, 0x41, 0x5a, 0x41, 0x59, 0x41, 0x58,
		// pop rbx/rdx/rcx/rax
		0x5b, 0x5a, 0x59, 0x58,
		// jmp h->tramp (original function)
		0xff, 0x25, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};
	unsigned char pre_tramp3[] = {
		// add rsp, 0x28
		0x48, 0x83, 0xc4, 0x28,
		// pop r11, r10, r9, r8
		0x41, 0x5b, 0x41, 0x5a, 0x41, 0x59, 0x41, 0x58,
		// pop rbx/rdx/rcx/rax
		0x5b, 0x5a, 0x59, 0x58,
		// jmp h->new_func (New_ func)
		0xff, 0x25, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};

#if DISABLE_HOOK_CONTENT
	*(ULONG_PTR *)(pre_tramp1 + 6) = (ULONG_PTR)h->tramp;
#endif

	p = h->hookdata->pre_tramp;
	off = sizeof(pre_tramp1) - sizeof(ULONG_PTR);
	*(ULONG_PTR *)(pre_tramp1 + off) = (ULONG_PTR)&enter_hook;
	memcpy(p, pre_tramp1, sizeof(pre_tramp1));
	p += sizeof(pre_tramp1);

	off = sizeof(pre_tramp2) - sizeof(ULONG_PTR);
	*(ULONG_PTR *)(pre_tramp2 + off) = (ULONG_PTR)h->hookdata->tramp;
	memcpy(p, pre_tramp2, sizeof(pre_tramp2));
	p += sizeof(pre_tramp2);

	off = sizeof(pre_tramp3) - sizeof(ULONG_PTR);
	*(ULONG_PTR *)(pre_tramp3 + off) = (ULONG_PTR)h->new_func;
	memcpy(p, pre_tramp3, sizeof(pre_tramp3));

	/* now add the necessary unwind information so that stack traces work
	 * properly.  must be modified whenever the assembly above changes
	 */

	/* would be really nice if MSDN had any mention whatsoever that the RUNTIME_FUNCTION needs to have
	   a global allocation -- it doesn't copy the contents of the tiny 12-byte RUNTIME_FUNCTION, it merely
	   stores the same pointer you provide to the API.  If you allocate it on the stack, or call the API multiple
	   times with the same pointer value, you'll end up with completely broken unwind information that fails
	   in spectacular ways.
	 */
	RUNTIME_FUNCTION *functable = malloc(sizeof(RUNTIME_FUNCTION));
	UNWIND_INFO *unwindinfo = &h->hookdata->unwind_info;

	functable->BeginAddress = offsetof(hook_data_t, pre_tramp);
	functable->EndAddress = offsetof(hook_data_t, pre_tramp) + sizeof(h->hookdata->pre_tramp);
	functable->UnwindData = offsetof(hook_data_t, unwind_info);

	unwindinfo->Version = 1;
	unwindinfo->Flags = UNW_FLAG_NHANDLER;
	unwindinfo->SizeOfProlog = 38;
	unwindinfo->CountOfCodes = 9;
	unwindinfo->FrameRegister = 0;
	unwindinfo->FrameOffset = 0;

	unwindinfo->UnwindCode[0].UnwindOp = UWOP_ALLOC_SMALL;
	unwindinfo->UnwindCode[0].CodeOffset = 38;
	unwindinfo->UnwindCode[0].OpInfo = 4; // (4 + 1) * 8 = 0x28

	BYTE regs1[] = { 11, 10, 9, 8 };
	BYTE regs2[] = { 3, 2, 1, 0 };

	for (int i = 0; i < ARRAYSIZE(regs1); i++) {
		unwindinfo->UnwindCode[1 + i].UnwindOp = UWOP_PUSH_NONVOL;
		unwindinfo->UnwindCode[1 + i].CodeOffset = 12 - (2 * i);
		unwindinfo->UnwindCode[1 + i].OpInfo = regs1[i];
	}

	for (int i = 0; i < ARRAYSIZE(regs2); i++) {
		unwindinfo->UnwindCode[5 + i].UnwindOp = UWOP_PUSH_NONVOL;
		unwindinfo->UnwindCode[5 + i].CodeOffset = 4 - i;
		unwindinfo->UnwindCode[5 + i].OpInfo = regs2[i];
	}

	RtlAddFunctionTable(functable, 1, (DWORD64)h->hookdata);
}
Ejemplo n.º 8
0
jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unordered_map<std::string, std::uintptr_t>&& table)
{
	verify(HERE), s_memory;

	std::string result;

	const auto module_ptr = _module.get();

	// Initialization
	llvm::InitializeNativeTarget();
	llvm::InitializeNativeTargetAsmPrinter();
	LLVMLinkInMCJIT();
	const auto _cpu = llvm::sys::getHostCPUName();

	m_engine.reset(llvm::EngineBuilder(std::move(_module))
		.setErrorStr(&result)
		.setMCJITMemoryManager(std::make_unique<MemoryManager>(std::move(table)))
		.setOptLevel(llvm::CodeGenOpt::Aggressive)
		.setCodeModel((u64)s_memory <= 0x60000000 ? llvm::CodeModel::Small : llvm::CodeModel::Large) // TODO
		.setMCPU(_cpu == "skylake" ? "haswell" : _cpu)
		.create());

	if (!m_engine)
	{
		fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
	}

	m_engine->setProcessAllSections(true); // ???
	m_engine->RegisterJITEventListener(&s_listener);
	m_engine->finalizeObject();

	for (auto& func : module_ptr->functions())
	{
		if (!func.empty())
		{
			const std::string& name = func.getName();

			// Register compiled function
			m_map[name] = m_engine->getFunctionAddress(name);
		}

		// Delete IR to lower memory consumption
		func.deleteBody();
	}

#ifdef _WIN32
	// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
	std::set<u64> func_set;

	for (const auto& pair : m_map)
	{
		func_set.emplace(pair.second);
	}

	const u64 base = (u64)s_memory;
	const u8* bits = s_unwind_info;

	s_unwind.clear();
	s_unwind.reserve(m_map.size());

	for (const u64 addr : func_set)
	{
		// Find next function address
		const auto _next = func_set.upper_bound(addr);
		const u64 next = _next != func_set.end() ? *_next : (u64)s_code_addr + s_code_size;

		// Generate RUNTIME_FUNCTION record
		RUNTIME_FUNCTION uw;
		uw.BeginAddress = static_cast<u32>(addr - base);
		uw.EndAddress   = static_cast<u32>(next - base);
		uw.UnwindData   = static_cast<u32>((u64)bits - base);
		s_unwind.emplace_back(uw);

		// Parse .xdata UNWIND_INFO record
		const u8 flags = *bits++; // Version and flags
		const u8 prolog = *bits++; // Size of prolog
		const u8 count = *bits++; // Count of unwind codes
		const u8 frame = *bits++; // Frame Reg + Off
		bits += ::align(std::max<u8>(1, count), 2) * sizeof(u16); // UNWIND_CODE array

		if (flags != 1) 
		{
			// Can't happen for trivial code
			LOG_ERROR(GENERAL, "LLVM: unsupported UNWIND_INFO version/flags (0x%02x)", flags);
			break;
		}

		LOG_TRACE(GENERAL, "LLVM: .xdata at 0x%llx: function 0x%x..0x%x: p0x%02x, c0x%02x, f0x%02x", uw.UnwindData + base, uw.BeginAddress + base, uw.EndAddress + base, prolog, count, frame);
	}

	if (s_unwind_info + s_unwind_size != bits)
	{
		LOG_ERROR(GENERAL, "LLVM: .xdata analysis failed! (%p != %p)", s_unwind_info + s_unwind_size, bits);
	}
	else if (!RtlAddFunctionTable(s_unwind.data(), (DWORD)s_unwind.size(), base))
	{
		LOG_ERROR(GENERAL, "RtlAddFunctionTable(%p) failed! Error %u", s_unwind_info, GetLastError());
	}
	else
	{
		LOG_SUCCESS(GENERAL, "LLVM: UNWIND_INFO registered (%p, size=0x%llx)", s_unwind_info, s_unwind_size);
	}
#endif
}