Skip to content

Commit

Permalink
Fix Windows arm64 build (#371)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
madebr authored Jul 9, 2024
1 parent 3d67fd7 commit e3a50e3
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 45 deletions.
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

0 comments on commit e3a50e3

Please sign in to comment.