Fix Windows arm64 build (#371)
* Build arm64 Windows executable * Use dbghelp to print a stacktrace and use addr2line as last resort * Print stack depth * Cleanup windows.c includes + use OS_Basename + define [RX]_OK ourselves * Cannot do _access(X_OK) on Windows * SymInitialize: don't invade process * Use _access_s * Ignore failure to get symbol line and line number * Only consider using addr2line when ADDR2LINE is set
This commit is contained in:
parent
3d67fd730d
commit
e3a50e34d1
|
@ -18,8 +18,9 @@ jobs:
|
||||||
- { name: 'Linux', arch: 'x64', os: ubuntu-latest, werror: true }
|
- { name: 'Linux', arch: 'x64', os: ubuntu-latest, werror: true }
|
||||||
- { name: 'Linux', arch: 'arm64', os: ubuntu-latest, werror: true, cmake-toolchain-file: 'cmake/toolchains/linux-aarch64.cmake', apt-packages: 'gcc-aarch64-linux-gnu g++-aarch64-linux-gnu', cross: true }
|
- { name: 'Linux', arch: 'arm64', os: ubuntu-latest, werror: true, cmake-toolchain-file: 'cmake/toolchains/linux-aarch64.cmake', apt-packages: 'gcc-aarch64-linux-gnu g++-aarch64-linux-gnu', cross: true }
|
||||||
- { name: 'MacOS', arch: 'arm64-x64', os: macos-latest, werror: true, cmake-args: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"' }
|
- { name: 'MacOS', arch: 'arm64-x64', os: macos-latest, werror: true, cmake-args: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"' }
|
||||||
- { name: 'Windows', arch: 'Win32', os: windows-latest }
|
- { name: 'Windows', arch: 'x86', os: windows-latest, msvc-arch: 'Win32' }
|
||||||
- { name: 'Windows', arch: 'x64', os: windows-latest }
|
- { name: 'Windows', arch: 'x64', os: windows-latest, msvc-arch: 'x64' }
|
||||||
|
# - { name: 'Windows', arch: 'arm64', os: windows-latest, msvc-arch: 'amd64_arm64', cross: true }
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
@ -33,9 +34,9 @@ jobs:
|
||||||
- name: Set up Ninja
|
- name: Set up Ninja
|
||||||
uses: aseprite/get-ninja@main
|
uses: aseprite/get-ninja@main
|
||||||
- uses: ilammy/msvc-dev-cmd@v1.13.0
|
- uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||||
if: runner.os == 'Windows'
|
if: ${{ !!matrix.platform.msvc-arch }}
|
||||||
with:
|
with:
|
||||||
arch: ${{ matrix.platform.arch }}
|
arch: ${{ matrix.platform.msvc-arch }}
|
||||||
- name: Install Linux dependencies
|
- name: Install Linux dependencies
|
||||||
if: ${{ runner.os == 'Linux' }}
|
if: ${{ runner.os == 'Linux' }}
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -2,61 +2,202 @@
|
||||||
|
|
||||||
// this has to be first
|
// this has to be first
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <dbghelp.h>
|
||||||
#include <imagehlp.h>
|
|
||||||
|
|
||||||
#include "harness/config.h"
|
#include "harness/config.h"
|
||||||
#include "harness/os.h"
|
#include "harness/os.h"
|
||||||
|
#include "harness/trace.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <errno.h> /* errno, strerror */
|
||||||
#include <direct.h>
|
#include <io.h> /* _access_s, F_OK */
|
||||||
#include <errno.h>
|
#include <stddef.h>
|
||||||
#include <limits.h>
|
#include <stdio.h> /* errno_t, FILE, fgets, fopen_s, fprintf*/
|
||||||
#include <signal.h>
|
#include <stdlib.h> /* _splitpath */
|
||||||
#include <stdbool.h>
|
#include <string.h> /* strcpy, strerror, strlen, strrchr */
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifdef _WIN64
|
#ifndef F_OK
|
||||||
#define Esp Rsp
|
#define F_OK 0
|
||||||
#define Eip Rip
|
|
||||||
#define Ebp Rbp
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void dr_dprintf(char* fmt_string, ...);
|
void dr_dprintf(char* fmt_string, ...);
|
||||||
|
|
||||||
static int stack_nbr = 0;
|
static int stack_nbr = 0;
|
||||||
static char windows_program_name[1024];
|
static char windows_program_name[1024];
|
||||||
|
static char path_addr2line[1024];
|
||||||
|
|
||||||
static char dirname_buf[_MAX_DIR];
|
static char dirname_buf[_MAX_DIR];
|
||||||
static char fname_buf[_MAX_FNAME];
|
static char fname_buf[_MAX_FNAME];
|
||||||
|
|
||||||
static int addr2line(char const* const program_name, void const* const addr) {
|
#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86)
|
||||||
char addr2line_cmd[512] = { 0 };
|
#define DETHRACE_CPU_X86 1
|
||||||
|
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
|
||||||
|
#define DETHRACE_CPU_X64 1
|
||||||
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||||
|
#define DETHRACE_CPU_ARM64 1
|
||||||
|
#elif defined(__arm__) || defined(_M_ARM)
|
||||||
|
#define DETHRACE_CPU_ARM32 1
|
||||||
|
#endif
|
||||||
|
|
||||||
sprintf(addr2line_cmd, "addr2line -f -p -e %.256s %p", program_name, addr);
|
#if !(defined(DETHRACE_CPU_X86) || defined(DETHRACE_CPU_X64) || defined(DETHRACE_CPU_ARM32) || defined(DETHRACE_CPU_ARM64))
|
||||||
|
#pragma message("Unsupported architecture: don't know how to StackWalk")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static BOOL print_addr2line_address_location(HANDLE const hProcess, const DWORD64 address) {
|
||||||
|
char addr2line_cmd[1024] = { 0 };
|
||||||
|
const char *program_name = windows_program_name;
|
||||||
|
IMAGEHLP_MODULE64 module_info;
|
||||||
|
|
||||||
|
if (path_addr2line[0] == '\0') {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&module_info, 0, sizeof(module_info));
|
||||||
|
module_info.SizeOfStruct = sizeof(module_info);
|
||||||
|
if (SymGetModuleInfo64(hProcess, address, &module_info)) {
|
||||||
|
program_name = module_info.ImageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(addr2line_cmd, "\"%.256s\" -f -p -e %.256s %lx", path_addr2line, program_name, (long int)address);
|
||||||
|
|
||||||
|
system(addr2line_cmd);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printf_windows_message(const char *format, ...) {
|
||||||
|
va_list ap;
|
||||||
|
char win_msg[512];
|
||||||
|
FormatMessageA(
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL,
|
||||||
|
GetLastError(),
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
win_msg, sizeof(win_msg)/sizeof(*win_msg),
|
||||||
|
NULL);
|
||||||
|
size_t win_msg_len = strlen(win_msg);
|
||||||
|
while (win_msg[win_msg_len-1] == '\r' || win_msg[win_msg_len-1] == '\n' || win_msg[win_msg_len-1] == ' ') {
|
||||||
|
win_msg[win_msg_len-1] = '\0';
|
||||||
|
win_msg_len--;
|
||||||
|
}
|
||||||
|
va_start(ap, format);
|
||||||
|
vfprintf(stderr, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fprintf(stderr, " (%s)\n", win_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_dbghelp(HANDLE const hProcess) {
|
||||||
|
|
||||||
|
if (!SymInitialize(hProcess, NULL, FALSE)) {
|
||||||
|
printf_windows_message("SymInitialize failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SymRefreshModuleList(hProcess)) {
|
||||||
|
printf_windows_message("SymRefreshModuleList failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup_dbghelp(HANDLE const hProcess) {
|
||||||
|
|
||||||
|
SymCleanup(hProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL print_dbghelp_address_location(HANDLE const hProcess, const DWORD64 address) {
|
||||||
|
IMAGEHLP_MODULE64 module_info;
|
||||||
|
union {
|
||||||
|
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
|
||||||
|
SYMBOL_INFO symbol_info;
|
||||||
|
} symbol;
|
||||||
|
DWORD64 dwDisplacement;
|
||||||
|
DWORD lineColumn = 0;
|
||||||
|
IMAGEHLP_LINE64 line;
|
||||||
|
const char *image_file_name;
|
||||||
|
const char *symbol_name;
|
||||||
|
const char *file_name;
|
||||||
|
char line_number[16];
|
||||||
|
|
||||||
|
memset(&module_info, 0, sizeof(module_info));
|
||||||
|
module_info.SizeOfStruct = sizeof(module_info);
|
||||||
|
if (!SymGetModuleInfo64(hProcess, address, &module_info)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
image_file_name = OS_Basename(module_info.ImageName);
|
||||||
|
|
||||||
|
memset(&symbol, 0, sizeof(symbol));
|
||||||
|
symbol.symbol_info.SizeOfStruct = sizeof(symbol.symbol_info);
|
||||||
|
symbol.symbol_info.MaxNameLen = MAX_SYM_NAME;
|
||||||
|
if (!SymFromAddr(hProcess, address, &dwDisplacement, &symbol.symbol_info)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
symbol_name = symbol.symbol_info.Name;
|
||||||
|
|
||||||
|
line.SizeOfStruct = sizeof(line);
|
||||||
|
if (SymGetLineFromAddr64(hProcess, address, &lineColumn, &line)) {
|
||||||
|
file_name = line.FileName;
|
||||||
|
snprintf(line_number, sizeof(line_number), "Line %u", (unsigned int)line.LineNumber);
|
||||||
|
} else {
|
||||||
|
file_name = "";
|
||||||
|
line_number[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "0x%lx %s!%s+0x%lx %s %s\n", (long unsigned int)address, image_file_name, symbol_name, (long unsigned int)dwDisplacement, file_name, line_number);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_address_location(HANDLE hProcess, DWORD64 address) {
|
||||||
|
IMAGEHLP_MODULE64 module_info;
|
||||||
|
|
||||||
fprintf(stderr, "%d: ", stack_nbr++);
|
fprintf(stderr, "%d: ", stack_nbr++);
|
||||||
return system(addr2line_cmd);
|
if (print_dbghelp_address_location(hProcess, address)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (print_addr2line_address_location(hProcess, address)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&module_info, 0, sizeof(module_info));
|
||||||
|
module_info.SizeOfStruct = sizeof(module_info);
|
||||||
|
if (SymGetModuleInfo64(hProcess, address, &module_info)) {
|
||||||
|
fprintf(stderr, "%s 0x%lx\n", module_info.ImageName, (long unsigned int)address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "0x%lx\n", (long unsigned int)address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_stacktrace(CONTEXT* context) {
|
static void print_stacktrace(CONTEXT* context) {
|
||||||
|
HANDLE hProcess = GetCurrentProcess();
|
||||||
|
|
||||||
SymInitialize(GetCurrentProcess(), 0, true);
|
init_dbghelp(hProcess);
|
||||||
|
|
||||||
STACKFRAME frame = { 0 };
|
STACKFRAME frame = { 0 };
|
||||||
|
|
||||||
/* setup initial stack frame */
|
/* setup initial stack frame */
|
||||||
frame.AddrPC.Offset = context->Eip;
|
|
||||||
frame.AddrPC.Mode = AddrModeFlat;
|
frame.AddrPC.Mode = AddrModeFlat;
|
||||||
frame.AddrStack.Offset = context->Esp;
|
|
||||||
frame.AddrStack.Mode = AddrModeFlat;
|
frame.AddrStack.Mode = AddrModeFlat;
|
||||||
frame.AddrFrame.Offset = context->Ebp;
|
|
||||||
frame.AddrFrame.Mode = AddrModeFlat;
|
frame.AddrFrame.Mode = AddrModeFlat;
|
||||||
|
#if defined(DETHRACE_CPU_X86)
|
||||||
|
DWORD machine_type = IMAGE_FILE_MACHINE_I386;
|
||||||
|
frame.AddrFrame.Offset = context->Ebp;
|
||||||
|
frame.AddrStack.Offset = context->Esp;
|
||||||
|
frame.AddrPC.Offset = context->Eip;
|
||||||
|
#elif defined(DETHRACE_CPU_X64)
|
||||||
|
DWORD machine_type = IMAGE_FILE_MACHINE_AMD64;
|
||||||
|
frame.AddrFrame.Offset = context->Rbp;
|
||||||
|
frame.AddrStack.Offset = context->Rsp;
|
||||||
|
frame.AddrPC.Offset = context->Rip;
|
||||||
|
#elif defined(DETHRACE_CPU_ARM32)
|
||||||
|
DWORD machine_type = IMAGE_FILE_MACHINE_ARM;
|
||||||
|
frame.AddrFrame.Offset = context->Lr;
|
||||||
|
frame.AddrStack.Offset = context->Sp;
|
||||||
|
frame.AddrPC.Offset = context->Pc;
|
||||||
|
#elif defined(DETHRACE_CPU_ARM64)
|
||||||
|
DWORD machine_type = IMAGE_FILE_MACHINE_ARM64;
|
||||||
|
frame.AddrFrame.Offset = context->Fp;
|
||||||
|
frame.AddrStack.Offset = context->Sp;
|
||||||
|
frame.AddrPC.Offset = context->Pc;
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "Unsupported architecture: cannot produce a stacktrace\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
while (StackWalk(IMAGE_FILE_MACHINE_I386,
|
while (StackWalk(machine_type,
|
||||||
GetCurrentProcess(),
|
GetCurrentProcess(),
|
||||||
GetCurrentThread(),
|
GetCurrentThread(),
|
||||||
&frame,
|
&frame,
|
||||||
|
@ -65,13 +206,18 @@ static void print_stacktrace(CONTEXT* context) {
|
||||||
SymFunctionTableAccess,
|
SymFunctionTableAccess,
|
||||||
SymGetModuleBase,
|
SymGetModuleBase,
|
||||||
0)) {
|
0)) {
|
||||||
addr2line(windows_program_name, (void*)frame.AddrPC.Offset);
|
|
||||||
|
if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
|
||||||
|
fprintf(stderr, "PC == Return Address => Possible endless callstack\n");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymCleanup(GetCurrentProcess());
|
print_address_location(hProcess, frame.AddrPC.Offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo) {
|
static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo) {
|
||||||
|
HANDLE hProcess;
|
||||||
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
||||||
case EXCEPTION_ACCESS_VIOLATION:
|
case EXCEPTION_ACCESS_VIOLATION:
|
||||||
fputs("Error: EXCEPTION_ACCESS_VIOLATION\n", stderr);
|
fputs("Error: EXCEPTION_ACCESS_VIOLATION\n", stderr);
|
||||||
|
@ -138,18 +284,40 @@ static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
|
hProcess = GetCurrentProcess();
|
||||||
|
init_dbghelp(hProcess);
|
||||||
/* If this is a stack overflow then we can't walk the stack, so just show
|
/* If this is a stack overflow then we can't walk the stack, so just show
|
||||||
where the error happened */
|
where the error happened */
|
||||||
if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
||||||
print_stacktrace(ExceptionInfo->ContextRecord);
|
print_stacktrace(ExceptionInfo->ContextRecord);
|
||||||
} else {
|
} else {
|
||||||
addr2line(windows_program_name, (void*)ExceptionInfo->ContextRecord->Eip);
|
#if defined(DETHRACE_CPU_X86)
|
||||||
|
DWORD64 addr = (DWORD64)ExceptionInfo->ContextRecord->Eip;
|
||||||
|
#elif defined(DETHRACE_CPU_X64)
|
||||||
|
DWORD64 addr = (DWORD64)ExceptionInfo->ContextRecord->Rip;
|
||||||
|
#elif defined(DETHRACE_CPU_ARM32) || defined(DETHRACE_CPU_ARM64)
|
||||||
|
DWORD64 addr = (DWORD64)ExceptionInfo->ContextRecord->Pc;
|
||||||
|
#endif
|
||||||
|
print_address_location(hProcess, addr);
|
||||||
}
|
}
|
||||||
|
cleanup_dbghelp(hProcess);
|
||||||
|
|
||||||
return EXCEPTION_EXECUTE_HANDLER;
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_InstallSignalHandler(char* program_name) {
|
void OS_InstallSignalHandler(char* program_name) {
|
||||||
|
const char *env_addr2line;
|
||||||
|
|
||||||
|
path_addr2line[0] = '\0';
|
||||||
|
env_addr2line = getenv("ADDR2LINE");
|
||||||
|
if (env_addr2line != NULL) {
|
||||||
|
errno_t e = _access_s(env_addr2line, F_OK);
|
||||||
|
if (e == 0) {
|
||||||
|
strcpy(path_addr2line, env_addr2line);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "ADDR2LINE does not exist (%s)\n", path_addr2line);
|
||||||
|
}
|
||||||
|
}
|
||||||
strcpy(windows_program_name, program_name);
|
strcpy(windows_program_name, program_name);
|
||||||
SetUnhandledExceptionFilter(windows_exception_handler);
|
SetUnhandledExceptionFilter(windows_exception_handler);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue