mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
Add ARM64 Windows platform support
Add complete ARM64 Windows (AArch64 WoA) support to SBCL. This enables building and running SBCL natively on Windows 11 ARM devices. Key design decisions: - x18 register reserved as TEB pointer (Windows ARM64 ABI requirement), matching the existing Darwin reservation - Safepoint GC with fake_foreign_function_call to save register context for precise stack scanning (ARM64 uses separate C and control stacks) - VEH (Vectored Exception Handling) for ARM64, decoding BRK instructions which Windows reports as EXCEPTION_ILLEGAL_INSTRUCTION - sb_udivmodti4 takes hi/lo split arguments to avoid __uint128_t ABI issues on LLP64 where unsigned long is 32 bits - FP exception traps disabled (ARM64 Windows does not deliver FP exceptions even with FPCR trap bits set) - Dynamic space pages committed on demand (Windows reserves but does not commit the full dynamic space upfront) New files: - src/runtime/Config.arm64-win32: Build configuration - src/runtime/arm64-win32-os.c: OS layer (VEH, context access, FP) - src/runtime/arm64-win32-os.h: Platform header - .github/workflows/windows-arm64.yml: CI workflow Modified files grouped by subsystem: Runtime (ARM64-specific): - arm64-arch.c: Windows memory APIs, LLP64-safe pointer storage, sb_udivmodti4 split-argument ABI, static space NIL header guard - arm64-assem.S: Windows calling conventions for call_into_lisp and call_into_c, TlsGetValue for thread pointer, x18 reservation Runtime (Windows general): - win32-os.c: ARM64 safepoint page handling, BRK exception decode, ARM64 VEH, dynamic space page commit, ARM64 debug printing Compiler: - vm.lisp: Reserve x18 on Windows (matching Darwin) - c-call.lisp: Don't zero control-stack-pointer on Windows (not FFCA) - parms.lisp: Address space layout for Windows ARM64 - tramps.lisp: Save CSP to thread for GC, don't zero CSP on Windows - genesis.lisp: GC_SAFEPOINT_PAGE_ADDR for non-x86-64 platforms - parms.lisp (generic): Update safepoint space comment - debug-int.lisp: NFP handling for ARM64 Windows - float-trap.lisp: Disable FP traps on ARM64 Windows - coreparse.c: ARM64 guard for TEXT_SPACE_START assignment Build system: - make-config.sh: ARM64 Windows detection and feature flags - make-windows-installer.sh: ARM64 architecture in installer CI: - linux-arm64.yml, mac.yml: Robustness fixes for repo naming - windows-arm64.yml: New workflow for ARM64 Windows builds Tests: - subr.sh: Auto-detect sbcl.exe on Windows Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
66e8662c13
commit
aa0fa1c56e
68
.github/workflows/windows-arm64.yml
vendored
Normal file
68
.github/workflows/windows-arm64.yml
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
name: Windows arm64
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-11-arm
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { sys: clangarm64, env: clang-aarch64, arch: arm64}
|
||||
|
||||
fail-fast: false
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
install: mingw-w64-${{matrix.env}}-clang mingw-w64-${{matrix.env}}-toolchain make diffutils git libzstd-devel
|
||||
msystem: ${{matrix.sys}}
|
||||
|
||||
- name: install host sbcl
|
||||
shell: pwsh
|
||||
run: |
|
||||
choco install sbcl -source tools-for-build
|
||||
- name: install WiX Toolset
|
||||
shell: pwsh
|
||||
run: |
|
||||
choco install wixtoolset
|
||||
$wixPath = (Get-ChildItem "C:\Program Files (x86)\WiX Toolset*" | Select-Object -First 1).FullName
|
||||
echo "WIX=$wixPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
- name: build
|
||||
env:
|
||||
SBCL_HOME: "/c/Program Files/Steel Bank Common Lisp/1.4.14"
|
||||
run: |
|
||||
PATH=$PATH:"/c/Program Files/Steel Bank Common Lisp/1.4.14"
|
||||
export PATH
|
||||
./make.sh --arch=${{matrix.arch}} --with-sb-core-compression --xc-host='sbcl --lose-on-corruption --disable-ldb --disable-debugger'
|
||||
- name: make installer
|
||||
env:
|
||||
WIX: ${{ env.WIX }}
|
||||
run: |
|
||||
./make-windows-installer.sh
|
||||
mkdir sbcl-windows-installer; mv output/*msi sbcl-windows-installer
|
||||
- name: save installer
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sbcl-windows-installer-${{ matrix.sys }}
|
||||
path: sbcl-windows-installer
|
||||
- name: tests
|
||||
continue-on-error: true
|
||||
working-directory: tests
|
||||
run: ./run-tests.sh
|
||||
- name: ansi-tests
|
||||
continue-on-error: true
|
||||
working-directory: tests
|
||||
run: ./ansi-tests.sh
|
||||
|
||||
|
||||
|
||||
|
|
@ -694,6 +694,17 @@ case "$sbcl_os" in
|
|||
printf ' :os-provides-dlopen' >> $ltf
|
||||
printf ' :sb-thread :sb-safepoint' >> $ltf
|
||||
#
|
||||
case "$sbcl_arch" in
|
||||
arm64)
|
||||
printf ' :immobile-space' >> $ltf
|
||||
;;
|
||||
x86 | x86-64)
|
||||
;;
|
||||
*)
|
||||
echo "unsupported architecture for win32: $sbcl_arch"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
link_or_copy Config.$sbcl_arch-win32 Config
|
||||
link_or_copy $sbcl_arch-win32-os.h target-arch-os.h
|
||||
link_or_copy win32-os.h target-os.h
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ cd ./output
|
|||
:if-exists :supersede)
|
||||
(format f "~a-~a-windows-binary"
|
||||
(lisp-implementation-version)
|
||||
#+x86 "x86" #+x86-64 "x86-64"))
|
||||
#+x86 "x86" #+x86-64 "x86-64" #+arm64 "arm64"))
|
||||
(exit))'
|
||||
|
||||
"$WIX_PATH/candle" sbcl.wxs
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@
|
|||
(inst ldr lr-tn (@ csp-tn -104))
|
||||
|
||||
(inst sub csp-tn csp-tn (+ 32 80)) ;; deallocate the frame
|
||||
#+sb-thread
|
||||
#+(and sb-thread (not win32))
|
||||
(inst str zr-tn (@ thread-tn (* thread-control-stack-pointer-slot n-word-bytes)))
|
||||
#-sb-thread
|
||||
(progn
|
||||
|
|
@ -146,6 +146,7 @@
|
|||
(map-pairs ldp csp-tn -16 lisp-registers :delta -16)
|
||||
(inst ldr lr-tn (@ csp-tn -104))
|
||||
(inst sub csp-tn csp-tn (+ 32 80))
|
||||
#-win32
|
||||
(inst str zr-tn (@ thread-tn (* thread-control-stack-pointer-slot n-word-bytes))))
|
||||
(map-pairs ldp nsp-tn 64 nl-registers :post-index 80 :delta -16)
|
||||
(inst ret))
|
||||
|
|
@ -167,6 +168,7 @@
|
|||
(map-pairs ldp csp-tn -16 lisp-registers :delta -16)
|
||||
(inst ldr lr-tn (@ csp-tn -104))
|
||||
(inst sub csp-tn csp-tn (+ 32 80))
|
||||
#-win32
|
||||
(inst str zr-tn (@ thread-tn (* thread-control-stack-pointer-slot n-word-bytes))))
|
||||
(map-pairs ldp nsp-tn 64 nl-registers :post-index 80 :delta -16)
|
||||
(inst ret)))))
|
||||
|
|
|
|||
|
|
@ -2625,11 +2625,11 @@ register."
|
|||
:invalid-value-for-unescaped-register-storage))
|
||||
(with-nfp ((var) &body body)
|
||||
;; x86oids have no separate number stack, so dummy it
|
||||
;; up for them.
|
||||
#+c-stack-is-control-stack
|
||||
;; up for them. ARM64 Windows is similar - use control stack pointer.
|
||||
#+(or c-stack-is-control-stack (and arm64 win32))
|
||||
`(let ((,var fp))
|
||||
,@body)
|
||||
#-c-stack-is-control-stack
|
||||
#-(or c-stack-is-control-stack (and arm64 win32))
|
||||
`(let ((,var (if escaped
|
||||
(int-sap
|
||||
(context-register escaped sb-vm::nfp-offset))
|
||||
|
|
|
|||
|
|
@ -158,7 +158,9 @@ sets the floating point modes to their current values (and thus is a no-op)."
|
|||
;;; disabled by default. Joe User can explicitly enable them if
|
||||
;;; desired.
|
||||
(define-load-time-global *saved-floating-point-modes*
|
||||
'(:traps (:overflow #-(or netbsd ppc) :invalid :divide-by-zero)
|
||||
'(:traps (#-(and arm64 win32) :overflow
|
||||
#-(or netbsd ppc (and arm64 win32)) :invalid
|
||||
#-(and arm64 win32) :divide-by-zero)
|
||||
:rounding-mode :nearest :current-exceptions nil
|
||||
:accrued-exceptions nil :fast-mode nil
|
||||
#+x86 :precision #+x86 :53-bit))
|
||||
|
|
|
|||
|
|
@ -481,6 +481,7 @@
|
|||
;; No longer OK to run GC except at safepoints.
|
||||
#+(or sb-safepoint nonstop-foreign-call)
|
||||
(storew zr-tn thread-tn thread-saved-csp-slot))
|
||||
#-win32
|
||||
(storew zr-tn thread-tn thread-control-stack-pointer-slot)
|
||||
return
|
||||
#-sb-thread
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
|
||||
;;;; Where to put the different spaces.
|
||||
|
||||
(gc-space-setup #+(or linux openbsd netbsd freebsd)
|
||||
(gc-space-setup #+(or linux openbsd netbsd freebsd win32)
|
||||
#x2F0000000
|
||||
#+darwin #x300000000
|
||||
#-darwin :read-only-space-size #-darwin 0
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@
|
|||
(defreg r5 15)
|
||||
(defreg r6 16)
|
||||
(defreg r7 17)
|
||||
(defreg #-darwin r8 #+darwin reserved 18)
|
||||
;; x18 is reserved on Darwin (macOS) and Windows (TEB pointer)
|
||||
(defreg #-(or darwin win32) r8 #+(or darwin win32) reserved 18)
|
||||
(defreg r9 19)
|
||||
|
||||
(defreg r10 20)
|
||||
|
|
@ -68,7 +69,7 @@
|
|||
null cfp nsp lr)
|
||||
|
||||
(defregset descriptor-regs
|
||||
r0 r1 r2 r3 r4 r5 r6 r7 #-darwin r8 r9 r10 #-sb-thread r11 lexenv)
|
||||
r0 r1 r2 r3 r4 r5 r6 r7 #-(or darwin win32) r8 r9 r10 #-sb-thread r11 lexenv)
|
||||
|
||||
;; nl9 can't be selected by PACK as it is a freely usable temp reg
|
||||
(defregset non-descriptor-regs
|
||||
|
|
@ -76,7 +77,7 @@
|
|||
|
||||
(defregset boxed-regs
|
||||
r0 r1 r2 r3 r4 r5 r6
|
||||
r7 #-darwin r8 r9 r10 #-sb-thread r11 lexenv)
|
||||
r7 #-(or darwin win32) r8 r9 r10 #-sb-thread r11 lexenv)
|
||||
|
||||
;; registers used to pass arguments
|
||||
;;
|
||||
|
|
@ -85,7 +86,7 @@
|
|||
;; names and offsets for registers used to pass arguments
|
||||
(defregset *register-arg-offsets* r0 r1 r2 r3)
|
||||
(defconstant-eqx register-arg-names '(r0 r1 r2 r3) #'equal)
|
||||
(defregset *descriptor-args* r0 r1 r2 r3 r4 r5 r6 r7 #-darwin r8 r9 r10)
|
||||
(defregset *descriptor-args* r0 r1 r2 r3 r4 r5 r6 r7 #-(or darwin win32) r8 r9 r10)
|
||||
(defregset *non-descriptor-args* nl0 nl1 nl2 nl3 nl4 nl5 nl6 nl7 nl8)
|
||||
(defglobal *float-regs* (loop for i below 32 collect i)))
|
||||
|
||||
|
|
|
|||
|
|
@ -3297,6 +3297,8 @@ Legal values for OFFSET are -4, -8, -12, ..."
|
|||
"#define PSEUDO_ATOMIC_TRAP ~D /* 0x~:*~X */~%"
|
||||
sb-vm::pseudo-atomic-trap)
|
||||
(terpri))
|
||||
;; x86-64 uses GC_SAFEPOINT_PAGE_ADDR from gc.h (at STATIC_SPACE_END).
|
||||
;; Other platforms (including ARM64 Windows) place it before STATIC_SPACE_START.
|
||||
#+(and sb-safepoint (not x86-64))
|
||||
(progn
|
||||
(format t "#define GC_SAFEPOINT_PAGE_ADDR (void*)((char*)STATIC_SPACE_START - ~d)~%"
|
||||
|
|
|
|||
|
|
@ -83,10 +83,9 @@
|
|||
;; #+immobile-space implies a relocatable alien linkage space. And x86-64 always
|
||||
;; has relocatable linkage tables
|
||||
#-(or x86-64 immobile-space) (alien-linkage ,alien-linkage-space-size)
|
||||
;; safepoint on 64-bit uses a relocatable trap page just below the card mark
|
||||
;; table, which works nicely assuming a register is wired to the card table
|
||||
;; x86-64 uses a relocatable trap page just below the card mark
|
||||
;; table (wired to a register). Other platforms allocate a separate page.
|
||||
#+(and sb-safepoint (not x86-64))
|
||||
;; Must be just before NIL.
|
||||
(safepoint ,(symbol-value '+backend-page-bytes+))
|
||||
(static ,small-space-size)
|
||||
#+darwin-jit (static-code ,small-space-size)))
|
||||
|
|
|
|||
55
src/runtime/Config.arm64-win32
Normal file
55
src/runtime/Config.arm64-win32
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# This software is part of the SBCL system. See the README file for
|
||||
# more information.
|
||||
#
|
||||
# This software is derived from the CMU CL system, which was
|
||||
# written at Carnegie Mellon University and released into the
|
||||
# public domain. The software is in the public domain and is
|
||||
# provided with absolutely no warranty. See the COPYING and CREDITS
|
||||
# files for more information.
|
||||
|
||||
TARGET=sbcl.exe
|
||||
|
||||
ASSEM_SRC = arm64-assem.S
|
||||
ARCH_SRC = arm64-arch.c
|
||||
|
||||
OS_SRC = win32-os.c arm64-win32-os.c
|
||||
|
||||
ifdef LISP_FEATURE_SB_LINKABLE_RUNTIME
|
||||
LIBSBCL = libsbcl.a
|
||||
USE_LIBSBCL = -Wl,--whole-archive libsbcl.a -Wl,--no-whole-archive
|
||||
endif
|
||||
|
||||
LINKFLAGS = -Wl,-export-all-symbols
|
||||
LIBSBCL += mswin64.def mswin.def
|
||||
USE_LIBSBCL += -Wl,mswin64.def -Wl,mswin.def
|
||||
SOFLAGS += -Wl,-export-all-symbols -Wl,mswin64.def -Wl,mswin.def
|
||||
|
||||
__LDFLAGS__ =
|
||||
|
||||
OS_LIBS = -l ws2_32 -ladvapi32
|
||||
ifdef LISP_FEATURE_SB_CORE_COMPRESSION
|
||||
OS_LIBS += -lzstd
|
||||
endif
|
||||
ifdef LISP_FEATURE_SB_FUTEX
|
||||
OS_LIBS += -lSynchronization
|
||||
endif
|
||||
|
||||
ifdef LISP_FEATURE_IMMOBILE_SPACE
|
||||
GC_SRC = fullcgc.c gencgc.c traceroot.c immobile-space.c
|
||||
else
|
||||
GC_SRC = fullcgc.c gencgc.c traceroot.c
|
||||
endif
|
||||
|
||||
CFLAGS += -g -W -Wall \
|
||||
-Wno-unused-function -Wno-unused-parameter -Wno-cast-function-type \
|
||||
-Wno-type-limits \
|
||||
-fno-omit-frame-pointer \
|
||||
-O3 -DWINVER=0x0501 \
|
||||
-D__W32API_USE_DLLIMPORT__ \
|
||||
-std=gnu99
|
||||
|
||||
CC = clang
|
||||
|
||||
ifeq ($(shell $(LD) --disable-dynamicbase 2>&1 | grep disable-dynamicbase),)
|
||||
LINKFLAGS += -Wl,--disable-dynamicbase
|
||||
endif
|
||||
|
|
@ -25,7 +25,14 @@
|
|||
|
||||
os_vm_address_t arch_get_bad_addr(int sig, siginfo_t *code, os_context_t *context)
|
||||
{
|
||||
#ifdef WIN32
|
||||
/* The `code` argument is really a pointer to an EXCEPTION_RECORD,
|
||||
* not a siginfo_t. */
|
||||
EXCEPTION_RECORD *exception = (EXCEPTION_RECORD *)code;
|
||||
return (os_vm_address_t)exception->ExceptionInformation[1];
|
||||
#else
|
||||
return (os_vm_address_t)code->si_addr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void arch_skip_instruction(os_context_t *context)
|
||||
|
|
@ -210,20 +217,30 @@ void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
|
|||
}
|
||||
else {
|
||||
// Do orig_inst by copying it into a trampoline.
|
||||
#ifdef WIN32
|
||||
SYSTEM_INFO sys_info;
|
||||
GetSystemInfo(&sys_info);
|
||||
size_t size = sys_info.dwPageSize;
|
||||
#else
|
||||
size_t size = getpagesize();
|
||||
#endif
|
||||
// Allocate the thread-local trampoline on-demand.
|
||||
struct thread *th = get_sb_vm_thread();
|
||||
unsigned int *trampoline = (unsigned int*)th->breakpoint_misc;
|
||||
|
||||
if (!trampoline) {
|
||||
#ifdef WIN32
|
||||
trampoline = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
#else
|
||||
trampoline = mmap(NULL, size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
#endif
|
||||
th->breakpoint_misc = trampoline;
|
||||
}
|
||||
else
|
||||
os_protect((os_vm_address_t)trampoline, size, PROT_READ | PROT_WRITE);
|
||||
os_protect((os_vm_address_t)trampoline, size, OS_VM_PROT_READ | OS_VM_PROT_WRITE);
|
||||
|
||||
unsigned int *inst_ptr = trampoline;
|
||||
unsigned int inst;
|
||||
|
|
@ -246,11 +263,13 @@ void arch_do_displaced_inst(os_context_t *context, unsigned int orig_inst)
|
|||
inst = 0xD61F0000 | BREAKPOINT_TEMP_REG << 5;
|
||||
*inst_ptr++ = inst;
|
||||
|
||||
// address
|
||||
*(unsigned long *)inst_ptr++ = (unsigned long)next_pc;
|
||||
// address (8 bytes for a 64-bit pointer; unsigned long is only
|
||||
// 4 bytes on Windows LLP64, so use uint64_t explicitly)
|
||||
*(uint64_t *)inst_ptr = (uint64_t)next_pc;
|
||||
inst_ptr += 2;
|
||||
OS_CONTEXT_PC(context) = (uword_t)trampoline;
|
||||
os_flush_icache((os_vm_address_t) trampoline, (char*) inst_ptr - (char*)trampoline);
|
||||
os_protect((os_vm_address_t)trampoline, size, PROT_READ | PROT_EXEC);
|
||||
os_protect((os_vm_address_t)trampoline, size, OS_VM_PROT_READ | OS_VM_PROT_EXECUTE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -277,6 +296,7 @@ arch_handle_single_step_trap(os_context_t *context, int trap)
|
|||
arch_skip_instruction(context);
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
static void
|
||||
sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
|
||||
{
|
||||
|
|
@ -289,10 +309,11 @@ sigtrap_handler(int signal, siginfo_t *siginfo, os_context_t *context)
|
|||
|
||||
handle_trap(context, code);
|
||||
}
|
||||
#endif
|
||||
|
||||
void sigill_handler(int signal, siginfo_t *siginfo, os_context_t *context);
|
||||
|
||||
#ifndef LISP_FEATURE_DARWIN
|
||||
#if !defined(LISP_FEATURE_DARWIN) && !defined(WIN32)
|
||||
void
|
||||
sigill_handler(int signal, siginfo_t *siginfo, os_context_t *context) {
|
||||
fake_foreign_function_call(context);
|
||||
|
|
@ -302,8 +323,12 @@ sigill_handler(int signal, siginfo_t *siginfo, os_context_t *context) {
|
|||
|
||||
void arch_install_interrupt_handlers()
|
||||
{
|
||||
#ifndef WIN32
|
||||
ll_install_handler(SIGTRAP, sigtrap_handler);
|
||||
ll_install_handler(SIGILL, sigill_handler);
|
||||
#else
|
||||
// Interrupt handlers are installed in win32-os.c
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -320,7 +345,7 @@ void arch_write_linkage_table_entry(int index, void *target_addr, int datap)
|
|||
char *reloc_addr = (char*)ALIEN_LINKAGE_SPACE_START + index * ALIEN_LINKAGE_TABLE_ENTRY_SIZE;
|
||||
|
||||
if (datap) {
|
||||
*(unsigned long *)reloc_addr = (unsigned long)target_addr;
|
||||
*(uword_t *)reloc_addr = (uword_t)target_addr;
|
||||
goto DONE;
|
||||
}
|
||||
/*
|
||||
|
|
@ -341,8 +366,9 @@ void arch_write_linkage_table_entry(int index, void *target_addr, int datap)
|
|||
inst = 0xD61F0000 | LINKAGE_TEMP_REG << 5;
|
||||
*inst_ptr++ = inst;
|
||||
|
||||
// address
|
||||
*(unsigned long *)inst_ptr++ = (unsigned long)target_addr;
|
||||
// address (64-bit pointer - must use uword_t on Windows where unsigned long is 32-bit)
|
||||
*(uword_t *)inst_ptr = (uword_t)target_addr;
|
||||
inst_ptr += 2; // advance by 8 bytes (2 x 4-byte ints)
|
||||
|
||||
os_flush_icache((os_vm_address_t) reloc_addr, (char*) inst_ptr - reloc_addr);
|
||||
|
||||
|
|
@ -369,6 +395,19 @@ os_vm_address_t coreparse_alloc_space(int space_id, int attr,
|
|||
addr -= extra_request; // try to put text space start where expected
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
// On Windows ARM64, STATIC_SPACE needs extra space before it for NIL header access.
|
||||
// ARM64 accesses NIL-0x1F for type checking, which must be within allocated memory.
|
||||
// This must be done even when size==0.
|
||||
if (space_id == STATIC_CORE_SPACE_ID) {
|
||||
uword_t alloc_size = (size == 0) ? BACKEND_PAGE_BYTES : size + BACKEND_PAGE_BYTES;
|
||||
addr = os_alloc_gc_space(space_id, attr, addr - BACKEND_PAGE_BYTES, alloc_size) + BACKEND_PAGE_BYTES;
|
||||
if (!addr) lose("Can't allocate %#"OBJ_FMTX" bytes for space %d", size, space_id);
|
||||
return addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (size == 0) return addr;
|
||||
|
||||
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
||||
|
|
@ -385,14 +424,28 @@ os_vm_address_t coreparse_alloc_space(int space_id, int attr,
|
|||
#ifdef LISP_FEATURE_IMMOBILE_SPACE
|
||||
if (space_id == IMMOBILE_TEXT_CORE_SPACE_ID) {
|
||||
ALIEN_LINKAGE_SPACE_START = (uword_t)addr;
|
||||
// TEXT_SPACE actually starts after ALIEN_LINKAGE_SPACE
|
||||
TEXT_SPACE_START = (uword_t)addr + extra_request;
|
||||
addr += extra_request;
|
||||
}
|
||||
#endif
|
||||
return addr;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Windows ARM64 (LLP64): unsigned long is 32-bit, and __uint128_t
|
||||
passing via registers differs from the Lisp alien-funcall ABI.
|
||||
Take hi/lo as separate 64-bit args to match the Lisp transform. */
|
||||
uint64_t sb_udivmodti4(uint64_t lo, uint64_t hi, uint64_t y, uint64_t *rem) {
|
||||
__uint128_t x = ((__uint128_t)hi << 64) | lo;
|
||||
if (rem)
|
||||
*rem = x % y;
|
||||
return x / y;
|
||||
}
|
||||
#else
|
||||
long sb_udivmodti4(__uint128_t x, unsigned long y, unsigned long *rem) {
|
||||
if (rem)
|
||||
*rem = x % y;
|
||||
return x / y;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -23,8 +23,16 @@
|
|||
ldr \dest, [\dest, _\symbol@PAGEOFF]
|
||||
.endm
|
||||
|
||||
#else
|
||||
#elif defined(LISP_FEATURE_WIN32)
|
||||
#define TYPE(name)
|
||||
#define SIZE(name)
|
||||
#define GNAME(var) var
|
||||
.macro LOAD_GNAME, dest, symbol
|
||||
adrp \dest, \symbol
|
||||
ldr \dest, [\dest, #:lo12:\symbol]
|
||||
.endm
|
||||
|
||||
#else
|
||||
#define TYPE(name) .type name,%function
|
||||
#define SIZE(name) .size name,.-name
|
||||
#define GNAME(var) var
|
||||
|
|
@ -153,6 +161,22 @@ GNAME(call_into_lisp):
|
|||
mov reg_LEXENV, x0
|
||||
|
||||
#ifdef LISP_FEATURE_SB_THREAD
|
||||
#if defined(LISP_FEATURE_WIN32)
|
||||
// On Windows ARM64, get thread pointer via TlsGetValue(OUR_TLS_INDEX)
|
||||
// Save arguments (x1, x2) and link register before C call
|
||||
stp x1, x2, [sp, #-32]!
|
||||
stp x29, x30, [sp, #16]
|
||||
|
||||
// Load OUR_TLS_INDEX (sbcl_thread_tls_index) and call TlsGetValue
|
||||
adrp x0, sbcl_thread_tls_index
|
||||
ldr w0, [x0, #:lo12:sbcl_thread_tls_index]
|
||||
bl TlsGetValue
|
||||
mov reg_THREAD, x0
|
||||
|
||||
// Restore arguments and link register
|
||||
ldp x29, x30, [sp, #16]
|
||||
ldp x1, x2, [sp], #32
|
||||
#else
|
||||
#ifdef LISP_FEATURE_GCC_TLS
|
||||
#ifdef LISP_FEATURE_DARWIN
|
||||
|
||||
|
|
@ -179,6 +203,7 @@ GNAME(call_into_lisp):
|
|||
bl pthread_getspecific
|
||||
mov reg_THREAD, x0
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
// Clear the boxed registers that don't already have something
|
||||
// in them.
|
||||
|
|
@ -190,7 +215,8 @@ GNAME(call_into_lisp):
|
|||
mov reg_R5, #0
|
||||
mov reg_R6, #0
|
||||
mov reg_R7, #0
|
||||
#ifndef LISP_FEATURE_DARWIN
|
||||
#if !defined(LISP_FEATURE_DARWIN) && !defined(LISP_FEATURE_WIN32)
|
||||
// x18 is reserved on Darwin (macOS) and Windows (TEB pointer)
|
||||
mov reg_R8, #0
|
||||
#endif
|
||||
mov reg_R10, #0
|
||||
|
|
@ -219,7 +245,10 @@ GNAME(call_into_lisp):
|
|||
1:
|
||||
|
||||
#endif
|
||||
// Windows uses control_stack_pointer as actual SP, not FFCA flag
|
||||
#ifndef LISP_FEATURE_WIN32
|
||||
str xzr,[reg_THREAD, THREAD_CONTROL_STACK_POINTER_OFFSET]
|
||||
#endif
|
||||
#else
|
||||
ldr reg_NL3, =GNAME(foreign_function_call_active)
|
||||
str xzr, [reg_NL3]
|
||||
|
|
@ -311,13 +340,21 @@ GNAME(call_into_c):
|
|||
// All other C arguments are already stashed on the C stack.
|
||||
|
||||
// Build a Lisp stack frame.
|
||||
#ifdef LISP_FEATURE_WIN32
|
||||
// On Windows ARM64, save thread CFP/CSP before overwriting.
|
||||
// CSP cannot be zeroed to indicate "in Lisp" state on safepoint builds.
|
||||
ldp x3, x4, [reg_THREAD, THREAD_CONTROL_FRAME_POINTER_OFFSET]
|
||||
stp x3, x4, [reg_CSP, #-16]!
|
||||
stp reg_CFP, reg_LR, [reg_CSP, #-16]!
|
||||
add reg_R10, reg_CSP, #32
|
||||
#else
|
||||
// Can store two values above the stack pointer, interrupts ignore them.
|
||||
stp reg_CFP, reg_LR, [reg_CSP]
|
||||
add reg_R10, reg_CSP, #2*8
|
||||
#endif
|
||||
mov reg_LEXENV, reg_LR
|
||||
|
||||
// Save the lisp stack and frame pointers.
|
||||
|
||||
#ifdef LISP_FEATURE_SB_THREAD
|
||||
stp reg_CSP, reg_R10, [reg_THREAD, THREAD_CONTROL_FRAME_POINTER_OFFSET]
|
||||
#else
|
||||
|
|
@ -359,7 +396,8 @@ GNAME(call_into_c):
|
|||
mov reg_R5, #0
|
||||
mov reg_R6, #0
|
||||
mov reg_R7, #0
|
||||
#ifndef LISP_FEATURE_DARWIN
|
||||
#if !defined(LISP_FEATURE_DARWIN) && !defined(LISP_FEATURE_WIN32)
|
||||
// x18 is reserved on Darwin (macOS) and Windows (TEB pointer)
|
||||
mov reg_R8,#0
|
||||
#endif
|
||||
#ifndef LISP_FEATURE_SB_THREAD
|
||||
|
|
@ -373,6 +411,12 @@ GNAME(call_into_c):
|
|||
|
||||
|
||||
// Restore the Lisp stack and frame pointers
|
||||
#ifdef LISP_FEATURE_WIN32
|
||||
// Restore CFP/LR from stack, then restore saved thread CFP/CSP.
|
||||
ldp reg_CFP, reg_LR, [reg_CSP], #16
|
||||
ldp x3, x4, [reg_CSP], #16
|
||||
stp x3, x4, [reg_THREAD, THREAD_CONTROL_FRAME_POINTER_OFFSET]
|
||||
#else
|
||||
#ifdef LISP_FEATURE_SB_THREAD
|
||||
str xzr, [reg_THREAD, THREAD_CONTROL_STACK_POINTER_OFFSET]
|
||||
#else
|
||||
|
|
@ -380,6 +424,7 @@ GNAME(call_into_c):
|
|||
str xzr, [reg_OCFP]
|
||||
#endif
|
||||
mov reg_LR, reg_LEXENV
|
||||
#endif
|
||||
ret
|
||||
|
||||
SIZE(call_into_c)
|
||||
|
|
|
|||
240
src/runtime/arm64-win32-os.c
Normal file
240
src/runtime/arm64-win32-os.c
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* The ARM64 Win32 incarnation of arch-dependent OS-dependent routines.
|
||||
* See also "win32-os.c".
|
||||
*/
|
||||
|
||||
/*
|
||||
* This software is part of the SBCL system. See the README file for
|
||||
* more information.
|
||||
*
|
||||
* This software is derived from the CMU CL system, which was
|
||||
* written at Carnegie Mellon University and released into the
|
||||
* public domain. The software is in the public domain and is
|
||||
* provided with absolutely no warranty. See the COPYING and CREDITS
|
||||
* files for more information.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h> // For memset
|
||||
#include <sys/param.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "os.h"
|
||||
#include "arch.h"
|
||||
#include "globals.h"
|
||||
#include "interrupt.h"
|
||||
#include "interr.h"
|
||||
#include "lispregs.h"
|
||||
#include "genesis/sbcl.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "runtime.h"
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include "thread.h" /* dynamic_values_bytes */
|
||||
#include "align.h"
|
||||
|
||||
#include "validate.h"
|
||||
|
||||
#include <windows.h> // For VirtualQuery, GetLastError, FlushInstructionCache
|
||||
|
||||
int arch_os_thread_init(struct thread *thread)
|
||||
{
|
||||
// On ARM64 Windows, we use the allocated Lisp control stack (from alloc_thread_struct),
|
||||
// not the OS thread's C stack. Unlike x86-64 which uses C_STACK_IS_CONTROL_STACK,
|
||||
// ARM64 has a separate Lisp control stack.
|
||||
// Therefore, we do NOT call VirtualQuery here to overwrite control_stack_start/end.
|
||||
// They have already been set correctly by alloc_thread_struct().
|
||||
|
||||
// CRITICAL: Set the TLS value so that get_sb_vm_thread() returns the correct thread!
|
||||
// Without this, funcall0-3 will get the wrong thread from TlsGetValue.
|
||||
extern DWORD OUR_TLS_INDEX;
|
||||
TlsSetValue(OUR_TLS_INDEX, thread);
|
||||
|
||||
extern void win32_set_stack_guarantee(void);
|
||||
win32_set_stack_guarantee();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* free any arch/os-specific resources used by thread, which is now
|
||||
* defunct. Not called on live threads
|
||||
*/
|
||||
int arch_os_thread_cleanup(struct thread *thread) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sigset_t *os_context_sigmask_addr(os_context_t *context)
|
||||
{
|
||||
return &context->sigmask;
|
||||
}
|
||||
|
||||
void visit_context_registers(void (*proc)(os_context_register_t,void*),
|
||||
os_context_t *context, void* arg)
|
||||
{
|
||||
// ARM64 general purpose registers X0-X30 and program counter Pc
|
||||
proc(context->win32_context->X0, arg);
|
||||
proc(context->win32_context->X1, arg);
|
||||
proc(context->win32_context->X2, arg);
|
||||
proc(context->win32_context->X3, arg);
|
||||
proc(context->win32_context->X4, arg);
|
||||
proc(context->win32_context->X5, arg);
|
||||
proc(context->win32_context->X6, arg);
|
||||
proc(context->win32_context->X7, arg);
|
||||
proc(context->win32_context->X8, arg);
|
||||
proc(context->win32_context->X9, arg);
|
||||
proc(context->win32_context->X10, arg);
|
||||
proc(context->win32_context->X11, arg);
|
||||
proc(context->win32_context->X12, arg);
|
||||
proc(context->win32_context->X13, arg);
|
||||
proc(context->win32_context->X14, arg);
|
||||
proc(context->win32_context->X15, arg);
|
||||
proc(context->win32_context->X16, arg);
|
||||
proc(context->win32_context->X17, arg);
|
||||
proc(context->win32_context->X18, arg);
|
||||
proc(context->win32_context->X19, arg);
|
||||
proc(context->win32_context->X20, arg);
|
||||
proc(context->win32_context->X21, arg);
|
||||
proc(context->win32_context->X22, arg);
|
||||
proc(context->win32_context->X23, arg);
|
||||
proc(context->win32_context->X24, arg);
|
||||
proc(context->win32_context->X25, arg);
|
||||
proc(context->win32_context->X26, arg);
|
||||
proc(context->win32_context->X27, arg);
|
||||
proc(context->win32_context->X28, arg);
|
||||
proc(context->win32_context->Fp, arg); // X29 Frame Pointer
|
||||
proc(context->win32_context->Lr, arg); // X30 Link Register
|
||||
proc(context->win32_context->Pc, arg); // Program Counter
|
||||
}
|
||||
|
||||
#include "lispregs.h" // This will pull in arm64-lispregs.h via target-lispregs.h
|
||||
|
||||
// ... (rest of the file remains the same until os_context_register_addr) ...
|
||||
|
||||
os_context_register_t *
|
||||
os_context_register_addr(os_context_t *context, int offset)
|
||||
{
|
||||
if (!context) {
|
||||
return NULL;
|
||||
}
|
||||
if (!context->win32_context) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Map physical ARM64 register number (0-31) to CONTEXT structure fields
|
||||
// The offset parameter is the physical register number from arm64-lispregs.h
|
||||
|
||||
switch (offset) {
|
||||
case 0: return (os_context_register_t*)&context->win32_context->X0;
|
||||
case 1: return (os_context_register_t*)&context->win32_context->X1;
|
||||
case 2: return (os_context_register_t*)&context->win32_context->X2;
|
||||
case 3: return (os_context_register_t*)&context->win32_context->X3;
|
||||
case 4: return (os_context_register_t*)&context->win32_context->X4;
|
||||
case 5: return (os_context_register_t*)&context->win32_context->X5;
|
||||
case 6: return (os_context_register_t*)&context->win32_context->X6;
|
||||
case 7: return (os_context_register_t*)&context->win32_context->X7;
|
||||
case 8: return (os_context_register_t*)&context->win32_context->X8;
|
||||
case 9: return (os_context_register_t*)&context->win32_context->X9;
|
||||
case 10: return (os_context_register_t*)&context->win32_context->X10;
|
||||
case 11: return (os_context_register_t*)&context->win32_context->X11;
|
||||
case 12: return (os_context_register_t*)&context->win32_context->X12;
|
||||
case 13: return (os_context_register_t*)&context->win32_context->X13;
|
||||
case 14: return (os_context_register_t*)&context->win32_context->X14;
|
||||
case 15: return (os_context_register_t*)&context->win32_context->X15;
|
||||
case 16: return (os_context_register_t*)&context->win32_context->X16;
|
||||
case 17: return (os_context_register_t*)&context->win32_context->X17;
|
||||
case 18: return (os_context_register_t*)&context->win32_context->X18;
|
||||
case 19: return (os_context_register_t*)&context->win32_context->X19;
|
||||
case 20: return (os_context_register_t*)&context->win32_context->X20;
|
||||
case 21: return (os_context_register_t*)&context->win32_context->X21;
|
||||
case 22: return (os_context_register_t*)&context->win32_context->X22;
|
||||
case 23: return (os_context_register_t*)&context->win32_context->X23;
|
||||
case 24: return (os_context_register_t*)&context->win32_context->X24;
|
||||
case 25: return (os_context_register_t*)&context->win32_context->X25;
|
||||
case 26: return (os_context_register_t*)&context->win32_context->X26;
|
||||
case 27: return (os_context_register_t*)&context->win32_context->X27;
|
||||
case 28: return (os_context_register_t*)&context->win32_context->X28;
|
||||
case 29: return (os_context_register_t*)&context->win32_context->Fp; // X29
|
||||
case 30: return (os_context_register_t*)&context->win32_context->Lr; // X30
|
||||
case 31: return (os_context_register_t*)&context->win32_context->Sp; // X31 (SP)
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
os_context_register_t *
|
||||
os_context_sp_addr(os_context_t *context)
|
||||
{
|
||||
return (os_context_register_t*)&context->win32_context->Sp;
|
||||
}
|
||||
|
||||
os_context_register_t *
|
||||
os_context_fp_addr(os_context_t *context)
|
||||
{
|
||||
return (os_context_register_t*)&context->win32_context->Fp;
|
||||
}
|
||||
|
||||
os_context_register_t *
|
||||
os_context_lr_addr(os_context_t *context)
|
||||
{
|
||||
return (os_context_register_t*)&context->win32_context->Lr;
|
||||
}
|
||||
|
||||
os_context_register_t *
|
||||
os_context_flags_addr(os_context_t *context)
|
||||
{
|
||||
// On ARM64 Windows, the CPSR is typically available as a separate field
|
||||
// within the CONTEXT structure or can be accessed via an appropriate
|
||||
// field for flags. Assuming `Cpsr` is the field name.
|
||||
return (os_context_register_t*)&context->win32_context->Cpsr;
|
||||
}
|
||||
|
||||
|
||||
unsigned long
|
||||
os_context_fp_control(os_context_t *context)
|
||||
{
|
||||
return context->win32_context->Fpsr | context->win32_context->Fpcr;
|
||||
}
|
||||
|
||||
void
|
||||
os_restore_fp_control(os_context_t *context)
|
||||
{
|
||||
/* No-op on ARM64, same as Linux and BSD. FPCR is preserved across exceptions. */
|
||||
}
|
||||
|
||||
os_context_register_t *
|
||||
os_context_float_register_addr(os_context_t *context, int offset)
|
||||
{
|
||||
if (!context) {
|
||||
return NULL;
|
||||
}
|
||||
if (!context->win32_context) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ARM64 Windows CONTEXT structure has V[0-31] array for SIMD/FP registers
|
||||
// Each V register is 128 bits (16 bytes)
|
||||
// The offset parameter is the physical register number (0-31)
|
||||
|
||||
if (offset >= 0 && offset < 32) {
|
||||
return (os_context_register_t*)&context->win32_context->V[offset];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
os_flush_icache(os_vm_address_t address, os_vm_size_t length)
|
||||
{
|
||||
// Use the Windows API function for flushing instruction cache.
|
||||
// The first argument is a process handle. For the current process, GetCurrentProcess() can be used.
|
||||
if (!FlushInstructionCache(GetCurrentProcess(), address, length)) {
|
||||
// Log an error or handle failure if necessary.
|
||||
fprintf(stderr, "FlushInstructionCache failed: 0x%lx.\n", GetLastError());
|
||||
}
|
||||
}
|
||||
26
src/runtime/arm64-win32-os.h
Normal file
26
src/runtime/arm64-win32-os.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef _ARM64_WIN32_OS_H
|
||||
#define _ARM64_WIN32_OS_H
|
||||
|
||||
typedef struct os_context_t {
|
||||
CONTEXT* win32_context;
|
||||
sigset_t sigmask;
|
||||
} os_context_t;
|
||||
|
||||
typedef intptr_t os_context_register_t;
|
||||
|
||||
static inline DWORD NT_GetLastError() {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
unsigned long os_context_fp_control(os_context_t *context);
|
||||
void os_restore_fp_control(os_context_t *context);
|
||||
os_context_register_t * os_context_fp_addr(os_context_t *context);
|
||||
os_context_register_t * os_context_sp_addr(os_context_t *context);
|
||||
os_context_register_t * os_context_lr_addr(os_context_t *context);
|
||||
os_context_register_t * os_context_flags_addr(os_context_t *context);
|
||||
os_context_register_t * os_context_register_addr(os_context_t *context, int offset);
|
||||
os_context_register_t * os_context_float_register_addr(os_context_t *context, int offset);
|
||||
|
||||
#define OS_CONTEXT_PC(context) context->win32_context->Pc
|
||||
|
||||
#endif /* _ARM64_WIN32_OS_H */
|
||||
|
|
@ -853,7 +853,9 @@ process_directory(int count, struct ndir_entry *entry,
|
|||
break;
|
||||
#endif
|
||||
case IMMOBILE_TEXT_CORE_SPACE_ID:
|
||||
#ifndef LISP_FEATURE_ARM64
|
||||
TEXT_SPACE_START = addr;
|
||||
#endif
|
||||
break;
|
||||
case DYNAMIC_CORE_SPACE_ID:
|
||||
{
|
||||
|
|
|
|||
|
|
@ -115,7 +115,14 @@ static void set_seh_frame(void *frame)
|
|||
|
||||
void alloc_gc_page()
|
||||
{
|
||||
#ifndef LISP_FEATURE_64_BIT // 64-bit uses the page below the card mark table
|
||||
#ifdef LISP_FEATURE_ARM64
|
||||
// ARM64: safepoint page address is reserved by the address space layout
|
||||
// (parms.lisp), but needs to be committed. Use MEM_COMMIT only.
|
||||
gc_assert(VirtualAlloc(GC_SAFEPOINT_PAGE_ADDR, BACKEND_PAGE_BYTES,
|
||||
MEM_COMMIT, PAGE_READWRITE));
|
||||
#elif !defined(LISP_FEATURE_64_BIT)
|
||||
// 32-bit: reserve and commit safepoint page.
|
||||
// 64-bit x86-64: allocated as part of static space (extra_above in x86-64-arch.c).
|
||||
gc_assert(VirtualAlloc(GC_SAFEPOINT_PAGE_ADDR, BACKEND_PAGE_BYTES,
|
||||
MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE));
|
||||
#endif
|
||||
|
|
@ -144,16 +151,20 @@ void alloc_gc_page()
|
|||
*/
|
||||
void map_gc_page()
|
||||
{
|
||||
#if defined(LISP_FEATURE_SB_SAFEPOINT)
|
||||
DWORD oldProt;
|
||||
gc_assert(VirtualProtect((void*) GC_SAFEPOINT_PAGE_ADDR, BACKEND_PAGE_BYTES,
|
||||
PAGE_READWRITE, &oldProt));
|
||||
#endif
|
||||
}
|
||||
|
||||
void unmap_gc_page()
|
||||
{
|
||||
#if defined(LISP_FEATURE_SB_SAFEPOINT)
|
||||
DWORD oldProt;
|
||||
gc_assert(VirtualProtect((void*) GC_SAFEPOINT_PAGE_ADDR, BACKEND_PAGE_BYTES,
|
||||
PAGE_NOACCESS, &oldProt));
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t os_get_build_time_shared_libraries(uint32_t excl_maximum,
|
||||
|
|
@ -735,7 +746,6 @@ void
|
|||
os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot)
|
||||
{
|
||||
DWORD old_prot;
|
||||
|
||||
DWORD new_prot = os_protect_modes[prot];
|
||||
gc_assert(VirtualProtect(address, length, new_prot, &old_prot)||
|
||||
(VirtualAlloc(address, length, MEM_COMMIT, new_prot) &&
|
||||
|
|
@ -750,16 +760,19 @@ extern int internal_errors_enabled;
|
|||
|
||||
extern void exception_handler_wrapper();
|
||||
|
||||
#ifdef LISP_FEATURE_X86
|
||||
#if defined(LISP_FEATURE_X86)
|
||||
#define voidreg(ctxptr,name) ((void*)((ctxptr)->E##name))
|
||||
#else
|
||||
#elif defined(LISP_FEATURE_X86_64)
|
||||
#define voidreg(ctxptr,name) ((void*)((ctxptr)->R##name))
|
||||
#else // ARM64 and others. Stub out for now.
|
||||
#define voidreg(ctxptr,name) (0)
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
handle_single_step(os_context_t *ctx)
|
||||
{
|
||||
#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
|
||||
if (!single_stepping)
|
||||
return -1;
|
||||
|
||||
|
|
@ -768,19 +781,48 @@ handle_single_step(os_context_t *ctx)
|
|||
restore_breakpoint_from_single_step(ctx);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef LISP_FEATURE_UD2_BREAKPOINTS
|
||||
#define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_ILLEGAL_INSTRUCTION
|
||||
#define TRAP_CODE_WIDTH 2
|
||||
#else
|
||||
#define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_BREAKPOINT
|
||||
#define TRAP_CODE_WIDTH 1
|
||||
#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
|
||||
#ifdef LISP_FEATURE_UD2_BREAKPOINTS
|
||||
#define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_ILLEGAL_INSTRUCTION
|
||||
#define TRAP_CODE_WIDTH 2
|
||||
#else
|
||||
#define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_BREAKPOINT
|
||||
#define TRAP_CODE_WIDTH 1
|
||||
#endif
|
||||
#elif defined(LISP_FEATURE_ARM64)
|
||||
// On Windows ARM64, BRK instructions trigger EXCEPTION_ILLEGAL_INSTRUCTION, not EXCEPTION_BREAKPOINT
|
||||
#define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_ILLEGAL_INSTRUCTION
|
||||
#define TRAP_CODE_WIDTH 4 // ARM breakpoint instruction is 4 bytes
|
||||
#endif
|
||||
|
||||
static int
|
||||
handle_breakpoint_trap(os_context_t *ctx, struct thread* self)
|
||||
{
|
||||
#if defined(LISP_FEATURE_ARM64)
|
||||
uint32_t trap_instruction = *(uint32_t *)OS_CONTEXT_PC(ctx);
|
||||
unsigned int trap;
|
||||
|
||||
// Check for BRK instruction format used by SBCL. The high bits are
|
||||
// 11010100001, which is 0x6a1.
|
||||
if ((trap_instruction >> 21) == 0x6a1) {
|
||||
// Extract 8-bit trap code from bits 5..12 (matching sigtrap_handler on Linux)
|
||||
trap = (trap_instruction >> 5) & 0xFF;
|
||||
} else {
|
||||
// Not a recognized SBCL trap, could be a debugger breakpoint
|
||||
// or a real illegal instruction. Let other handlers deal with it.
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* On ARM64, do NOT advance PC past the BRK instruction here.
|
||||
* The Lisp-side internal-error-args reads the BRK instruction from
|
||||
* the context PC to decode error number and arguments.
|
||||
* PC will be advanced by arch_skip_instruction after error handling. */
|
||||
#else /* Not ARM64 */
|
||||
#ifdef LISP_FEATURE_UD2_BREAKPOINTS
|
||||
if (((unsigned short *)OS_CONTEXT_PC(ctx))[0] != 0x0b0f)
|
||||
return -1;
|
||||
|
|
@ -793,14 +835,14 @@ handle_breakpoint_trap(os_context_t *ctx, struct thread* self)
|
|||
/* Now EIP points just after the INT3 byte and aims at the
|
||||
* 'kind' value (eg trap_Cerror). */
|
||||
unsigned trap = *(unsigned char *)OS_CONTEXT_PC(ctx);
|
||||
#endif
|
||||
|
||||
/* Before any other trap handler: gc_safepoint ensures that
|
||||
inner alloc_sap for passing the context won't trap on
|
||||
pseudo-atomic. */
|
||||
/* Now that there is no alloc_sap, I don't know what happens here. */
|
||||
if (trap == trap_PendingInterrupt) {
|
||||
/* Done everything needed for this trap, except EIP
|
||||
adjustment */
|
||||
/* Advance PC past the trap instruction and any trailing data. */
|
||||
arch_skip_instruction(ctx);
|
||||
thread_interrupted(ctx);
|
||||
return 0;
|
||||
|
|
@ -840,9 +882,9 @@ handle_access_violation(os_context_t *ctx,
|
|||
win32_context->Edi,
|
||||
fault_address,
|
||||
exception_record->ExceptionInformation[0]);
|
||||
#else
|
||||
#elif defined(LISP_FEATURE_X86_64)
|
||||
odxprint(pagefaults,
|
||||
"SEGV. ThSap %p, Eip %p, Esp %p, Esi %p, Edi %p, "
|
||||
"SEGV. ThSap %p, Rip %p, Rsp %p, Rsi %p, Rdi %p, "
|
||||
"Addr %p Access %d\n",
|
||||
self,
|
||||
win32_context->Rip,
|
||||
|
|
@ -851,11 +893,42 @@ handle_access_violation(os_context_t *ctx,
|
|||
win32_context->Rdi,
|
||||
fault_address,
|
||||
exception_record->ExceptionInformation[0]);
|
||||
#elif defined(LISP_FEATURE_ARM64)
|
||||
odxprint(pagefaults,
|
||||
"SEGV. ThSap %p, Pc %p, Sp %p, "
|
||||
"Addr %p Access %d\n",
|
||||
self,
|
||||
win32_context->Pc,
|
||||
win32_context->Sp,
|
||||
fault_address,
|
||||
exception_record->ExceptionInformation[0]);
|
||||
#endif
|
||||
|
||||
/* Safepoint pages */
|
||||
if (fault_address == (void *) GC_SAFEPOINT_TRAP_ADDR) {
|
||||
#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
|
||||
/* x86/x86-64: set_csp_from_context arranges for the conservative
|
||||
* stack scan to cover the CONTEXT, so register values are found. */
|
||||
thread_in_lisp_raised(ctx);
|
||||
#else
|
||||
/* ARM64: separate control and C stacks. The register context must
|
||||
* be explicitly saved so that GC can scan Lisp register values.
|
||||
* (Mirrors handle_safepoint_violation in safepoint.c for Unix.) */
|
||||
fake_foreign_function_call(ctx);
|
||||
thread_in_lisp_raised(ctx);
|
||||
undo_fake_foreign_function_call(ctx);
|
||||
/* Scrub stale Lisp frames left on the control stack by SUB-GC.
|
||||
* undo_fake_foreign_function_call zeroed thread->csp (it IS
|
||||
* foreign_function_call_active_p on ARM64), so temporarily
|
||||
* restore it from the interrupt context for scrubbing. */
|
||||
{
|
||||
lispobj *csp = (lispobj*)(uword_t)
|
||||
(*os_context_register_addr(ctx, reg_CSP));
|
||||
access_control_stack_pointer(self) = csp;
|
||||
scrub_thread_control_stack(self);
|
||||
access_control_stack_pointer(self) = 0;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -867,7 +940,15 @@ handle_access_violation(os_context_t *ctx,
|
|||
/* dynamic space */
|
||||
page_index_t page = find_page_index(fault_address);
|
||||
#ifdef LISP_FEATURE_SOFT_CARD_MARKS
|
||||
if (page >= 0) lose("should not get access violation in dynamic space %p", fault_address);
|
||||
if (page >= 0) {
|
||||
/* With soft card marks, pages are never write-protected, so a
|
||||
* dynamic space access violation means the page needs committing.
|
||||
* (Windows reserves the full dynamic space but only commits pages
|
||||
* on demand or via prepare_pages/gc_alloc_large.) */
|
||||
os_commit_memory(PTR_ALIGN_DOWN(fault_address, os_vm_page_size),
|
||||
os_vm_page_size);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
if (page != -1 && !PAGE_WRITEPROTECTED_P(page)) {
|
||||
os_commit_memory(PTR_ALIGN_DOWN(fault_address, os_vm_page_size),
|
||||
|
|
@ -913,7 +994,9 @@ signal_internal_error_or_lose(os_context_t *ctx,
|
|||
/* The exception system doesn't automatically clear pending
|
||||
* exceptions, so we lose as soon as we execute any FP
|
||||
* instruction unless we do this first. */
|
||||
#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
|
||||
asm("fnclex");
|
||||
#endif
|
||||
/* We're making the somewhat arbitrary decision that having
|
||||
* internal errors enabled means that lisp has sufficient
|
||||
* marbles to be able to handle exceptions, but exceptions
|
||||
|
|
@ -928,7 +1011,7 @@ signal_internal_error_or_lose(os_context_t *ctx,
|
|||
DX_ALLOC_SAP(exception_record_sap, exception_record);
|
||||
thread_sigmask(SIG_SETMASK, &ctx->sigmask, NULL);
|
||||
|
||||
#ifdef LISP_FEATURE_X86_64
|
||||
#if defined(LISP_FEATURE_X86_64)
|
||||
asm("fninit");
|
||||
#endif
|
||||
|
||||
|
|
@ -1003,6 +1086,7 @@ handle_exception_ex(EXCEPTION_RECORD *exception_record,
|
|||
/* For EXCEPTION_ACCESS_VIOLATION only. */
|
||||
void *fault_address = (void *)exception_record->ExceptionInformation[1];
|
||||
|
||||
#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
|
||||
odxprint(seh,
|
||||
"SEH: rec %p, ctxptr %p, rip %p, fault %p\n"
|
||||
"... code %p, rcx %p, fp-tags %p\n\n",
|
||||
|
|
@ -1013,6 +1097,15 @@ handle_exception_ex(EXCEPTION_RECORD *exception_record,
|
|||
(void*)(intptr_t)code,
|
||||
voidreg(win32_context,cx),
|
||||
win32_context->FloatSave.TagWord);
|
||||
#else // ARM64
|
||||
odxprint(seh,
|
||||
"SEH: rec %p, ctxptr %p, pc %p, fault %p, code %p\n\n",
|
||||
exception_record,
|
||||
win32_context,
|
||||
(void*)win32_context->Pc,
|
||||
fault_address,
|
||||
(void*)(intptr_t)code);
|
||||
#endif
|
||||
|
||||
/* This function had become unwieldy. Let's cut it down into
|
||||
* pieces based on the different exception codes. Each exception
|
||||
|
|
@ -1093,7 +1186,7 @@ handle_exception(EXCEPTION_RECORD *exception_record,
|
|||
return handle_exception_ex(exception_record, exception_frame, win32_context, FALSE);
|
||||
}
|
||||
|
||||
#ifdef LISP_FEATURE_X86_64
|
||||
#if defined(LISP_FEATURE_X86_64) || defined(LISP_FEATURE_ARM64)
|
||||
|
||||
#define RESTORING_ERRNO() \
|
||||
int sbcl__lastErrno = errno; \
|
||||
|
|
@ -1119,7 +1212,11 @@ veh(EXCEPTION_POINTERS *ep)
|
|||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
#if defined(LISP_FEATURE_X86_64)
|
||||
DWORD64 rip = ep->ContextRecord->Rip;
|
||||
#elif defined(LISP_FEATURE_ARM64)
|
||||
DWORD64 rip = ep->ContextRecord->Pc;
|
||||
#endif
|
||||
long int code = ep->ExceptionRecord->ExceptionCode;
|
||||
BOOL from_lisp =
|
||||
(rip >= DYNAMIC_SPACE_START && rip < DYNAMIC_SPACE_START+dynamic_space_size) ||
|
||||
|
|
@ -1166,7 +1263,7 @@ wos_install_interrupt_handlers
|
|||
handler->next_frame = get_seh_frame();
|
||||
handler->handler = (void*)exception_handler_wrapper;
|
||||
set_seh_frame(handler);
|
||||
#else
|
||||
#elif defined(LISP_FEATURE_X86_64) || defined(LISP_FEATURE_ARM64)
|
||||
static int once = 0;
|
||||
if (!once++)
|
||||
AddVectoredExceptionHandler(1,veh);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,12 @@ set -a # export all variables at assignment-time.
|
|||
# quote them (with double quotes), to contend with whitespace.
|
||||
SBCL_HOME="${TEST_SBCL_HOME:-$SBCL_PWD/../obj/sbcl-home}"
|
||||
SBCL_CORE="${TEST_SBCL_CORE:-$SBCL_PWD/../output/sbcl.core}"
|
||||
SBCL_RUNTIME="${TEST_SBCL_RUNTIME:-$SBCL_PWD/../src/runtime/sbcl}"
|
||||
# On Windows, the runtime is sbcl.exe; on Unix, it's sbcl
|
||||
if [ -f "$SBCL_PWD/../src/runtime/sbcl.exe" ]; then
|
||||
SBCL_RUNTIME="${TEST_SBCL_RUNTIME:-$SBCL_PWD/../src/runtime/sbcl.exe}"
|
||||
else
|
||||
SBCL_RUNTIME="${TEST_SBCL_RUNTIME:-$SBCL_PWD/../src/runtime/sbcl}"
|
||||
fi
|
||||
SBCL_ARGS="${TEST_SBCL_ARGS:---disable-ldb --noinform --no-sysinit --no-userinit --noprint --disable-debugger}"
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue