From 81c93e3488a10f7a5a4bfd66cf56e225c0f128a3 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 13 Nov 2023 23:30:00 +0100 Subject: [PATCH] Use backtrace on windows. Updated backtrace API --- .github/workflows/main.yml | 1 + lib/std/core/builtin.c3 | 5 +- lib/std/core/env.c3 | 2 +- lib/std/os/backtrace.c3 | 38 +++- lib/std/os/cpu.c3 | 4 +- lib/std/os/linux/linux.c3 | 16 +- lib/std/os/macos/darwin.c3 | 17 +- lib/std/os/win32/process.c3 | 195 +++++++++++----- lib/std/os/win32/types.c3 | 428 ++++++++++++++++++++++++++++++++++++ releasenotes.md | 2 +- resources/linux_stack.c3 | 6 +- resources/msvc_stack.c3 | 18 ++ src/compiler/linker.c | 8 +- src/compiler/target.c | 2 +- src/version.h | 2 +- 15 files changed, 641 insertions(+), 103 deletions(-) create mode 100644 resources/msvc_stack.c3 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dcc29e751..7d72646b2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,6 +41,7 @@ jobs: ..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3 ..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\process.c3 ..\build\${{ matrix.build_type }}\c3c.exe compile --test -g -O0 --threads 1 --target macos-x64 examples\constants.c3 + ..\build\${{ matrix.build_type }}\c3c.exe compile-run msvc_stack.c3 - name: Build testproject run: | diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index 0f23f711f..7bbe2dac7 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -91,7 +91,10 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIV { @pool() { - BacktraceList! backtrace = backtrace::backtrace_load(mem::temp()); + void*[256] buffer; + void*[] backtraces = backtrace::capture_current(&buffer); + backtraces_to_ignore++; + BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, mem::temp()); if (catch backtrace) return false; if (backtrace.len() <= backtraces_to_ignore) return false; io::eprint("\nERROR: '"); diff --git a/lib/std/core/env.c3 b/lib/std/core/env.c3 index 024ca1ddf..b42e01f06 100644 --- a/lib/std/core/env.c3 +++ b/lib/std/core/env.c3 @@ -132,7 +132,7 @@ const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT; const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING); const bool X86_64 = ARCH_TYPE == X86_64; const bool AARCH64 = ARCH_TYPE == AARCH64; -const bool NATIVE_STACKTRACE = LINUX || DARWIN; +const bool NATIVE_STACKTRACE = LINUX || DARWIN || WIN32; const bool LINUX = LIBC && OS_TYPE == LINUX; const bool DARWIN = LIBC && os_is_darwin(); const bool WIN32 = LIBC && OS_TYPE == WIN32; diff --git a/lib/std/os/backtrace.c3 b/lib/std/os/backtrace.c3 index 15a6cc50d..c99728589 100644 --- a/lib/std/os/backtrace.c3 +++ b/lib/std/os/backtrace.c3 @@ -1,4 +1,5 @@ module std::os::backtrace; +import std::collections::list; fault BacktraceFault { @@ -6,6 +7,7 @@ fault BacktraceFault EXECUTABLE_PATH_NOT_FOUND, IMAGE_NOT_FOUND, NO_BACKTRACE_SYMBOLS, + RESOLUTION_FAILED, } const Backtrace BACKTRACE_UNKNOWN = { 0, "", "", "", 0, null }; @@ -20,6 +22,7 @@ struct Backtrace (Printable) Allocator* allocator; } + fn bool Backtrace.has_file(&self) { return self.file.len > 0; @@ -50,12 +53,9 @@ fn void Backtrace.free(&self) self.allocator.free(self.file); } -def backtrace_load = darwin::backtrace_load @if(env::DARWIN); -def backtrace_load = linux::backtrace_load @if(env::LINUX); - -fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator* using = mem::heap()) +fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator* allocator) { - if (!using) + if (!allocator) { self.offset = offset; self.function = function; @@ -66,11 +66,31 @@ fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_ return self; } self.offset = offset; - self.function = function.copy(using); - self.object_file = object_file.copy(using); - self.file = file.copy(using); - self.allocator = using; + self.function = function.copy(allocator); + self.object_file = object_file.copy(allocator); + self.file = file.copy(allocator); + self.allocator = allocator; self.line = line; return self; } +fn void*[] capture_current(void*[] buffer) @if(env::POSIX || env::WIN32) +{ + if (!buffer.len) return buffer[:0]; + $switch + $case env::POSIX: + CInt len = posix::backtrace(buffer.ptr, buffer.len); + return buffer[:len]; + $case env::WIN32: + Win32_WORD len = win32::rtlCaptureStackBackTrace(0, buffer.len, buffer.ptr, null); + return buffer[:len]; + $default: + return buffer[:0]; + $endswitch +} + +def BacktraceList = List(); + +def symbolize_backtrace = linux::symbolize_backtrace @if(env::LINUX); +def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32); +def symbolize_backtrace = darwin::symbolize_backtrace @if(env::DARWIN); diff --git a/lib/std/os/cpu.c3 b/lib/std/os/cpu.c3 index 22cd0339c..25c8627c0 100644 --- a/lib/std/os/cpu.c3 +++ b/lib/std/os/cpu.c3 @@ -28,7 +28,7 @@ import std::os::win32; fn uint num_cpu() { - SystemInfo info; - win32::get_system_info(&info); + Win32_SYSTEM_INFO info; + win32::getSystemInfo(&info); return info.dwNumberOfProcessors; } \ No newline at end of file diff --git a/lib/std/os/linux/linux.c3 b/lib/std/os/linux/linux.c3 index f4cc0749e..cdef56f3b 100644 --- a/lib/std/os/linux/linux.c3 +++ b/lib/std/os/linux/linux.c3 @@ -6,8 +6,6 @@ import std::collections::list; extern fn isz readlink(ZString path, char* buf, usz bufsize); -def BacktraceList = List(); - const PT_PHDR = 6; const EI_NIDENT = 16; def Elf32_Half = ushort; @@ -153,7 +151,8 @@ fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = mem::hea .object_file = info.dli_fname.copy(allocator), .offset = (uptr)addr, .file = "".copy(allocator), - .line = 0 + .line = 0, + .allocator = allocator }; } uint line = 0; @@ -174,13 +173,10 @@ fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = mem::hea }; } -fn BacktraceList! backtrace_load(Allocator* allocator) +fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator) { - void*[256] bt_buffer; - CInt size = posix::backtrace(&bt_buffer, 256); - io::printfn("Backtrace list %s", size); BacktraceList list; - list.init_new(size, allocator); + list.init_new(backtrace.len, allocator); defer catch { foreach (trace : list) @@ -191,9 +187,9 @@ fn BacktraceList! backtrace_load(Allocator* allocator) } @pool(allocator) { - for (usz i = 0; i < size; i++) + foreach (addr : backtrace) { - Backtrace trace = backtrace_load_element(bt_buffer[i], allocator)!; + Backtrace trace = backtrace_load_element(addr, allocator)!; list.append(trace); } }; diff --git a/lib/std/os/macos/darwin.c3 b/lib/std/os/macos/darwin.c3 index d619e6c5f..78bd271bb 100644 --- a/lib/std/os/macos/darwin.c3 +++ b/lib/std/os/macos/darwin.c3 @@ -76,8 +76,6 @@ fn String! executable_path(Allocator *allocator) return ((ZString)&path).copy(allocator); } -def BacktraceList = List(); - fn uptr! load_address() @local { Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT"); @@ -128,18 +126,17 @@ fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_a .function = info.dli_sname ? info.dli_sname.copy(allocator) : "???".copy(allocator), .object_file = info.dli_fname.copy(allocator), .file = "".copy(allocator), - .line = 0 + .line = 0, + .allocator = allocator }; }; } -fn BacktraceList! backtrace_load(Allocator* allocator) +fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator) { - void*[256] bt_buffer; - CInt size = posix::backtrace(&bt_buffer, 256); void *load_addr = (void *)load_address()!; BacktraceList list; - list.init_new(size, allocator); + list.init_new(backtrace.len, allocator); defer catch { foreach (trace : list) @@ -151,11 +148,9 @@ fn BacktraceList! backtrace_load(Allocator* allocator) @pool(allocator) { String execpath = executable_path(mem::temp())!; - for (usz i = 1; i < size; i++) + foreach (addr : backtrace) { - void* buffer = bt_buffer[i]; - Backtrace trace = backtrace_load_element(execpath, buffer, load_addr, allocator)!; - list.append(trace); + list.append(backtrace_load_element(execpath, addr, load_addr, allocator) ?? backtrace::BACKTRACE_UNKNOWN); } }; return list; diff --git a/lib/std/os/win32/process.c3 b/lib/std/os/win32/process.c3 index e25d42cbb..60fff82b4 100644 --- a/lib/std/os/win32/process.c3 +++ b/lib/std/os/win32/process.c3 @@ -28,7 +28,22 @@ const Win32_DWORD PIPE_WAIT = 0; const Win32_DWORD PIPE_NOWAIT = 1; const Win32_DWORD PIPE_ACCEPT_REMOTE_CLIENTS = 0; const Win32_DWORD PIPE_REJECT_REMOTE_CLIENTS = 8; +const SYMOPT_CASE_INSENSITIVE = 0x01; +const SYMOPT_UNDNAME = 0x02; +const SYMOPT_DEFERRED_LOADS = 0x04; +const SYMOPT_NO_CPP = 0x08; +const SYMOPT_LOAD_LINES = 0x10; +const SYMOPT_OMAP_FIND_NEAREST = 0x20; +const SYMOPT_LOAD_ANYTHING = 0x40; +const SYMOPT_IGNORE_CVREC = 0x80; +const IMAGE_FILE_MACHINE_UNKNOWN = 0; +const IMAGE_FILE_MACHINE_TARGET_HOST = 1; +const IMAGE_FILE_MACHINE_I386 = 0x014c; +const IMAGE_FILE_MACHINE_IA64 = 0x0200; +const IMAGE_FILE_MACHINE_ARM64 = 0xAA64; +const IMAGE_FILE_MACHINE_AMD64 = 0x8664; +const UNDNAME_COMPLETE = 0x0000; extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection"); extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection"); @@ -47,70 +62,136 @@ extern fn Win32_DWORD sleepEx(Win32_DWORD ms, Win32_BOOL alertable) @extern("Sle extern fn Win32_HANDLE createThread(void* attributes, usz stack, ThreadFn func, void* arg, uint flags, uint* thread_id) @extern("CreateThread"); extern fn Win32_BOOL getExitCodeThread(Win32_HANDLE handle, uint* exit_code) @extern("GetExitCodeThread"); extern fn Win32_BOOL getExitCodeProcess(Win32_HANDLE hProcess, Win32_LPDWORD lpExitCode) @extern("GetExitCodeProcess"); -extern fn uint getThreadId(Win32_HANDLE) @extern("GetThreadId"); -extern fn void exitThread(uint res) @noreturn @extern("ExitThread"); +extern fn Win32_DWORD getThreadId(Win32_HANDLE) @extern("GetThreadId"); +extern fn void exitThread(Win32_DWORD dwExitCode) @noreturn @extern("ExitThread"); extern fn Win32_HANDLE getCurrentThread() @extern("GetCurrentThread"); extern fn Win32_BOOL terminateProcess(Win32_HANDLE hProcess, Win32_UINT uExitCode) @extern("TerminateProcess"); extern fn Win32_DWORD getCurrentProcessId() @extern("GetCurrentProcessId"); +extern fn Win32_HANDLE getCurrentProcess() @extern("GetCurrentProcess"); extern fn Win32_DWORD getCurrentThreadId() @extern("GetCurrentThreadId"); extern fn Win32_BOOL setHandleInformation(Win32_HANDLE hObject, Win32_DWORD dwMask, Win32_DWORD dwFlags) @extern("SetHandleInformation"); -extern fn Win32_HANDLE createEventA( - Win32_LPSECURITY_ATTRIBUTES lpEventAttributes, - Win32_BOOL bManualReset, - Win32_BOOL bInitialState, - Win32_LPCSTR lpName -) @extern("CreateEventA"); -extern fn Win32_BOOL createProcessW( - Win32_LPCWSTR lpApplicationName, - Win32_LPWSTR lpCommandLine, - Win32_LPSECURITY_ATTRIBUTES lpProcessAttributes, - Win32_LPSECURITY_ATTRIBUTES lpThreadAttributes, - Win32_BOOL bInheritHandles, - Win32_DWORD dwCreationFlags, - Win32_LPVOID lpEnvironment, - Win32_LPCWSTR lpCurrentDirectory, - Win32_LPSTARTUPINFOW lpStartupInfo, - Win32_LPPROCESS_INFORMATION lpProcessInformation -) @extern("CreateProcessW"); -extern fn Win32_HANDLE createNamedPipeA( - Win32_LPCSTR lpName, Win32_DWORD dwOpenMode, Win32_DWORD dwPipeMode, - Win32_DWORD nMaxInstances, Win32_DWORD nOutBufferSize, Win32_DWORD nInBufferSize, - Win32_DWORD nDefaultTimeOut, Win32_LPSECURITY_ATTRIBUTES lpSecurityAttributes -) @extern("CreateNamedPipeA"); -extern fn Win32_BOOL getOverlappedResult( - Win32_HANDLE hFile, - Win32_LPOVERLAPPED lpOverlapped, - Win32_LPDWORD lpNumberOfBytesTransferred, - Win32_BOOL bWait -) @extern("GetOverlappedResult"); -extern fn Win32_DWORD getEnvironmentVariableW( - Win32_LPCWSTR lpName, - Win32_LPWSTR lpBuffer, - Win32_DWORD nSize -) @extern("GetEnvironmentVariableW"); -extern fn Win32_BOOL setEnvironmentVariableW( - Win32_LPCWSTR lpName, - Win32_LPCWSTR lpValue -) @extern("SetEnvironmentVariableW"); +extern fn Win32_HANDLE createEventA(Win32_LPSECURITY_ATTRIBUTES lpEventAttributes, Win32_BOOL bManualReset, Win32_BOOL bInitialState, Win32_LPCSTR lpName) @extern("CreateEventA"); +extern fn Win32_BOOL createProcessW(Win32_LPCWSTR lpApplicationName, Win32_LPWSTR lpCommandLine, Win32_LPSECURITY_ATTRIBUTES lpProcessAttributes, Win32_LPSECURITY_ATTRIBUTES lpThreadAttributes, Win32_BOOL bInheritHandles, Win32_DWORD dwCreationFlags, Win32_LPVOID lpEnvironment, Win32_LPCWSTR lpCurrentDirectory, Win32_LPSTARTUPINFOW lpStartupInfo, Win32_LPPROCESS_INFORMATION lpProcessInformation) @extern("CreateProcessW"); +extern fn Win32_HANDLE createNamedPipeA(Win32_LPCSTR lpName, Win32_DWORD dwOpenMode, Win32_DWORD dwPipeMode, Win32_DWORD nMaxInstances, Win32_DWORD nOutBufferSize, Win32_DWORD nInBufferSize, Win32_DWORD nDefaultTimeOut, Win32_LPSECURITY_ATTRIBUTES lpSecurityAttributes) @extern("CreateNamedPipeA"); +extern fn Win32_BOOL getOverlappedResult(Win32_HANDLE hFile, Win32_LPOVERLAPPED lpOverlapped, Win32_LPDWORD lpNumberOfBytesTransferred, Win32_BOOL bWait) @extern("GetOverlappedResult"); +extern fn Win32_DWORD getEnvironmentVariableW(Win32_LPCWSTR lpName, Win32_LPWSTR lpBuffer, Win32_DWORD nSize) @extern("GetEnvironmentVariableW"); +extern fn Win32_BOOL setEnvironmentVariableW(Win32_LPCWSTR lpName, Win32_LPCWSTR lpValue) @extern("SetEnvironmentVariableW"); +extern fn void getSystemInfo(Win32_LPSYSTEM_INFO lpSystemInfo) @extern("GetSystemInfo"); +extern fn Win32_BOOL enumProcessModules(Win32_HANDLE hProcess, Win32_HMODULE* lphModule, Win32_DWORD cb, Win32_LPDWORD lpcbNeeded) @extern("K32EnumProcessModules"); +extern fn Win32_BOOL getModuleInformation(Win32_HANDLE hProcess, Win32_HMODULE hModule, Win32_LPMODULEINFO lpmodinfo, Win32_DWORD cb) @extern("K32GetModuleInformation"); +extern fn Win32_DWORD symAddrIncludeInlineTrace(Win32_HANDLE hProcess, Win32_DWORD64 address) @extern("SymAddrIncludeInlineTrace"); +extern fn Win32_BOOL symQueryInlineTrace(Win32_HANDLE hProcess, Win32_DWORD64 startAddress, Win32_DWORD startContext, Win32_DWORD64 startRetAddress, Win32_DWORD64 curAddress, Win32_LPDWORD curContext, Win32_LPDWORD curFrameIndex) @extern("SymQueryInlineTrace"); +extern fn Win32_BOOL symFromInlineContext(Win32_HANDLE hProcess, Win32_DWORD64 address, Win32_ULONG inlineContext, Win32_PDWORD64 displacement, Win32_PSYMBOL_INFO symbol) @extern("SymFromInlineContext"); +extern fn Win32_BOOL symGetLineFromInlineContext(Win32_HANDLE hProcess, Win32_DWORD64 qwAddr, Win32_ULONG inlineContext, Win32_DWORD64 qwModuleBaseAddress, Win32_PDWORD pdwDisplacement, Win32_PIMAGEHLP_LINE64 line64) @extern("SymGetLineFromInlineContext"); +extern fn Win32_ULONG rtlWalkFrameChain(Win32_PVOID*, Win32_ULONG, Win32_ULONG) @extern("RtlWalkFrameChain"); +extern fn Win32_BOOL symInitialize(Win32_HANDLE hProcess, Win32_PCSTR userSearchPath, Win32_BOOL fInvadeProcess) @extern("SymInitialize"); +extern fn Win32_BOOL symCleanup(Win32_HANDLE hProcess) @extern("SymCleanup"); +extern fn Win32_DWORD getModuleFileNameA(Win32_HMODULE hModule, Win32_LPSTR lpFilename, Win32_DWORD nSize) @extern("GetModuleFileNameA"); +extern fn Win32_DWORD getModuleFileNameExA(Win32_HANDLE hProcess, Win32_HMODULE hModule, Win32_LPSTR lpFilename, Win32_DWORD nSize) @extern("GetModuleFileNameExA"); +extern fn Win32_DWORD64 symLoadModuleEx(Win32_HANDLE hProcess, Win32_HANDLE hFile, Win32_PCSTR imageName, Win32_PCSTR moduleName, Win32_DWORD64 baseOfDll, Win32_DWORD dllSize, Win32_PMODLOAD_DATA data, Win32_DWORD flags) @extern("SymLoadModule"); +extern fn Win32_BOOL stackWalk64(Win32_DWORD machineType, Win32_HANDLE hProcess, Win32_HANDLE hThread, Win32_LPSTACKFRAME64 stackFrame, Win32_PVOID contextRecord, Win32_PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryRoutine, Win32_PFUNCTION_TABLE_ACCESS_ROUTINE64 functionTableAccessRoutine, Win32_PGET_MODULE_BASE_ROUTINE64 getModuleBaseRoutine, Win32_PTRANSLATE_ADDRESS_ROUTINE64 translateAddress) @extern("StackWalk64"); +extern fn void rtlCaptureContext(Win32_PCONTEXT contextRecord) @extern("RtlCaptureContext"); +extern fn void* symFunctionTableAccess64(Win32_HANDLE hProcess, Win32_DWORD64 addrBase) @extern("SymFunctionTableAccess64"); +extern fn Win32_DWORD64 symGetModuleBase64(Win32_HANDLE hProcess, Win32_DWORD64 qwAddr) @extern("SymGetModuleBase64"); +extern fn Win32_DWORD getModuleBaseNameA(Win32_HANDLE hProcess, Win32_HMODULE hModule, Win32_LPSTR lpBaseName, Win32_DWORD nSize) @extern("K32GetModuleBaseNameA"); +extern fn Win32_DWORD symGetOptions() @extern("SymGetOptions"); +extern fn Win32_DWORD symSetOptions(Win32_DWORD symOptions) @extern("SymSetOptions"); +extern fn Win32_PIMAGE_NT_HEADERS imageNtHeader(Win32_PVOID base) @extern("ImageNtHeader"); +extern fn Win32_DWORD unDecorateSymbolName(Win32_PCSTR name, Win32_PSTR outputString, Win32_DWORD maxStringLength, Win32_DWORD flags) @extern("UnDecorateSymbolName"); +extern fn Win32_BOOL symFromAddr(Win32_HANDLE hProcess, Win32_DWORD64 address, Win32_PDWORD64 displacement, Win32_PSYMBOL_INFO symbol) @extern("SymFromAddr"); +extern fn Win32_BOOL symGetLineFromAddr64(Win32_HANDLE hProcess, Win32_DWORD64 dwAddr, Win32_PDWORD pdwDisplacement, Win32_PIMAGEHLP_LINE64 line) @extern("SymGetLineFromAddr64"); +extern fn Win32_WORD rtlCaptureStackBackTrace(Win32_DWORD framesToSkip, Win32_DWORD framesToCapture, Win32_PVOID *backTrace, Win32_PDWORD backTraceHash) @extern("RtlCaptureStackBackTrace"); +extern fn Win32_BOOL symGetModuleInfo64(Win32_HANDLE hProcess, Win32_DWORD64 qwAddr, Win32_PIMAGEHLP_MODULE64 moduleInfo) @extern("SymGetModuleInfo64"); -struct SystemInfo +fn Win32_DWORD! load_modules() { - union { - uint dwOemId; - struct { - ushort wProcessorArchitecture; - ushort wReserved; - } + Win32_HANDLE process = getCurrentProcess(); + Win32_DWORD needed; + symInitialize(getCurrentProcess(), null, 1); + Win32_DWORD symOptions = symGetOptions(); + symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; + symSetOptions(symOptions); + Win32_HMODULE[1024] mod_buffer; + if (!enumProcessModules(process, &mod_buffer, mod_buffer.len, &needed)) + { + return BacktraceFault.RESOLUTION_FAILED?; } - uint dwPageSize; - void* lpMinimumApplicationAddress; - void* lpMaximumApplicationAddress; - usz dwActiveProcessorMask; - uint dwNumberOfProcessors; - uint dwProcessorType; - uint dwAllocationGranularity; - ushort wProcessorLevel; - ushort wProcessorRevision; + if (needed > mod_buffer.len) return BacktraceFault.RESOLUTION_FAILED?; + Win32_HMODULE[] modules = mod_buffer[:needed]; + void* base = null; + foreach (mod : modules) + { + Win32_MODULEINFO info; + if (!getModuleInformation(process, mod, &info, $sizeof(info))) + { + return BacktraceFault.RESOLUTION_FAILED?; + } + if (!base) base = info.lpBaseOfDll; + Win32_DWORD load_size = info.sizeOfImage; + char[1024] char_buf; + Win32_DWORD len = getModuleFileNameA(mod, (Win32_LPSTR)&char_buf, char_buf.len - 1); + if (len < 1) continue; + char[1024] module_name; + Win32_DWORD len2 = getModuleBaseNameA(process, mod, (Win32_LPSTR)&module_name, 1021); + if (len2 < 1) continue; + Win32_DWORD64 base_addr = symLoadModuleEx(process, null, (Win32_PCSTR)&char_buf, (Win32_PCSTR)&module_name, (Win32_DWORD64)info.lpBaseOfDll, load_size, null, 0); + } + if (!base) return BacktraceFault.IMAGE_NOT_FOUND?; + Win32_IMAGE_NT_HEADERS* h = imageNtHeader(base); + return h.fileHeader.machine; } -extern fn CInt get_system_info(SystemInfo*) @extern("GetSystemInfo"); +struct Symbol +{ + inline Win32_SYMBOL_INFO sym; + char[256] buffer; +} + +Win32_DWORD64 displacement; + +fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator) +{ + BacktraceList list; + list.init_new(backtrace.len, allocator); + Win32_HANDLE process = getCurrentProcess(); + symInitialize(process, null, 1); + defer symCleanup(process); + foreach (addr : backtrace) + { + list.append(resolve_backtrace(addr, process, allocator) ?? backtrace::BACKTRACE_UNKNOWN); + } + return list; +} + +fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator* allocator) +{ + Symbol symbol; + //Win32_DWORD image_type = load_modules()!; + symbol.sizeOfStruct = Win32_SYMBOL_INFO.sizeof; + symbol.maxNameLen = 255; + if (!symFromAddr(process, (Win32_DWORD64)addr - 1, &displacement, &symbol)) + { + return BacktraceFault.NO_BACKTRACE_SYMBOLS?; + } + Win32_IMAGEHLP_MODULE64 module_info; + module_info.sizeOfStruct = Win32_IMAGEHLP_MODULE64.sizeof; + if (!symGetModuleInfo64(process, (Win32_DWORD64)addr - 1, &module_info)) + { + return BacktraceFault.NO_BACKTRACE_SYMBOLS?; + } + ZString module_name = (ZString)&module_info.imageName; + char[256] name; + unDecorateSymbolName(&symbol.name, (Win32_PSTR)&name, 256, UNDNAME_COMPLETE); + Win32_DWORD offset = 0; + Win32_IMAGEHLP_LINE64 line; + Backtrace backtrace; + ZString zname = (ZString)&name; + if (!symGetLineFromAddr64(process, (Win32_ULONG64)addr - 1, &offset, &line)) + { + backtrace.init((uptr)addr, .function = zname.str_view(), .object_file = module_name.str_view(), .allocator = allocator); + return backtrace; + } + String filename = ((ZString)line.fileName).str_view(); + backtrace.init((uptr)addr, .function = zname.str_view(), .object_file = module_name.str_view(), .file = filename, .line = line.lineNumber, .allocator = allocator); + return backtrace; +} diff --git a/lib/std/os/win32/types.c3 b/lib/std/os/win32/types.c3 index d1ad70cbe..20bf52540 100644 --- a/lib/std/os/win32/types.c3 +++ b/lib/std/os/win32/types.c3 @@ -266,3 +266,431 @@ struct Win32_PROCESS_INFORMATION def Win32_PPROCESS_INFORMATION = Win32_PROCESS_INFORMATION*; def Win32_LPPROCESS_INFORMATION = Win32_PROCESS_INFORMATION*; + +struct Win32_SYSTEM_INFO +{ + union + { + Win32_DWORD dwOemId; + struct + { + Win32_WORD wProcessorArchitecture; + Win32_WORD wReserved; + } + } + Win32_DWORD dwPageSize; + Win32_LPVOID lpMinimumApplicationAddress; + Win32_LPVOID lpMaximumApplicationAddress; + Win32_DWORD_PTR dwActiveProcessorMask; + Win32_DWORD dwNumberOfProcessors; + Win32_DWORD dwProcessorType; + Win32_DWORD dwAllocationGranularity; + Win32_WORD wProcessorLevel; + Win32_WORD wProcessorRevision; +} + +def Win32_LPSYSTEM_INFO = Win32_SYSTEM_INFO*; + +struct Win32_MODULEINFO +{ + Win32_LPVOID lpBaseOfDll; + Win32_DWORD sizeOfImage; + Win32_LPVOID entryPoint; +} + +struct Win32_IMAGEHLP_LINE64 +{ + Win32_DWORD sizeOfStruct; + Win32_PVOID key; + Win32_DWORD lineNumber; + Win32_PCHAR fileName; + Win32_DWORD64 address; +} + +enum Win32_SYM_TYPE +{ + SYM_NONE, + SYM_COFF, + SYM_CV, + SYM_PDB, + SYM_EXPORT, + SYM_DEFERRED, + SYM_SYM, + SYM_DIA, + SYM_VIRTUAL +} + +struct Win32_GUID +{ + CULong data1; + CUShort data2; + CUShort data3; + char[8] data4; +} + +struct Win32_IMAGEHLP_MODULE64 +{ + Win32_DWORD sizeOfStruct; + Win32_DWORD64 baseOfImage; + Win32_DWORD imageSize; + Win32_DWORD timeDateStamp; + Win32_DWORD checkSum; + Win32_DWORD numSyms; + Win32_SYM_TYPE symType; + Win32_CHAR[32] moduleName; + Win32_CHAR[256] imageName; + Win32_CHAR[256] loadedImageName; + Win32_CHAR[256] loadedPdbName; + Win32_DWORD cVSig; + Win32_CHAR** cVData; + Win32_DWORD pdbSig; + Win32_GUID pdbSig70; + Win32_DWORD pdbAge; + Win32_BOOL pdbUnmatched; + Win32_BOOL dbgUnmatched; + Win32_BOOL lineNumbers; + Win32_BOOL globalSymbols; + Win32_BOOL typeInfo; + Win32_BOOL sourceIndexed; + Win32_BOOL publics; + Win32_DWORD machineType; + Win32_DWORD reserved; +} + +def Win32_PIMAGEHLP_MODULE64 = Win32_IMAGEHLP_MODULE64*; + +struct Win32_ARM64_NT_CONTEXT @align(16) +{ + Win32_DWORD contextFlags; + Win32_DWORD cpsr; + union + { + struct + { + Win32_DWORD64 x0; + Win32_DWORD64 x1; + Win32_DWORD64 x2; + Win32_DWORD64 x3; + Win32_DWORD64 x4; + Win32_DWORD64 x5; + Win32_DWORD64 x6; + Win32_DWORD64 x7; + Win32_DWORD64 x8; + Win32_DWORD64 x9; + Win32_DWORD64 x10; + Win32_DWORD64 x11; + Win32_DWORD64 x12; + Win32_DWORD64 x13; + Win32_DWORD64 x14; + Win32_DWORD64 x15; + Win32_DWORD64 x16; + Win32_DWORD64 x17; + Win32_DWORD64 x18; + Win32_DWORD64 x19; + Win32_DWORD64 x20; + Win32_DWORD64 x21; + Win32_DWORD64 x22; + Win32_DWORD64 x23; + Win32_DWORD64 x24; + Win32_DWORD64 x25; + Win32_DWORD64 x26; + Win32_DWORD64 x27; + Win32_DWORD64 x28; + Win32_DWORD64 fp; + Win32_DWORD64 lr; + } + Win32_DWORD64[31] x; + } + Win32_DWORD64 sp; + Win32_DWORD64 pc; + Win32_ARM64_NT_NEON128[32] v; + Win32_DWORD fpcr; + Win32_DWORD fpsr; + Win32_DWORD[ARM64_MAX_BREAKPOINTS] bcr; + Win32_DWORD64[ARM64_MAX_BREAKPOINTS] bvr; + Win32_DWORD[ARM64_MAX_WATCHPOINTS] wcr; + Win32_DWORD64[ARM64_MAX_WATCHPOINTS] wvr; +} +const ARM64_MAX_BREAKPOINTS = 8; +const ARM64_MAX_WATCHPOINTS = 2; + +struct Win32_ARM64_NT_NEON128 +{ + Win32_ULONGLONG low; + Win32_LONGLONG high; +} + +struct Win32_XMM_SAVE_AREA32 +{ + Win32_USHORT controlWord; + Win32_USHORT statusWord; + Win32_UCHAR tagWord; + Win32_UCHAR reserved1; + Win32_USHORT errorOpcode; + Win32_ULONG errrorOffset; + Win32_USHORT errorSelector; + Win32_USHORT reserved2; + Win32_ULONG dataOffset; + Win32_USHORT dataSelector; + Win32_USHORT reserved3; + Win32_ULONG mxCsr; + Win32_ULONG mxCsr_Mask; + Win32_M128A[8] floatRegisters; + Win32_M128A[16] xmmRegisters; + Win32_UCHAR[96] reserved4; +} + +struct Win32_AMD64_CONTEXT @align(16) +{ + Win32_DWORD64 p1Home; + Win32_DWORD64 p2Home; + Win32_DWORD64 p3Home; + Win32_DWORD64 p4Home; + Win32_DWORD64 p5Home; + Win32_DWORD64 p6Home; + Win32_DWORD contextFlags; + Win32_DWORD mxCsr; + Win32_WORD segCs; + Win32_WORD segDs; + Win32_WORD segEs; + Win32_WORD segFs; + Win32_WORD segGs; + Win32_WORD segSs; + Win32_DWORD eFlags; + Win32_DWORD64 dr0; + Win32_DWORD64 dr1; + Win32_DWORD64 dr2; + Win32_DWORD64 dr3; + Win32_DWORD64 dr6; + Win32_DWORD64 dr7; + Win32_DWORD64 rax; + Win32_DWORD64 rcx; + Win32_DWORD64 rdx; + Win32_DWORD64 rbx; + Win32_DWORD64 rsp; + Win32_DWORD64 rbp; + Win32_DWORD64 rsi; + Win32_DWORD64 rdi; + Win32_DWORD64 r8; + Win32_DWORD64 r9; + Win32_DWORD64 r10; + Win32_DWORD64 r11; + Win32_DWORD64 r12; + Win32_DWORD64 r13; + Win32_DWORD64 r14; + Win32_DWORD64 r15; + Win32_DWORD64 rip; + union + { + Win32_XMM_SAVE_AREA32 fltSave; + //Win32_NEON128[16] q; + Win32_ULONGLONG[32] d; + struct + { + Win32_M128A[2] header; + Win32_M128A[8] legacy; + Win32_M128A xmm0; + Win32_M128A xmm1; + Win32_M128A xmm2; + Win32_M128A xmm3; + Win32_M128A xmm4; + Win32_M128A xmm5; + Win32_M128A xmm6; + Win32_M128A xmm7; + Win32_M128A xmm8; + Win32_M128A xmm9; + Win32_M128A xmm10; + Win32_M128A xmm11; + Win32_M128A xmm12; + Win32_M128A xmm13; + Win32_M128A xmm14; + Win32_M128A xmm15; + } + Win32_DWORD[32] s; + } + Win32_M128A[26] vectorRegister; + Win32_DWORD64 vectorControl; + Win32_DWORD64 debugControl; + Win32_DWORD64 lastBranchToRip; + Win32_DWORD64 lastBranchFromRip; + Win32_DWORD64 lastExceptionToRip; + Win32_DWORD64 lastExceptionFromRip; +} + +const CONTEXT_AMD64 = 0x00100000; +const CONTEXT_AMD64_CONTROL = CONTEXT_AMD64 | 0x0001; +const CONTEXT_AMD64_INTEGER = CONTEXT_AMD64 | 0x0002; +const CONTEXT_AMD64_SEGMENTS = CONTEXT_AMD64 | 0x0004; +const CONTEXT_AMD64_FLOATING_POINT = CONTEXT_AMD64 | 0x0008; +const CONTEXT_AMD64_DEBUG_REGISTERS = CONTEXT_AMD64 | 0x0010; +const CONTEXT_AMD64_FULL = CONTEXT_AMD64_CONTROL | CONTEXT_AMD64_INTEGER | CONTEXT_AMD64_FLOATING_POINT; +const CONTEXT_AMD64_ALL = CONTEXT_AMD64_CONTROL | CONTEXT_AMD64_INTEGER | CONTEXT_AMD64_SEGMENTS | CONTEXT_AMD64_FLOATING_POINT | CONTEXT_AMD64_DEBUG_REGISTERS; + +def CONTEXT_CONTROL = CONTEXT_AMD64_CONTROL; +def CONTEXT_FULL = CONTEXT_AMD64_FULL; +def CONTEXT_ALL = CONTEXT_AMD64_ALL; + +def Win32_CONTEXT = Win32_AMD64_CONTEXT; +def Win32_PCONTEXT = Win32_CONTEXT*; + +struct Win32_M128A @align(16) +{ + Win32_ULONGLONG low; + Win32_LONGLONG high; +} + +struct Win32_IMAGE_DATA_DIRECTORY +{ + Win32_ULONG virtualAddress; + Win32_ULONG size; +} + +struct Win32_IMAGE_OPTIONAL_HEADER64 +{ + Win32_WORD magic; + Win32_BYTE majorLinkerVersion; + Win32_BYTE minorLinkerVersion; + Win32_DWORD sizeOfCode; + Win32_DWORD sizeOfInitializedData; + Win32_DWORD sizeOfUninitializedData; + Win32_DWORD addressOfEntryPoint; + Win32_DWORD baseOfCode; + Win32_ULONGLONG imageBase; + Win32_DWORD sectionAlignment; + Win32_DWORD fileAlignment; + Win32_WORD majorOperatingSystemVersion; + Win32_WORD minorOperatingSystemVersion; + Win32_WORD majorImageVersion; + Win32_WORD minorImageVersion; + Win32_WORD majorSubsystemVersion; + Win32_WORD minorSubsystemVersion; + Win32_DWORD win32VersionValue; + Win32_DWORD sizeOfImage; + Win32_DWORD sizeOfHeaders; + Win32_DWORD checkSum; + Win32_WORD subsystem; + Win32_WORD dllCharacteristics; + Win32_ULONGLONG sizeOfStackReserve; + Win32_ULONGLONG sizeOfStackCommit; + Win32_ULONGLONG sizeOfHeapReserve; + Win32_ULONGLONG sizeOfHeapCommit; + Win32_DWORD loaderFlags; + Win32_DWORD numberOfRvaAndSizes; + Win32_IMAGE_DATA_DIRECTORY[16] dataDirectory; +} + +def Win32_PIMAGE_OPTIONAL_HEADER64 = Win32_IMAGE_OPTIONAL_HEADER64*; +struct Win32_IMAGE_FILE_HEADER +{ + Win32_WORD machine; + Win32_WORD numberOfSections; + Win32_DWORD timeDateStamp; + Win32_DWORD pointerToSymbolTable; + Win32_DWORD numberOfSymbols; + Win32_WORD sizeOfOptionalHeader; + Win32_WORD characteristics; +} + +def Win32_PIMAGE_FILE_HEADER = Win32_IMAGE_FILE_HEADER*; + +struct Win32_IMAGE_NT_HEADERS +{ + Win32_DWORD signature; + Win32_IMAGE_FILE_HEADER fileHeader; + Win32_IMAGE_OPTIONAL_HEADER64 optionalHeader; +} + +def Win32_PIMAGE_NT_HEADERS = Win32_IMAGE_NT_HEADERS*; + +struct Win32_SYMBOL_INFO +{ + Win32_ULONG sizeOfStruct; + Win32_ULONG typeIndex; + Win32_ULONG64[2] reserved; + Win32_ULONG index; + Win32_ULONG size; + Win32_ULONG64 modBase; + Win32_ULONG flags; + Win32_ULONG64 value; + Win32_ULONG64 address; + Win32_ULONG register; + Win32_ULONG scope; + Win32_ULONG tag; + Win32_ULONG nameLen; + Win32_ULONG maxNameLen; + Win32_CHAR[1] name; +} + +def Win32_PSYMBOL_INFO = Win32_SYMBOL_INFO*; + +struct Win32_MODLOAD_DATA +{ + Win32_DWORD ssize; + Win32_DWORD ssig; + Win32_PVOID data; + Win32_DWORD size; + Win32_DWORD flags; +} + +enum Win32_ADDRESS_MODE +{ + ADDR_MODE_1616, + ADDR_MODE_1632, + ADDR_MODE_REAL, + ADDR_MODE_FLAT, +} + +struct Win32_ADDRESS64 +{ + Win32_DWORD64 offset; + Win32_WORD segment; + Win32_ADDRESS_MODE mode; +} + +struct Win32_KDHELP64 +{ + Win32_DWORD64 thread; + Win32_DWORD thCallbackStack; + Win32_DWORD thCallbackBStore; + Win32_DWORD nextCallback; + Win32_DWORD framePointer; + Win32_DWORD64 kiCallUserMode; + Win32_DWORD64 keUserCallbackDispatcher; + Win32_DWORD64 systemRangeStart; + Win32_DWORD64 kiUserExceptionDispatcher; + Win32_DWORD64 stackBase; + Win32_DWORD64 stackLimit; + Win32_DWORD buildVersion; + Win32_DWORD retpolineStubFunctionTableSize; + Win32_DWORD64 retpolineStubFunctionTable; + Win32_DWORD retpolineStubOffset; + Win32_DWORD retpolineStubSize; + Win32_DWORD64[2] reserved0; +} + +struct Win32_STACKFRAME64 +{ + Win32_ADDRESS64 addrPC; + Win32_ADDRESS64 addrReturn; + Win32_ADDRESS64 addrFrame; + Win32_ADDRESS64 addrStack; + Win32_ADDRESS64 addrBStore; + Win32_PVOID funcTableEntry; + Win32_DWORD64[4] params; + Win32_BOOL far; + Win32_BOOL virtual; + Win32_DWORD64[3] reserved; + Win32_KDHELP64 kdHelp; +} + +def Win32_PREAD_PROCESS_MEMORY_ROUTINE64 = fn Win32_BOOL(Win32_HANDLE hProcess, Win32_DWORD64 qwBaseAddress, Win32_PVOID lpBuffer, Win32_DWORD nSize, Win32_LPDWORD lpNumberOfBytesRead); +def Win32_PFUNCTION_TABLE_ACCESS_ROUTINE64 = fn Win32_PVOID(Win32_HANDLE ahProcess, Win32_DWORD64 addrBase); +def Win32_PGET_MODULE_BASE_ROUTINE64 = fn Win32_DWORD64(Win32_HANDLE hProcess, Win32_DWORD64 address); +def Win32_PTRANSLATE_ADDRESS_ROUTINE64 = fn Win32_DWORD64(Win32_HANDLE hProcess, Win32_HANDLE hThread, Win32_LPADDRESS64 lpaddr); +def Win32_PKDHELP64 = Win32_KDHELP64*; +def Win32_LPADDRESS64 = Win32_ADDRESS64*; +def Win32_LPSTACKFRAME64 = Win32_STACKFRAME64*; +def Win32_PMODLOAD_DATA = Win32_MODLOAD_DATA*; +def Win32_PIMAGEHLP_LINE64 = Win32_IMAGEHLP_LINE64*; +def Win32_LPMODULEINFO = Win32_MODULEINFO*; + diff --git a/releasenotes.md b/releasenotes.md index bfe04c53c..c9d5f0bee 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -30,7 +30,7 @@ - New generic syntax. - Slice initialization. - `$feature` for feature flags. -- Improved stack trace. +- Native stacktrace for Linux, MacOS and Windows. - Macro ref parameters are now of pointer type and ref parameters are not assignable. - Added `nextcase default`. - Added `$embed` to embed binary data. diff --git a/resources/linux_stack.c3 b/resources/linux_stack.c3 index 0ce201512..b4e829f23 100644 --- a/resources/linux_stack.c3 +++ b/resources/linux_stack.c3 @@ -5,11 +5,7 @@ import std::os; fn void! test2() { - BacktraceList list = linux::backtrace_load(mem::heap())!; - foreach (Backtrace trace : list) - { - io::printfn("%s", trace); - } + builtin::print_backtrace("hello", 1); } fn void test1() diff --git a/resources/msvc_stack.c3 b/resources/msvc_stack.c3 new file mode 100644 index 000000000..3e564e928 --- /dev/null +++ b/resources/msvc_stack.c3 @@ -0,0 +1,18 @@ +module test; +import std::io; +import std::collections::map; +import std::os; + +fn void test1() +{ + builtin::print_backtrace("hello", 1); + + + int x = 1; + return; +} + +fn void main() +{ + test1(); +} \ No newline at end of file diff --git a/src/compiler/linker.c b/src/compiler/linker.c index 942f63afb..17146b24b 100644 --- a/src/compiler/linker.c +++ b/src/compiler/linker.c @@ -78,6 +78,7 @@ static const char *string_esc(const char *str) static void linker_setup_windows(const char ***args_ref, LinkerType linker_type, const char ***additional_linked_ref) { add_arg(active_target.win.use_win_subsystem ? "/SUBSYSTEM:WINDOWS" : "/SUBSYSTEM:CONSOLE"); + vec_add(*additional_linked_ref, "dbghelp"); if (linker_type == LINKER_CC) return; //add_arg("/MACHINE:X64"); bool is_debug = false; @@ -382,8 +383,9 @@ static const char *find_linux_crt_begin(void) return NULL; } -static void linker_setup_linux(const char ***args_ref, LinkerType linker_type) +static void linker_setup_linux(const char ***args_ref, LinkerType linker_type, const char ***additional_linked_ref) { + vec_add(*additional_linked_ref, "dl"); if (linker_type == LINKER_CC) { if (!link_libc()) @@ -391,7 +393,6 @@ static void linker_setup_linux(const char ***args_ref, LinkerType linker_type) add_arg("-nostdlib"); return; } - vec_add(active_target.linker_libs, "dl"); if (active_target.debug_info == DEBUG_INFO_FULL) { add_arg("-rdynamic"); @@ -438,7 +439,6 @@ static void linker_setup_linux(const char ***args_ref, LinkerType linker_type) add_arg2("-L", crt_dir); add_arg("-L"); add_arg("/usr/lib/x86_64-linux-gnu/libdl.so"); - add_arg("-ldl"); add_arg("--dynamic-linker=/lib64/ld-linux-x86-64.so.2"); add_arg("-lm"); add_arg("-lpthread"); @@ -583,7 +583,7 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns linker_setup_freebsd(args_ref, linker_type); break; case OS_TYPE_LINUX: - linker_setup_linux(args_ref, linker_type); + linker_setup_linux(args_ref, linker_type, &additional_linked); break; case OS_TYPE_UNKNOWN: if (link_libc()) diff --git a/src/compiler/target.c b/src/compiler/target.c index 3a5ebaed1..d105f2d34 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -34,7 +34,7 @@ int target_alloca_addr_space() bool os_supports_stacktrace(OsType os_type) { - return os_type == OS_TYPE_LINUX || os_is_apple(os_type); + return os_type == OS_TYPE_LINUX || os_is_apple(os_type) || os_type == OS_TYPE_WIN32; } bool os_is_apple(OsType os_type) { diff --git a/src/version.h b/src/version.h index 4008805b9..9da4ab102 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.703" +#define COMPILER_VERSION "0.4.704"