Signal handling in native code without the page table (#9682)

Signal handlers sometimes need to know whether the signal occurred
in ocamlopt-generated code, as opposed to runtime or C library code.

Today this determination uses a page table lookup to keep track
of dynamically-loaded modules, plus ad-hoc tests for the main program.

This PR uses the code fragment table instead.  That's more reliable,
less ad-hoc, and independent of the page table.

i386nt.asm: add caml_system__code_{begin,end}, ,like in the other ports.
master
Xavier Leroy 2020-06-15 14:17:42 +02:00 committed by GitHub
parent 603506aa34
commit e4bf109d1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 22 additions and 29 deletions

View File

@ -26,9 +26,14 @@
EXTERN _caml_stash_backtrace: PROC EXTERN _caml_stash_backtrace: PROC
EXTERN _Caml_state: DWORD EXTERN _Caml_state: DWORD
.CODE
PUBLIC _caml_system__code_begin
_caml_system__code_begin:
ret ; just one instruction, so that debuggers don't display
; caml_system__code_begin instead of caml_call_gc
; Allocation ; Allocation
.CODE
PUBLIC _caml_call_gc PUBLIC _caml_call_gc
PUBLIC _caml_alloc1 PUBLIC _caml_alloc1
PUBLIC _caml_alloc2 PUBLIC _caml_alloc2
@ -292,6 +297,9 @@ _caml_ml_array_bound_error:
mov eax, offset _caml_array_bound_error mov eax, offset _caml_array_bound_error
jmp _caml_c_call jmp _caml_c_call
PUBLIC _caml_system__code_end
_caml_system__code_end:
.DATA .DATA
PUBLIC _caml_system__frametable PUBLIC _caml_system__frametable
_caml_system__frametable LABEL DWORD _caml_system__frametable LABEL DWORD

View File

@ -26,6 +26,7 @@
#include <signal.h> #include <signal.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include "caml/codefrag.h"
#include "caml/fail.h" #include "caml/fail.h"
#include "caml/memory.h" #include "caml/memory.h"
#include "caml/osdeps.h" #include "caml/osdeps.h"
@ -49,18 +50,6 @@ extern signal_handler caml_win32_signal(int sig, signal_handler action);
extern void caml_win32_overflow_detection(); extern void caml_win32_overflow_detection();
#endif #endif
extern char * caml_code_area_start, * caml_code_area_end;
extern char caml_system__code_begin, caml_system__code_end;
/* Do not use the macro from address_class.h here. */
#undef Is_in_code_area
#define Is_in_code_area(pc) \
( ((char *)(pc) >= caml_code_area_start && \
(char *)(pc) <= caml_code_area_end) \
|| ((char *)(pc) >= &caml_system__code_begin && \
(char *)(pc) <= &caml_system__code_end) \
|| (Classify_addr(pc) & In_code_area) )
/* This routine is the common entry point for garbage collection /* This routine is the common entry point for garbage collection
and signal handling. It can trigger a callback to OCaml code. and signal handling. It can trigger a callback to OCaml code.
With system threads, this callback can cause a context switch. With system threads, this callback can cause a context switch.
@ -119,7 +108,7 @@ DECLARE_SIGNAL_HANDLER(handle_signal)
Use the signal context to modify that register too, but only if Use the signal context to modify that register too, but only if
we are inside OCaml code (not inside C code). */ we are inside OCaml code (not inside C code). */
#if defined(CONTEXT_PC) && defined(CONTEXT_YOUNG_LIMIT) #if defined(CONTEXT_PC) && defined(CONTEXT_YOUNG_LIMIT)
if (Is_in_code_area(CONTEXT_PC)) if (caml_find_code_fragment_by_pc((char *) CONTEXT_PC) != NULL)
CONTEXT_YOUNG_LIMIT = (context_reg) Caml_state->young_limit; CONTEXT_YOUNG_LIMIT = (context_reg) Caml_state->young_limit;
#endif #endif
} }
@ -226,7 +215,7 @@ DECLARE_SIGNAL_HANDLER(segv_handler)
&& fault_addr < Caml_state->top_of_stack && fault_addr < Caml_state->top_of_stack
&& (uintnat)fault_addr >= CONTEXT_SP - EXTRA_STACK && (uintnat)fault_addr >= CONTEXT_SP - EXTRA_STACK
#ifdef CONTEXT_PC #ifdef CONTEXT_PC
&& Is_in_code_area(CONTEXT_PC) && caml_find_code_fragment_by_pc((char *) CONTEXT_PC) != NULL
#endif #endif
) { ) {
#ifdef RETURN_AFTER_STACK_OVERFLOW #ifdef RETURN_AFTER_STACK_OVERFLOW

View File

@ -48,6 +48,7 @@
extern int caml_parser_trace; extern int caml_parser_trace;
char * caml_code_area_start, * caml_code_area_end; char * caml_code_area_start, * caml_code_area_end;
extern char caml_system__code_begin, caml_system__code_end;
/* Initialize the atom table and the static data and code area limits. */ /* Initialize the atom table and the static data and code area limits. */
@ -81,6 +82,10 @@ static void init_static(void)
caml_register_code_fragment(caml_code_area_start, caml_register_code_fragment(caml_code_area_start,
caml_code_area_end, caml_code_area_end,
DIGEST_LATER, NULL); DIGEST_LATER, NULL);
/* Also register the glue code written in assembly */
caml_register_code_fragment(&caml_system__code_begin,
&caml_system__code_end,
DIGEST_IGNORE, NULL);
} }
/* These are termination hooks used by the systhreads library */ /* These are termination hooks used by the systhreads library */

View File

@ -38,7 +38,7 @@
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include "caml/alloc.h" #include "caml/alloc.h"
#include "caml/address_class.h" #include "caml/codefrag.h"
#include "caml/fail.h" #include "caml/fail.h"
#include "caml/io.h" #include "caml/io.h"
#include "caml/memory.h" #include "caml/memory.h"
@ -539,7 +539,8 @@ static LONG CALLBACK
DWORD *ctx_ip = &(ctx->Eip); DWORD *ctx_ip = &(ctx->Eip);
DWORD *ctx_sp = &(ctx->Esp); DWORD *ctx_sp = &(ctx->Esp);
if (code == EXCEPTION_STACK_OVERFLOW && Is_in_code_area (*ctx_ip)) if (code == EXCEPTION_STACK_OVERFLOW &&
caml_find_code_fragment_by_pc((char *) (*ctx_ip)) != NULL)
{ {
uintnat faulting_address; uintnat faulting_address;
uintnat * alt_esp; uintnat * alt_esp;
@ -561,24 +562,14 @@ static LONG CALLBACK
#else #else
/* Do not use the macro from address_class.h here. */
#undef Is_in_code_area
#define Is_in_code_area(pc) \
( ((char *)(pc) >= caml_code_area_start && \
(char *)(pc) <= caml_code_area_end) \
|| ((char *)(pc) >= &caml_system__code_begin && \
(char *)(pc) <= &caml_system__code_end) \
|| (Classify_addr(pc) & In_code_area) )
extern char caml_system__code_begin, caml_system__code_end;
static LONG CALLBACK static LONG CALLBACK
caml_stack_overflow_VEH (EXCEPTION_POINTERS* exn_info) caml_stack_overflow_VEH (EXCEPTION_POINTERS* exn_info)
{ {
DWORD code = exn_info->ExceptionRecord->ExceptionCode; DWORD code = exn_info->ExceptionRecord->ExceptionCode;
CONTEXT *ctx = exn_info->ContextRecord; CONTEXT *ctx = exn_info->ContextRecord;
if (code == EXCEPTION_STACK_OVERFLOW && Is_in_code_area (ctx->Rip)) if (code == EXCEPTION_STACK_OVERFLOW &&
caml_find_code_fragment_by_pc((char *) (ctx->Rip)) != NULL)
{ {
uintnat faulting_address; uintnat faulting_address;
uintnat * alt_rsp; uintnat * alt_rsp;