Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Windows arm64 build #371

Merged
merged 10 commits into from
Jul 9, 2024
15 changes: 8 additions & 7 deletions .github/workflows/workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ jobs:
strategy:
matrix:
platform:
- { 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: '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: 'x64', os: windows-latest }
- { 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: 'MacOS', arch: 'arm64-x64', os: macos-latest, werror: true, cmake-args: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"' }
- { name: 'Windows', arch: 'x86', os: windows-latest, msvc-arch: 'Win32' }
- { name: 'Windows', arch: 'x64', os: windows-latest, msvc-arch: 'x64' }
# - { name: 'Windows', arch: 'arm64', os: windows-latest, msvc-arch: 'amd64_arm64', cross: true }

defaults:
run:
Expand All @@ -33,9 +34,9 @@ jobs:
- name: Set up Ninja
uses: aseprite/get-ninja@main
- uses: ilammy/[email protected]
if: runner.os == 'Windows'
if: ${{ !!matrix.platform.msvc-arch }}
with:
arch: ${{ matrix.platform.arch }}
arch: ${{ matrix.platform.msvc-arch }}
- name: Install Linux dependencies
if: ${{ runner.os == 'Linux' }}
run: |
Expand Down
244 changes: 206 additions & 38 deletions src/harness/os/windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,222 @@

// this has to be first
#include <windows.h>

#include <imagehlp.h>
#include <dbghelp.h>

#include "harness/config.h"
#include "harness/os.h"
#include "harness/trace.h"

#include <errno.h> /* errno, strerror */
#include <io.h> /* _access_s, F_OK */
#include <stddef.h>
#include <stdio.h> /* errno_t, FILE, fgets, fopen_s, fprintf*/
#include <stdlib.h> /* _splitpath */
#include <string.h> /* strcpy, strerror, strlen, strrchr */

#include <assert.h>
#include <direct.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN64
#define Esp Rsp
#define Eip Rip
#define Ebp Rbp
#ifndef F_OK
#define F_OK 0
#endif

void dr_dprintf(char* fmt_string, ...);

static int stack_nbr = 0;
static char windows_program_name[1024];
static char path_addr2line[1024];

static char dirname_buf[_MAX_DIR];
static char fname_buf[_MAX_FNAME];

static int addr2line(char const* const program_name, void const* const addr) {
char addr2line_cmd[512] = { 0 };
#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86)
#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

#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;
}

sprintf(addr2line_cmd, "addr2line -f -p -e %.256s %p", program_name, addr);
static void print_address_location(HANDLE hProcess, DWORD64 address) {
IMAGEHLP_MODULE64 module_info;

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) {
HANDLE hProcess = GetCurrentProcess();

SymInitialize(GetCurrentProcess(), 0, true);
init_dbghelp(hProcess);

STACKFRAME frame = { 0 };

/* setup initial stack frame */
frame.AddrPC.Offset = context->Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = context->Esp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context->Ebp;
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,
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0)) {
addr2line(windows_program_name, (void*)frame.AddrPC.Offset);
}
while (StackWalk(machine_type,
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0)) {

SymCleanup(GetCurrentProcess());
if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
fprintf(stderr, "PC == Return Address => Possible endless callstack\n");
break;
}

print_address_location(hProcess, frame.AddrPC.Offset);
}
}

static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo) {
HANDLE hProcess;
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
fputs("Error: EXCEPTION_ACCESS_VIOLATION\n", stderr);
Expand Down Expand Up @@ -138,18 +284,40 @@ static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo)
break;
}
fflush(stderr);
hProcess = GetCurrentProcess();
init_dbghelp(hProcess);
/* If this is a stack overflow then we can't walk the stack, so just show
where the error happened */
if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
print_stacktrace(ExceptionInfo->ContextRecord);
} 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;
}

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);
SetUnhandledExceptionFilter(windows_exception_handler);
}
Expand Down