mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-24 12:42:32 +00:00
[util] Add Sys_setjmp and Sys_longjmp
Host_Error and Host_EndGame use setjmp/longjmp to implement an exception of sorts, but this messes with tracy's state even with cleanup attributes. However, it turns out that those cleanup attributes are exactly how gcc implements C++ destructors, and so the standard Unwind api (part of libgcc) respects them (so long as -fexceptions is enabled for C). Thus... replace longjmp with an implementation that uses Unwind to unwind the stack and call the cleanup functions as needed. This is actually important for more than just tracy as the cleanup attributed vars can be thread locks.
This commit is contained in:
parent
52210b8c55
commit
1a83fe21c1
7 changed files with 184 additions and 18 deletions
|
@ -25,7 +25,7 @@ NOCONV_DIST= \
|
|||
$(distdir)/include/win32/resources/icon1XP.ico
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
AM_CFLAGS= $(TRACY_CFLAGS) -funwind-tables -include qftracy.h
|
||||
AM_CFLAGS= $(TRACY_CFLAGS) -fexceptions -include qftracy.h
|
||||
AM_CXXFLAGS= $(TRACY_CFLAGS) -include qftracy.h
|
||||
AM_CPPFLAGS= -I$(top_srcdir)/include $(UNWIND_CFLAGS) $(PTHREAD_CFLAGS) $(FNM_FLAGS) $(NCURSES_CFLAGS) $(FREETYPE_CFLAGS) $(HARFBUZZ_CFLAGS) $(VULKAN_CPPFLAGS) $(LIBCURL_CFLAGS)
|
||||
|
||||
|
|
|
@ -202,6 +202,10 @@ char *Sys_ExpandSquiggle (const char *path);
|
|||
int Sys_UniqueFile (struct dstring_s *name, const char *prefix,
|
||||
const char *suffix, int mindigits);
|
||||
|
||||
typedef intptr_t sys_jmpbuf[5];
|
||||
#define Sys_setjmp(jmpbuf) __builtin_setjmp(jmpbuf)
|
||||
void Sys_longjmp (sys_jmpbuf jmpbuf) __attribute__((noreturn));
|
||||
|
||||
///@}
|
||||
|
||||
#endif//__QF_sys_h
|
||||
|
|
169
libs/util/sys.c
169
libs/util/sys.c
|
@ -73,6 +73,7 @@
|
|||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <unwind.h>
|
||||
|
||||
#ifdef HAVE_DIRECT_H
|
||||
#include <direct.h>
|
||||
|
@ -85,6 +86,7 @@
|
|||
#include "QF/cvar.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/mathlib.h"
|
||||
#include "QF/msg.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/quakefs.h"
|
||||
#include "QF/va.h"
|
||||
|
@ -1261,3 +1263,170 @@ Sys_Shutdown (void)
|
|||
sys_debuglog_data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
#define DW_EH_PE_absptr 0x00
|
||||
#define DW_EH_PE_omit 0xff
|
||||
|
||||
#define DW_EH_PE_uleb128 0x01
|
||||
#define DW_EH_PE_udata2 0x02
|
||||
#define DW_EH_PE_udata4 0x03
|
||||
#define DW_EH_PE_udata8 0x04
|
||||
#define DW_EH_PE_sleb128 0x09
|
||||
#define DW_EH_PE_sdata2 0x0A
|
||||
#define DW_EH_PE_sdata4 0x0B
|
||||
#define DW_EH_PE_sdata8 0x0C
|
||||
#define DW_EH_PE_signed 0x08
|
||||
|
||||
#define DW_EH_PE_pcrel 0x10
|
||||
#define DW_EH_PE_textrel 0x20
|
||||
#define DW_EH_PE_datarel 0x30
|
||||
#define DW_EH_PE_funcrel 0x40
|
||||
#define DW_EH_PE_aligned 0x50
|
||||
|
||||
|
||||
typedef struct {
|
||||
uintptr_t start;
|
||||
uintptr_t lpstart;
|
||||
uintptr_t ttype_base;
|
||||
const byte *ttype;
|
||||
const byte *action_table;
|
||||
byte ttype_encoding;
|
||||
byte cs_encoding;
|
||||
} lsd_header_t;
|
||||
|
||||
static uintptr_t
|
||||
read_value (byte enc, qmsg_t *msg)
|
||||
{
|
||||
if (enc == DW_EH_PE_aligned) {
|
||||
Sys_Error ("unexpected DW_EH_PE_aligned\n");
|
||||
}
|
||||
uintptr_t res = 0;
|
||||
switch (enc & 0x0f) {
|
||||
case DW_EH_PE_uleb128:
|
||||
res = MSG_ReadUleb128 (msg);
|
||||
break;
|
||||
case DW_EH_PE_sleb128:
|
||||
res = MSG_ReadSleb128 (msg);
|
||||
break;
|
||||
default:
|
||||
Sys_Error ("unexpected dwarf encoding: %d\n", enc);
|
||||
break;
|
||||
}
|
||||
if (res && enc & 0xf0) {
|
||||
Sys_Error ("unexpected dwarf encoding: %d\n", enc);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static lsd_header_t
|
||||
read_lsd_header (qmsg_t *msg, uintptr_t start)
|
||||
{
|
||||
lsd_header_t hdr;
|
||||
uintptr_t tmp;
|
||||
byte enc = MSG_ReadByte (msg);
|
||||
hdr.start = start;
|
||||
if (enc == DW_EH_PE_omit) {
|
||||
hdr.lpstart = start;
|
||||
} else {
|
||||
hdr.lpstart = read_value (enc, msg);
|
||||
}
|
||||
hdr.ttype_encoding = MSG_ReadByte (msg);
|
||||
if (hdr.ttype_encoding == DW_EH_PE_omit) {
|
||||
hdr.ttype = 0;
|
||||
} else {
|
||||
tmp = MSG_ReadUleb128 (msg);
|
||||
hdr.ttype = msg->message->data + msg->readcount + tmp;
|
||||
}
|
||||
hdr.cs_encoding = MSG_ReadByte (msg);;
|
||||
tmp = MSG_ReadUleb128 (msg);
|
||||
hdr.action_table = msg->message->data + msg->readcount + tmp;
|
||||
return hdr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
sys_stop (int version, _Unwind_Action actions,
|
||||
_Unwind_Exception_Class excls,
|
||||
struct _Unwind_Exception *exobj,
|
||||
struct _Unwind_Context *context, void *_jmpbuf)
|
||||
{
|
||||
if (version != 1) {
|
||||
Sys_Error ("unknown unwind version");
|
||||
}
|
||||
intptr_t *jmpbuf = _jmpbuf;
|
||||
#ifdef _WIN32
|
||||
if (!context) {
|
||||
// if we get here, then attempting to delete the exception contextuuu
|
||||
// can fail
|
||||
__builtin_longjmp (jmpbuf, 1);
|
||||
}
|
||||
uintptr_t target_cfa = jmpbuf[2];
|
||||
uintptr_t cfa = _Unwind_GetCFA (context);
|
||||
if (target_cfa < cfa) {
|
||||
// we've gone past the target frame (there were no intervening
|
||||
// cleanup points) so just jump out to avoid cleaning up something
|
||||
// we shouldn't touch
|
||||
__builtin_longjmp (jmpbuf, 1);
|
||||
}
|
||||
// These are not meant to be read, let alone written, but this is
|
||||
// the only way to communicate the target frame/ip to RtlUnwindEx
|
||||
exobj->private_[1] = target_cfa;
|
||||
exobj->private_[2] = jmpbuf[1]; // target ip
|
||||
return _URC_NO_REASON;
|
||||
#else
|
||||
if ((actions & _UA_END_OF_STACK)) {
|
||||
Sys_Error ("Sys_longjmp called outside a Sys_setjmp context");
|
||||
}
|
||||
uintptr_t target_cfa = jmpbuf[2];
|
||||
uintptr_t cfa = _Unwind_GetCFA (context);
|
||||
int no_dec_ip;
|
||||
uintptr_t ip = _Unwind_GetIPInfo (context, &no_dec_ip);
|
||||
if (!ip) {
|
||||
Sys_Error ("null ip\n");
|
||||
}
|
||||
if (cfa != target_cfa) {
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
ip -= !no_dec_ip;
|
||||
byte *lsd = _Unwind_GetLanguageSpecificData (context);
|
||||
if (lsd) {
|
||||
sizebuf_t message = { .data = lsd, .cursize = -1 };
|
||||
qmsg_t msg = { .message = &message };
|
||||
auto region_start = _Unwind_GetRegionStart (context);
|
||||
lsd_header_t hdr = read_lsd_header (&msg, region_start);
|
||||
message.cursize = hdr.action_table - message.data;
|
||||
bool match = false;
|
||||
bool cleanup = false;
|
||||
while (!match && msg.readcount < message.cursize) {
|
||||
auto cs_start = read_value (hdr.cs_encoding, &msg);
|
||||
auto cs_len = read_value (hdr.cs_encoding, &msg);
|
||||
auto cs_lp = read_value (hdr.cs_encoding, &msg);
|
||||
/*auto cs_action =*/ read_value (hdr.cs_encoding, &msg);
|
||||
cs_start += hdr.start;
|
||||
match = ip >= cs_start && ip < cs_start + cs_len;
|
||||
cleanup = cs_lp;
|
||||
}
|
||||
if (!match) {
|
||||
Sys_Error ("could not find ip in call site list");
|
||||
}
|
||||
if (cleanup) {
|
||||
// the call site has cleanup associated with it, so return to
|
||||
// let the unwind system deal with the cleanup (we'll get called
|
||||
// again with the same cfa)
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
}
|
||||
_Unwind_DeleteException (exobj);
|
||||
__builtin_longjmp (jmpbuf, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Sys_longjmp (sys_jmpbuf jmpbuf)
|
||||
{
|
||||
static struct _Unwind_Exception sys_exception;
|
||||
auto res = _Unwind_ForcedUnwind (&sys_exception, sys_stop, jmpbuf);
|
||||
Sys_Error ("_Unwind_ForcedUnwind returned %d", res);
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
#ifndef __server_h
|
||||
#define __server_h
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "QF/info.h"
|
||||
#include "QF/model.h"
|
||||
#include "QF/quakeio.h"
|
||||
|
@ -232,8 +230,6 @@ extern server_t sv; // local server
|
|||
|
||||
extern client_t *host_client;
|
||||
|
||||
extern jmp_buf host_abortserver;
|
||||
|
||||
extern double host_time;
|
||||
extern double sv_frametime;
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ size_t minimum_memory;
|
|||
|
||||
client_t *host_client; // current client
|
||||
|
||||
jmp_buf host_abortserver;
|
||||
static sys_jmpbuf host_abortserver;
|
||||
|
||||
float host_mem_size;
|
||||
static cvar_t host_mem_size_cvar = {
|
||||
|
@ -286,7 +286,7 @@ Host_EndGame (const char *message, ...)
|
|||
else
|
||||
CL_Disconnect ();
|
||||
|
||||
longjmp (host_abortserver, 1);
|
||||
Sys_longjmp (host_abortserver);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -328,7 +328,7 @@ Host_Error (const char *error, ...)
|
|||
|
||||
inerror = false;
|
||||
|
||||
longjmp (host_abortserver, 1);
|
||||
Sys_longjmp (host_abortserver);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -678,14 +678,14 @@ Host_FilterTime (float time)
|
|||
static void
|
||||
_Host_Frame (float time)
|
||||
{
|
||||
qfZoneScoped (true);
|
||||
static int first = 1;
|
||||
float sleeptime;
|
||||
|
||||
if (setjmp (host_abortserver))
|
||||
if (Sys_setjmp (host_abortserver))
|
||||
return; // something bad happened, or the
|
||||
// server disconnected
|
||||
|
||||
qfZoneScoped (true);
|
||||
rand (); // keep the random time dependent
|
||||
|
||||
if (cls.demo_capture)
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "qfalloca.h"
|
||||
|
||||
|
@ -451,7 +450,7 @@ static cvar_t host_speeds_cvar = {
|
|||
|
||||
int fps_count;
|
||||
|
||||
jmp_buf host_abort;
|
||||
static sys_jmpbuf host_abort;
|
||||
|
||||
char *server_version = NULL; // version of server we connected to
|
||||
|
||||
|
@ -1684,7 +1683,7 @@ Host_EndGame (const char *message, ...)
|
|||
|
||||
CL_Disconnect ();
|
||||
|
||||
longjmp (host_abort, 1);
|
||||
Sys_longjmp (host_abort);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1716,7 +1715,7 @@ Host_Error (const char *error, ...)
|
|||
inerror = false;
|
||||
|
||||
if (host_initialized) {
|
||||
longjmp (host_abort, 1);
|
||||
Sys_longjmp (host_abort);
|
||||
} else {
|
||||
Sys_Error ("Host_Error: %s", str->str);
|
||||
}
|
||||
|
@ -1866,7 +1865,7 @@ Host_Frame (float time)
|
|||
float sleeptime;
|
||||
int pass1, pass2, pass3;
|
||||
|
||||
if (setjmp (host_abort))
|
||||
if (Sys_setjmp (host_abort))
|
||||
// something bad happened, or the server disconnected
|
||||
return;
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "QF/cbuf.h"
|
||||
#include "QF/idparse.h"
|
||||
|
@ -2078,8 +2077,7 @@ SV_OutOfBandPrint (netadr_t adr, const char *format, ...)
|
|||
static void
|
||||
SV_ReadPackets (void)
|
||||
{
|
||||
//NOTE star volatile, not volatile star
|
||||
client_t *volatile cl; // * volatile for longjmp
|
||||
client_t *cl;
|
||||
int i;
|
||||
int qport;
|
||||
double until;
|
||||
|
|
Loading…
Reference in a new issue