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)); }
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); }
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; }
/* 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 }
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; }
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; } }
// 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); }
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 }