new Linux signal handlers

removed the statically linked libbacktrace
dynamically linking to libunwind8 when available
fixed the c++98 option being passed to the wrong premake filter
This commit is contained in:
myT 2017-10-10 19:36:12 +02:00
parent 584d064523
commit ac48bc3248
16 changed files with 1379 additions and 183 deletions

View file

@ -0,0 +1,257 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2001-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#define UNW_VERSION_MAJOR 1
#define UNW_VERSION_MINOR 1
#define UNW_VERSION_EXTRA
#define UNW_VERSION_CODE(maj,min) (((maj) << 16) | (min))
#define UNW_VERSION UNW_VERSION_CODE(UNW_VERSION_MAJOR, UNW_VERSION_MINOR)
#define UNW_PASTE2(x,y) x##y
#define UNW_PASTE(x,y) UNW_PASTE2(x,y)
#define UNW_OBJ(fn) UNW_PASTE(UNW_PREFIX, fn)
#define UNW_ARCH_OBJ(fn) UNW_PASTE(UNW_PASTE(UNW_PASTE(_U,UNW_TARGET),_), fn)
#ifdef UNW_LOCAL_ONLY
# define UNW_PREFIX UNW_PASTE(UNW_PASTE(_UL,UNW_TARGET),_)
#else /* !UNW_LOCAL_ONLY */
# define UNW_PREFIX UNW_PASTE(UNW_PASTE(_U,UNW_TARGET),_)
#endif /* !UNW_LOCAL_ONLY */
/* Error codes. The unwind routines return the *negated* values of
these error codes on error and a non-negative value on success. */
typedef enum
{
UNW_ESUCCESS = 0, /* no error */
UNW_EUNSPEC, /* unspecified (general) error */
UNW_ENOMEM, /* out of memory */
UNW_EBADREG, /* bad register number */
UNW_EREADONLYREG, /* attempt to write read-only register */
UNW_ESTOPUNWIND, /* stop unwinding */
UNW_EINVALIDIP, /* invalid IP */
UNW_EBADFRAME, /* bad frame */
UNW_EINVAL, /* unsupported operation or bad value */
UNW_EBADVERSION, /* unwind info has unsupported version */
UNW_ENOINFO /* no unwind info found */
}
unw_error_t;
/* The following enum defines the indices for a couple of
(pseudo-)registers which have the same meaning across all
platforms. (RO) means read-only. (RW) means read-write. General
registers (aka "integer registers") are expected to start with
index 0. The number of such registers is architecture-dependent.
The remaining indices can be used as an architecture sees fit. The
last valid register index is given by UNW_REG_LAST. */
typedef enum
{
UNW_REG_IP = UNW_TDEP_IP, /* (rw) instruction pointer (pc) */
UNW_REG_SP = UNW_TDEP_SP, /* (ro) stack pointer */
UNW_REG_EH = UNW_TDEP_EH, /* (rw) exception-handling reg base */
UNW_REG_LAST = UNW_TDEP_LAST_REG
}
unw_frame_regnum_t;
/* Number of exception-handler argument registers: */
#define UNW_NUM_EH_REGS UNW_TDEP_NUM_EH_REGS
typedef enum
{
UNW_CACHE_NONE, /* no caching */
UNW_CACHE_GLOBAL, /* shared global cache */
UNW_CACHE_PER_THREAD /* per-thread caching */
}
unw_caching_policy_t;
typedef int unw_regnum_t;
/* The unwind cursor starts at the youngest (most deeply nested) frame
and is used to track the frame state as the unwinder steps from
frame to frame. It is safe to make (shallow) copies of variables
of this type. */
typedef struct unw_cursor
{
unw_word_t opaque[UNW_TDEP_CURSOR_LEN];
}
unw_cursor_t;
/* This type encapsulates the entire (preserved) machine-state. */
typedef unw_tdep_context_t unw_context_t;
/* unw_getcontext() fills the unw_context_t pointed to by UC with the
machine state as it exists at the call-site. For implementation
reasons, this needs to be a target-dependent macro. It's easiest
to think of unw_getcontext() as being identical to getcontext(). */
#define unw_getcontext(uc) unw_tdep_getcontext(uc)
/* Return 1 if register number R is a floating-point register, zero
otherwise.
This routine is signal-safe. */
#define unw_is_fpreg(r) unw_tdep_is_fpreg(r)
typedef unw_tdep_fpreg_t unw_fpreg_t;
typedef struct unw_addr_space *unw_addr_space_t;
/* Each target may define it's own set of flags, but bits 0-15 are
reserved for general libunwind-use. */
#define UNW_PI_FLAG_FIRST_TDEP_BIT 16
/* The information comes from a .debug_frame section. */
#define UNW_PI_FLAG_DEBUG_FRAME 32
typedef struct unw_proc_info
{
unw_word_t start_ip; /* first IP covered by this procedure */
unw_word_t end_ip; /* first IP NOT covered by this procedure */
unw_word_t lsda; /* address of lang.-spec. data area (if any) */
unw_word_t handler; /* optional personality routine */
unw_word_t gp; /* global-pointer value for this procedure */
unw_word_t flags; /* misc. flags */
int format; /* unwind-info format (arch-specific) */
int unwind_info_size; /* size of the information (if applicable) */
void *unwind_info; /* unwind-info (arch-specific) */
unw_tdep_proc_info_t extra; /* target-dependent auxiliary proc-info */
}
unw_proc_info_t;
/* These are backend callback routines that provide access to the
state of a "remote" process. This can be used, for example, to
unwind another process through the ptrace() interface. */
typedef struct unw_accessors
{
/* Look up the unwind info associated with instruction-pointer IP.
On success, the routine fills in the PROC_INFO structure. */
int (*find_proc_info) (unw_addr_space_t, unw_word_t, unw_proc_info_t *,
int, void *);
/* Release any resources (e.g., memory) that were allocated for
the unwind info returned in by a previous call to
find_proc_info() with NEED_UNWIND_INFO set to 1. */
void (*put_unwind_info) (unw_addr_space_t, unw_proc_info_t *, void *);
/* Return the list-head of the dynamically registered unwind
info. */
int (*get_dyn_info_list_addr) (unw_addr_space_t, unw_word_t *, void *);
/* Access aligned word at address ADDR. The value is returned
according to the endianness of the host (e.g., if the host is
little-endian and the target is big-endian, access_mem() needs
to byte-swap the value before returning it). */
int (*access_mem) (unw_addr_space_t, unw_word_t, unw_word_t *, int,
void *);
/* Access register number REG at address ADDR. */
int (*access_reg) (unw_addr_space_t, unw_regnum_t, unw_word_t *, int,
void *);
/* Access register number REG at address ADDR. */
int (*access_fpreg) (unw_addr_space_t, unw_regnum_t,
unw_fpreg_t *, int, void *);
int (*resume) (unw_addr_space_t, unw_cursor_t *, void *);
/* Optional call back to obtain the name of a (static) procedure.
Dynamically generated procedures are handled automatically by
libunwind. This callback is optional and may be set to
NULL. */
int (*get_proc_name) (unw_addr_space_t, unw_word_t, char *, size_t,
unw_word_t *, void *);
}
unw_accessors_t;
typedef enum unw_save_loc_type
{
UNW_SLT_NONE, /* register is not saved ("not an l-value") */
UNW_SLT_MEMORY, /* register has been saved in memory */
UNW_SLT_REG /* register has been saved in (another) register */
}
unw_save_loc_type_t;
typedef struct unw_save_loc
{
unw_save_loc_type_t type;
union
{
unw_word_t addr; /* valid if type==UNW_SLT_MEMORY */
unw_regnum_t regnum; /* valid if type==UNW_SLT_REG */
}
u;
unw_tdep_save_loc_t extra; /* target-dependent additional information */
}
unw_save_loc_t;
/* These routines work both for local and remote unwinding. */
#define unw_local_addr_space UNW_OBJ(local_addr_space)
#define unw_create_addr_space UNW_OBJ(create_addr_space)
#define unw_destroy_addr_space UNW_OBJ(destroy_addr_space)
#define unw_get_accessors UNW_ARCH_OBJ(get_accessors)
#define unw_init_local UNW_OBJ(init_local)
#define unw_init_remote UNW_OBJ(init_remote)
#define unw_step UNW_OBJ(step)
#define unw_resume UNW_OBJ(resume)
#define unw_get_proc_info UNW_OBJ(get_proc_info)
#define unw_get_proc_info_by_ip UNW_OBJ(get_proc_info_by_ip)
#define unw_get_reg UNW_OBJ(get_reg)
#define unw_set_reg UNW_OBJ(set_reg)
#define unw_get_fpreg UNW_OBJ(get_fpreg)
#define unw_set_fpreg UNW_OBJ(set_fpreg)
#define unw_get_save_loc UNW_OBJ(get_save_loc)
#define unw_is_signal_frame UNW_OBJ(is_signal_frame)
#define unw_handle_signal_frame UNW_OBJ(handle_signal_frame)
#define unw_get_proc_name UNW_OBJ(get_proc_name)
#define unw_set_caching_policy UNW_OBJ(set_caching_policy)
#define unw_regname UNW_ARCH_OBJ(regname)
#define unw_flush_cache UNW_ARCH_OBJ(flush_cache)
#define unw_strerror UNW_ARCH_OBJ(strerror)
extern unw_addr_space_t unw_create_addr_space (unw_accessors_t *, int);
extern void unw_destroy_addr_space (unw_addr_space_t);
extern unw_accessors_t *unw_get_accessors (unw_addr_space_t);
extern void unw_flush_cache (unw_addr_space_t, unw_word_t, unw_word_t);
extern int unw_set_caching_policy (unw_addr_space_t, unw_caching_policy_t);
extern const char *unw_regname (unw_regnum_t);
extern int unw_init_local (unw_cursor_t *, unw_context_t *);
extern int unw_init_remote (unw_cursor_t *, unw_addr_space_t, void *);
extern int unw_step (unw_cursor_t *);
extern int unw_resume (unw_cursor_t *);
extern int unw_get_proc_info (unw_cursor_t *, unw_proc_info_t *);
extern int unw_get_proc_info_by_ip (unw_addr_space_t, unw_word_t,
unw_proc_info_t *, void *);
extern int unw_get_reg (unw_cursor_t *, int, unw_word_t *);
extern int unw_set_reg (unw_cursor_t *, int, unw_word_t);
extern int unw_get_fpreg (unw_cursor_t *, int, unw_fpreg_t *);
extern int unw_set_fpreg (unw_cursor_t *, int, unw_fpreg_t);
extern int unw_get_save_loc (unw_cursor_t *, int, unw_save_loc_t *);
extern int unw_is_signal_frame (unw_cursor_t *);
extern int unw_handle_signal_frame (unw_cursor_t *);
extern int unw_get_proc_name (unw_cursor_t *, char *, size_t, unw_word_t *);
extern const char *unw_strerror (int);
extern int unw_backtrace (void **, int);
extern unw_addr_space_t unw_local_addr_space;

View file

@ -0,0 +1,73 @@
/* libunwind - a platform-independent unwind library
This file is part of libunwind.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#ifndef libunwind_coredump_h
#define libunwind_coredump_h
#include <libunwind.h>
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
/* Helper routines which make it easy to use libunwind on a coredump.
They're available only if UNW_REMOTE_ONLY is _not_ defined and they
aren't really part of the libunwind API. They are implemented in a
archive library called libunwind-coredump.a. */
struct UCD_info;
extern struct UCD_info *_UCD_create(const char *filename);
extern void _UCD_destroy(struct UCD_info *);
extern int _UCD_get_num_threads(struct UCD_info *);
extern void _UCD_select_thread(struct UCD_info *, int);
extern pid_t _UCD_get_pid(struct UCD_info *);
extern int _UCD_get_cursig(struct UCD_info *);
extern int _UCD_add_backing_file_at_segment(struct UCD_info *, int phdr_no, const char *filename);
extern int _UCD_add_backing_file_at_vaddr(struct UCD_info *,
unsigned long vaddr,
const char *filename);
extern int _UCD_find_proc_info (unw_addr_space_t, unw_word_t,
unw_proc_info_t *, int, void *);
extern void _UCD_put_unwind_info (unw_addr_space_t, unw_proc_info_t *, void *);
extern int _UCD_get_dyn_info_list_addr (unw_addr_space_t, unw_word_t *,
void *);
extern int _UCD_access_mem (unw_addr_space_t, unw_word_t, unw_word_t *, int,
void *);
extern int _UCD_access_reg (unw_addr_space_t, unw_regnum_t, unw_word_t *,
int, void *);
extern int _UCD_access_fpreg (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *,
int, void *);
extern int _UCD_get_proc_name (unw_addr_space_t, unw_word_t, char *, size_t,
unw_word_t *, void *);
extern int _UCD_resume (unw_addr_space_t, unw_cursor_t *, void *);
extern unw_accessors_t _UCD_accessors;
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* libunwind_coredump_h */

View file

@ -0,0 +1,210 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2002-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/* This file defines the runtime-support routines for dynamically
generated code. Even though it is implemented as part of libunwind,
it is logically separate from the interface to perform the actual
unwinding. In particular, this interface is always used in the
context of the unwind target, whereas the rest of the unwind API is
used in context of the process that is doing the unwind (which may be
a debugger running on another machine, for example).
Note that the data-structures declared here server a dual purpose:
when a program registers a dynamically generated procedure, it uses
these structures directly. On the other hand, with remote-unwinding,
the data-structures are read from the remote process's memory and
translated into internalized versions. To facilitate remote-access,
the following rules should be followed in declaring these structures:
(1) Declare a member as a pointer only if the the information the
member points to needs to be internalized as well (e.g., a
string representing a procedure name should be declared as
"const char *", but the instruction pointer should be declared
as unw_word_t).
(2) Provide sufficient padding to ensure that no implicit padding
will be needed on any of the supported target architectures. For
the time being, padding data structures with the assumption that
sizeof (unw_word_t) == 8 should be sufficient. (Note: it's not
impossible to internalize structures with internal padding, but
it does make the process a bit harder).
(3) Don't declare members that contain bitfields or floating-point
values.
(4) Don't declare members with enumeration types. Declare them as
int32_t instead. */
typedef enum
{
UNW_DYN_STOP = 0, /* end-of-unwind-info marker */
UNW_DYN_SAVE_REG, /* save register to another register */
UNW_DYN_SPILL_FP_REL, /* frame-pointer-relative register spill */
UNW_DYN_SPILL_SP_REL, /* stack-pointer-relative register spill */
UNW_DYN_ADD, /* add constant value to a register */
UNW_DYN_POP_FRAMES, /* drop one or more stack frames */
UNW_DYN_LABEL_STATE, /* name the current state */
UNW_DYN_COPY_STATE, /* set the region's entry-state */
UNW_DYN_ALIAS /* get unwind info from an alias */
}
unw_dyn_operation_t;
typedef enum
{
UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */
UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */
UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */
UNW_INFO_FORMAT_ARM_EXIDX /* ARM specific unwind info */
}
unw_dyn_info_format_t;
typedef struct unw_dyn_op
{
int8_t tag; /* what operation? */
int8_t qp; /* qualifying predicate register */
int16_t reg; /* what register */
int32_t when; /* when does it take effect? */
unw_word_t val; /* auxiliary value */
}
unw_dyn_op_t;
typedef struct unw_dyn_region_info
{
struct unw_dyn_region_info *next; /* linked list of regions */
int32_t insn_count; /* region length (# of instructions) */
uint32_t op_count; /* length of op-array */
unw_dyn_op_t op[1]; /* variable-length op-array */
}
unw_dyn_region_info_t;
typedef struct unw_dyn_proc_info
{
unw_word_t name_ptr; /* address of human-readable procedure name */
unw_word_t handler; /* address of personality routine */
uint32_t flags;
int32_t pad0;
unw_dyn_region_info_t *regions;
}
unw_dyn_proc_info_t;
typedef struct unw_dyn_table_info
{
unw_word_t name_ptr; /* addr. of table name (e.g., library name) */
unw_word_t segbase; /* segment base */
unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */
unw_word_t *table_data;
}
unw_dyn_table_info_t;
typedef struct unw_dyn_remote_table_info
{
unw_word_t name_ptr; /* addr. of table name (e.g., library name) */
unw_word_t segbase; /* segment base */
unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */
unw_word_t table_data;
}
unw_dyn_remote_table_info_t;
typedef struct unw_dyn_info
{
/* doubly-linked list of dyn-info structures: */
struct unw_dyn_info *next;
struct unw_dyn_info *prev;
unw_word_t start_ip; /* first IP covered by this entry */
unw_word_t end_ip; /* first IP NOT covered by this entry */
unw_word_t gp; /* global-pointer in effect for this entry */
int32_t format; /* real type: unw_dyn_info_format_t */
int32_t pad;
union
{
unw_dyn_proc_info_t pi;
unw_dyn_table_info_t ti;
unw_dyn_remote_table_info_t rti;
}
u;
}
unw_dyn_info_t;
typedef struct unw_dyn_info_list
{
uint32_t version;
uint32_t generation;
unw_dyn_info_t *first;
}
unw_dyn_info_list_t;
/* Return the size (in bytes) of an unw_dyn_region_info_t structure that can
hold OP_COUNT ops. */
#define _U_dyn_region_info_size(op_count) \
((char *) (((unw_dyn_region_info_t *) NULL)->op + (op_count)) \
- (char *) NULL)
/* Register the unwind info for a single procedure.
This routine is NOT signal-safe. */
extern void _U_dyn_register (unw_dyn_info_t *);
/* Cancel the unwind info for a single procedure.
This routine is NOT signal-safe. */
extern void _U_dyn_cancel (unw_dyn_info_t *);
/* Convenience routines. */
#define _U_dyn_op(_tag, _qp, _when, _reg, _val) \
((unw_dyn_op_t) { (_tag), (_qp), (_reg), (_when), (_val) })
#define _U_dyn_op_save_reg(op, qp, when, reg, dst) \
(*(op) = _U_dyn_op (UNW_DYN_SAVE_REG, (qp), (when), (reg), (dst)))
#define _U_dyn_op_spill_fp_rel(op, qp, when, reg, offset) \
(*(op) = _U_dyn_op (UNW_DYN_SPILL_FP_REL, (qp), (when), (reg), \
(offset)))
#define _U_dyn_op_spill_sp_rel(op, qp, when, reg, offset) \
(*(op) = _U_dyn_op (UNW_DYN_SPILL_SP_REL, (qp), (when), (reg), \
(offset)))
#define _U_dyn_op_add(op, qp, when, reg, value) \
(*(op) = _U_dyn_op (UNW_DYN_ADD, (qp), (when), (reg), (value)))
#define _U_dyn_op_pop_frames(op, qp, when, num_frames) \
(*(op) = _U_dyn_op (UNW_DYN_POP_FRAMES, (qp), (when), 0, (num_frames)))
#define _U_dyn_op_label_state(op, label) \
(*(op) = _U_dyn_op (UNW_DYN_LABEL_STATE, _U_QP_TRUE, -1, 0, (label)))
#define _U_dyn_op_copy_state(op, label) \
(*(op) = _U_dyn_op (UNW_DYN_COPY_STATE, _U_QP_TRUE, -1, 0, (label)))
#define _U_dyn_op_alias(op, qp, when, addr) \
(*(op) = _U_dyn_op (UNW_DYN_ALIAS, (qp), (when), 0, (addr)))
#define _U_dyn_op_stop(op) \
(*(op) = _U_dyn_op (UNW_DYN_STOP, _U_QP_TRUE, -1, 0, 0))
/* The target-dependent qualifying predicate which is always TRUE. On
IA-64, that's p0 (0), on non-predicated architectures, the value is
ignored. */
#define _U_QP_TRUE _U_TDEP_QP_TRUE

View file

@ -0,0 +1,63 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of libunwind.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#ifndef libunwind_ptrace_h
#define libunwind_ptrace_h
#include <libunwind.h>
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
/* Helper routines which make it easy to use libunwind via ptrace().
They're available only if UNW_REMOTE_ONLY is _not_ defined and they
aren't really part of the libunwind API. They are implemented in a
archive library called libunwind-ptrace.a. */
extern void *_UPT_create (pid_t);
extern void _UPT_destroy (void *);
extern int _UPT_find_proc_info (unw_addr_space_t, unw_word_t,
unw_proc_info_t *, int, void *);
extern void _UPT_put_unwind_info (unw_addr_space_t, unw_proc_info_t *, void *);
extern int _UPT_get_dyn_info_list_addr (unw_addr_space_t, unw_word_t *,
void *);
extern int _UPT_access_mem (unw_addr_space_t, unw_word_t, unw_word_t *, int,
void *);
extern int _UPT_access_reg (unw_addr_space_t, unw_regnum_t, unw_word_t *,
int, void *);
extern int _UPT_access_fpreg (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *,
int, void *);
extern int _UPT_get_proc_name (unw_addr_space_t, unw_word_t, char *, size_t,
unw_word_t *, void *);
extern int _UPT_resume (unw_addr_space_t, unw_cursor_t *, void *);
extern unw_accessors_t _UPT_accessors;
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* libunwind_ptrace_h */

View file

@ -0,0 +1,139 @@
/* libunwind - a platform-independent unwind library
Copyright (C) 2002-2004 Hewlett-Packard Co
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
This file is part of libunwind.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#ifndef LIBUNWIND_H
#define LIBUNWIND_H
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#include <sys/types.h>
#include <inttypes.h>
#include <ucontext.h>
#define UNW_TARGET x86_64
#define UNW_TARGET_X86_64 1
#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */
/* This needs to be big enough to accommodate "struct cursor", while
leaving some slack for future expansion. Changing this value will
require recompiling all users of this library. Stack allocation is
relatively cheap and unwind-state copying is relatively rare, so we
want to err on making it rather too big than too small. */
#define UNW_TDEP_CURSOR_LEN 127
typedef uint64_t unw_word_t;
typedef int64_t unw_sword_t;
typedef long double unw_tdep_fpreg_t;
typedef enum
{
UNW_X86_64_RAX,
UNW_X86_64_RDX,
UNW_X86_64_RCX,
UNW_X86_64_RBX,
UNW_X86_64_RSI,
UNW_X86_64_RDI,
UNW_X86_64_RBP,
UNW_X86_64_RSP,
UNW_X86_64_R8,
UNW_X86_64_R9,
UNW_X86_64_R10,
UNW_X86_64_R11,
UNW_X86_64_R12,
UNW_X86_64_R13,
UNW_X86_64_R14,
UNW_X86_64_R15,
UNW_X86_64_RIP,
#ifdef CONFIG_MSABI_SUPPORT
UNW_X86_64_XMM0,
UNW_X86_64_XMM1,
UNW_X86_64_XMM2,
UNW_X86_64_XMM3,
UNW_X86_64_XMM4,
UNW_X86_64_XMM5,
UNW_X86_64_XMM6,
UNW_X86_64_XMM7,
UNW_X86_64_XMM8,
UNW_X86_64_XMM9,
UNW_X86_64_XMM10,
UNW_X86_64_XMM11,
UNW_X86_64_XMM12,
UNW_X86_64_XMM13,
UNW_X86_64_XMM14,
UNW_X86_64_XMM15,
UNW_TDEP_LAST_REG = UNW_X86_64_XMM15,
#else
UNW_TDEP_LAST_REG = UNW_X86_64_RIP,
#endif
/* XXX Add other regs here */
/* frame info (read-only) */
UNW_X86_64_CFA,
UNW_TDEP_IP = UNW_X86_64_RIP,
UNW_TDEP_SP = UNW_X86_64_RSP,
UNW_TDEP_BP = UNW_X86_64_RBP,
UNW_TDEP_EH = UNW_X86_64_RAX
}
x86_64_regnum_t;
#define UNW_TDEP_NUM_EH_REGS 2 /* XXX Not sure what this means */
typedef struct unw_tdep_save_loc
{
/* Additional target-dependent info on a save location. */
}
unw_tdep_save_loc_t;
/* On x86_64, we can directly use ucontext_t as the unwind context. */
typedef ucontext_t unw_tdep_context_t;
typedef struct
{
/* no x86-64-specific auxiliary proc-info */
}
unw_tdep_proc_info_t;
#include "libunwind-dynamic.h"
#include "libunwind-common.h"
#define unw_tdep_getcontext UNW_ARCH_OBJ(getcontext)
#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
extern int unw_tdep_getcontext (unw_tdep_context_t *);
extern int unw_tdep_is_fpreg (int);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* LIBUNWIND_H */

View file

@ -0,0 +1,34 @@
/* Provide a real file - not a symlink - as it would cause multiarch conflicts
when multiple different arch releases are installed simultaneously. */
#ifndef UNW_REMOTE_ONLY
#if defined __aarch64__
#include "libunwind-aarch64.h"
#elif defined __arm__
# include "libunwind-arm.h"
#elif defined __hppa__
# include "libunwind-hppa.h"
#elif defined __ia64__
# include "libunwind-ia64.h"
#elif defined __mips__
# include "libunwind-mips.h"
#elif defined __powerpc__ && !defined __powerpc64__
# include "libunwind-ppc32.h"
#elif defined __powerpc64__
# include "libunwind-ppc64.h"
#elif defined __sh__
# include "libunwind-sh.h"
#elif defined __i386__
# include "libunwind-x86.h"
#elif defined __x86_64__
# include "libunwind-x86_64.h"
#else
# error "Unsupported arch"
#endif
#else /* UNW_REMOTE_ONLY */
# include "libunwind-x86_64.h"
#endif /* UNW_REMOTE_ONLY */

View file

@ -5,14 +5,20 @@
typedef struct {
char gitHeadHash[24]; // SHA-1 -> 160 bits -> 20 hex chars
vm_t* vm;
unsigned int crc32; // CRC32
} vmCrashInfo_t;
char gitHeadHash[24]; // SHA-1 -> 160 bits -> 20 hex chars
vm_t* vm;
unsigned int crc32;
} vmCrash_t;
typedef struct {
vmCrash_t vm[VM_COUNT];
char modName[64];
char modVersion[64];
} crash_t;
static crash_t crash;
static vmCrashInfo_t crash_vm[VM_COUNT];
static char crash_modName[64];
static char crash_modVersion[64];
#if defined(_WIN32)
# define NEWLINE "\n"
@ -39,13 +45,13 @@ static const char* GetVMName(vmIndex_t vmIndex)
void Crash_SaveQVMPointer(vmIndex_t vmIndex, vm_t* vm)
{
if (IsVMIndexValid(vmIndex))
crash_vm[vmIndex].vm = vm;
crash.vm[vmIndex].vm = vm;
}
void Crash_SaveQVMChecksum(vmIndex_t vmIndex, unsigned int crc32)
{
if (IsVMIndexValid(vmIndex))
crash_vm[vmIndex].crc32 = crc32;
crash.vm[vmIndex].crc32 = crc32;
}
void Crash_SaveQVMGitString(const char* varName, const char* varValue)
@ -63,26 +69,26 @@ void Crash_SaveQVMGitString(const char* varName, const char* varValue)
else
return;
Q_strncpyz(crash_vm[index].gitHeadHash, varValue, sizeof(crash_vm[index].gitHeadHash));
Q_strncpyz(crash.vm[index].gitHeadHash, varValue, sizeof(crash.vm[index].gitHeadHash));
}
void Crash_SaveModName(const char* modName)
{
if (modName && *modName != '\0')
Q_strncpyz(crash_modName, modName, sizeof(crash_modName));
Q_strncpyz(crash.modName, modName, sizeof(crash.modName));
}
void Crash_SaveModVersion(const char* modVersion)
{
if (modVersion && *modVersion != '\0')
Q_strncpyz(crash_modVersion, modVersion, sizeof(crash_modVersion));
Q_strncpyz(crash.modVersion, modVersion, sizeof(crash.modVersion));
}
static void PrintQVMInfo(vmIndex_t vmIndex)
{
static char callStack[MAX_VM_CALL_STACK_DEPTH * 12];
vmCrashInfo_t* vm = &crash_vm[vmIndex];
vmCrash_t* vm = &crash.vm[vmIndex];
if (vm->crc32 == 0) {
return;
}
@ -131,7 +137,7 @@ static void PrintQVMInfo(vmIndex_t vmIndex)
static qbool IsAnyVMLoaded()
{
for (int i = 0; i < VM_COUNT; i++) {
if (crash_vm[i].crc32 != 0)
if (crash.vm[i].crc32 != 0)
return qtrue;
}
@ -206,8 +212,8 @@ void Crash_PrintToFile(const char* engineFilePath)
const unsigned int crc32 = CRC32_HashFile(engineFilePath);
if (crc32)
JSONW_HexValue("engine_crc32", crc32);
JSONW_StringValue("mod_name", crash_modName);
JSONW_StringValue("mod_version", crash_modVersion);
JSONW_StringValue("mod_name", crash.modName);
JSONW_StringValue("mod_version", crash.modVersion);
if (IsAnyVMLoaded()) {
JSONW_BeginNamedArray("vms");
@ -217,3 +223,67 @@ void Crash_PrintToFile(const char* engineFilePath)
JSONW_EndArray();
}
}
#if defined(__linux__)
#include <unistd.h>
static void Print(int fd, const char* msg)
{
write(fd, msg, strlen(msg));
}
static void PrintVMStackTrace(int fd, vmIndex_t vmIndex)
{
Print(fd, GetVMName(vmIndex));
Print(fd, ": ");
vmCrash_t* const vmc = &crash.vm[vmIndex];
vm_t* const vm = vmc->vm;
if (vm == NULL) {
Print(fd, "not loaded\r\n");
return;
}
Print(fd, "crc32 ");
Print(fd, Q_itohex(vmc->crc32, qtrue, qtrue));
Print(fd, " - ");
int d = vm->callStackDepth;
qbool current = qtrue;
if (d <= 0 || d > MAX_VM_CALL_STACK_DEPTH) {
d = vm->lastCallStackDepth;
current = qfalse;
}
if (d <= 0 || d > MAX_VM_CALL_STACK_DEPTH)
d = 0;
Print(fd, current ? "current " : "old ");
if (d > 0) {
for (int i = 0; i < d; i++) {
Print(fd, Q_itohex(vm->callStack[i], qtrue, qtrue));
if (i - 1 < d)
Print(fd, " ");
}
}
Print(fd, "\r\n");
}
void Crash_PrintVMStackTracesASS(int fd)
{
if (crash.modName[0] != '\0') {
Print(fd, "mod: ");
Print(fd, crash.modName);
Print(fd, " ");
Print(fd, crash.modVersion);
Print(fd, "\r\n");
}
for (int i = 0; i < VM_COUNT; i++) {
PrintVMStackTrace(fd, (vmIndex_t)i);
}
}
#endif

View file

@ -27,3 +27,6 @@ void Crash_SaveQVMGitString(const char* varName, const char* varValue);
void Crash_SaveModName(const char* modName);
void Crash_SaveModVersion(const char* modVersion);
void Crash_PrintToFile(const char* engineFilePath);
#if defined(__linux__)
void Crash_PrintVMStackTracesASS(int fd); // async-signal-safe
#endif

View file

@ -38,7 +38,9 @@ void QGL_EnableLogging( qboolean enable );
void QGL_Shutdown( void );
// signals.c
void SIG_Init(void);
void SIG_InitChild();
void SIG_InitParent();
void SIG_Frame();
extern int q_argc;
extern char** q_argv;

View file

@ -22,6 +22,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <signal.h>
#include <execinfo.h>
#include <backtrace.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dlfcn.h>
#include <cxxabi.h>
#include <sys/stat.h>
#include <sys/types.h>
#define UNW_LOCAL_ONLY
#include "../libunwind/libunwind.h"
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
@ -29,36 +39,106 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "linux_local.h"
// columns: Symbol, IsCrash, Desc
#define SIGNAL_LIST(X) \
X(SIGHUP, qfalse, "hangup detected on controlling terminal or death of controlling process") \
X(SIGQUIT, qfalse, "quit from keyboard") \
X(SIGILL, qtrue, "illegal instruction") \
X(SIGTRAP, qfalse, "trace/breakpoint trap") \
X(SIGIOT, qtrue, "IOT trap (a synonym for SIGABRT)") \
X(SIGBUS, qtrue, "bus error (bad memory access)") \
X(SIGFPE, qtrue, "fatal arithmetic error") \
X(SIGSEGV, qtrue, "invalid memory reference") \
X(SIGTERM, qfalse, "termination signal") \
X(SIGINT, qfalse, "interrupt")
/*
A sub-process created by fork from a signal handler will most certainly
run in the context of the signal handler and should therefore also be
limited to calling async-signal-safe functions only.
So the idea of launching a new process from a handler for doing the heavy
lifting (resolving symbols etc) is completely dead.
*/
static qboolean Sig_IsCrashSignal(int sig)
{
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return IsCrash;
switch (sig) {
SIGNAL_LIST(SIGNAL_ITEM)
default: return qfalse;
}
// the app crashed
// columns: Symbol, Desc
#define CRASH_SIGNAL_LIST(X) \
X(SIGILL, "illegal instruction") \
X(SIGIOT, "IOT trap (a synonym for SIGABRT)") \
X(SIGBUS, "bus error (bad memory access)") \
X(SIGFPE, "fatal arithmetic error") \
X(SIGSEGV, "invalid memory reference")
// the app should terminate normally
// columns: Symbol, Desc
#define TERM_SIGNAL_LIST(X) \
X(SIGHUP, "hangup detected on controlling terminal or death of controlling process") \
X(SIGQUIT, "quit from keyboard") \
X(SIGTRAP, "trace/breakpoint trap") \
X(SIGTERM, "termination signal") \
X(SIGINT, "interrupt")
#define SIGNAL_ITEM(Symbol, Desc) 1 +
static const int sig_crashSignalCount = CRASH_SIGNAL_LIST(SIGNAL_ITEM) 0;
static const int sig_termSignalCount = TERM_SIGNAL_LIST(SIGNAL_ITEM) 0;
#undef SIGNAL_ITEM
#define SIGNAL_ITEM(Symbol, Desc) Symbol,
static const int sig_crashSignals[sig_crashSignalCount + 1] =
{
CRASH_SIGNAL_LIST(SIGNAL_ITEM)
0
};
static const int sig_termSignals[sig_termSignalCount + 1] =
{
TERM_SIGNAL_LIST(SIGNAL_ITEM)
0
};
#undef SIGNAL_ITEM
static char sig_backTraceFilePath[MAX_OSPATH];
static char sig_jsonFilePath[MAX_OSPATH];
static void Sig_Unwind_OpenLibrary();
static void Sig_Unwind_GetContext();
static void Sig_Unwind_Print(FILE* file);
static void Sig_Unwind_PrintASS(int fd); // async-signal-safe
// both of these can call Com_Error, which is not safe
#define Q_strncpyz Sig_strncpyz
#define Q_strcat Sig_strcat
// make sure we don't use these
#define strcpy do_not_use_strcpy
#define strcat do_not_use_strcat
// async-signal-safe
static void Sig_strncpyz(char* dest, const char* src, int size)
{
if (!dest || !src || size < 1)
return;
strncpy(dest, src, size - 1);
dest[size - 1] = '\0';
}
// async-signal-safe
static void Sig_strcat(char* dest, int size, const char* src)
{
if (!dest || !src || size < 1)
return;
const int destLength = strlen(dest);
if (destLength >= size)
return;
Sig_strncpyz(dest + destLength, src, size - destLength);
}
static const char* Sig_GetDescription(int sig)
{
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return Desc;
switch (sig) {
SIGNAL_LIST(SIGNAL_ITEM)
#define SIGNAL_ITEM(Symbol, Desc) case Symbol: return Desc;
switch (sig)
{
CRASH_SIGNAL_LIST(SIGNAL_ITEM)
TERM_SIGNAL_LIST(SIGNAL_ITEM)
default: return "unhandled signal";
}
#undef SIGNAL_ITEM
@ -67,17 +147,21 @@ static const char* Sig_GetDescription(int sig)
static const char* Sig_GetName(int sig)
{
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return #Symbol;
switch (sig) {
SIGNAL_LIST(SIGNAL_ITEM)
#define SIGNAL_ITEM(Symbol, Desc) case Symbol: return #Symbol;
switch (sig)
{
CRASH_SIGNAL_LIST(SIGNAL_ITEM)
TERM_SIGNAL_LIST(SIGNAL_ITEM)
default: return "unhandled signal";
}
#undef SIGNAL_ITEM
}
static const char* Sig_CreateFileName(const char* extension)
{
static void Sig_UpdateFilePaths()
{
// gmtime and va (sprintf) are not async-signal-safe
const time_t epochTime = time(NULL);
struct tm* const utcTime = gmtime(&epochTime);
const int y = 1900 + utcTime->tm_year;
@ -87,14 +171,37 @@ static const char* Sig_CreateFileName(const char* extension)
const int mi = utcTime->tm_min;
const int s = utcTime->tm_sec;
return va("%s-crash-%d.%02d.%02d-%02d.%02d.%02d%s",
q_argv[0], y, m, d, h, mi, s, extension);
const char* const bn = va(
"%s-crash-%d.%02d.%02d-%02d.%02d.%02d",
q_argv[0], y, m, d, h, mi, s);
char baseName[MAX_OSPATH];
Q_strncpyz(baseName, bn, sizeof(baseName));
Q_strncpyz(sig_backTraceFilePath, va("%s.bt", baseName), sizeof(sig_backTraceFilePath));
Q_strncpyz(sig_jsonFilePath, va("%s.json", baseName), sizeof(sig_jsonFilePath));
}
static const char* Sig_BackTraceFilePath()
{
if (sig_backTraceFilePath[0] == '\0')
return "cnq3-crash.bt";
return sig_backTraceFilePath;
}
static const char* Sig_JSONFilePath()
{
if (sig_jsonFilePath[0] == '\0')
return "cnq3-crash.json";
return sig_jsonFilePath;
}
static void Sig_WriteJSON(int sig)
{
FILE* const file = fopen(Sig_CreateFileName(".json"), "w");
FILE* const file = fopen(Sig_JSONFilePath(), "w");
if (file == NULL)
return;
@ -108,18 +215,12 @@ static void Sig_WriteJSON(int sig)
}
// only uses functions safe to call in a signal handler
static void Sig_WriteBacktraceSafe()
static void Sig_Backtrace_Print(FILE* file)
{
FILE* const file = fopen(Sig_CreateFileName(".bt"), "w");
if (file == NULL)
return;
void* addresses[64];
const int addresscount = backtrace(addresses, sizeof(addresses));
if (addresscount > 0)
{
fprintf(file, "backtrace_symbols_fd stack trace:\r\n");
fflush(file);
backtrace_symbols_fd(addresses, addresscount, fileno(file));
}
@ -127,8 +228,6 @@ static void Sig_WriteBacktraceSafe()
{
fprintf(file, "The call to backtrace failed\r\n");
}
fclose(file);
}
@ -138,128 +237,367 @@ static void libbt_ErrorCallback(void* data, const char* msg, int errnum)
}
// might not be safe in a signal handler
static void Sig_WriteBacktraceUnsafe()
static void Sig_Print(int fd, const char* msg)
{
FILE* const file = fopen(Sig_CreateFileName(".bt"), "a");
if (file == NULL)
return;
write(fd, msg, strlen(msg));
}
fprintf(file, "\r\n\r\n");
backtrace_state* const state = backtrace_create_state(q_argv[0], 0, libbt_ErrorCallback, file);
if (state)
static void Sig_PrintLine(int fd, const char* msg)
{
write(fd, msg, strlen(msg));
write(fd, "\r\n", 2);
}
static void Sig_PrintSignalCaught(int sig)
{
// fprintf is not async-signal-safe, but write is
Sig_Print(STDERR_FILENO, "\nSignal caught: ");
Sig_Print(STDERR_FILENO, Sig_GetName(sig));
Sig_Print(STDERR_FILENO, " (");
Sig_Print(STDERR_FILENO, Sig_GetDescription(sig));
Sig_Print(STDERR_FILENO, ")\r\n");
}
static void Sig_HandleExit()
{
Sys_ConsoleInputShutdown();
}
static volatile sig_atomic_t sig_termRequested = 0;
static void Sig_HandleTermSignal(int sig)
{
Sig_PrintSignalCaught(sig);
sig_termRequested = 1;
}
static void Sig_PrintAttempt(const char* what)
{
Sig_Print(STDERR_FILENO, "Attempting to ");
Sig_Print(STDERR_FILENO, what);
Sig_Print(STDERR_FILENO, "...");
}
static void Sig_PrintDone()
{
Sig_Print(STDERR_FILENO, " done\r\n");
}
static void Sig_HandleCrash(int sig)
{
//
// Phase 1: async-signal-safe code
//
Sig_Unwind_GetContext();
const int fd = open(Sig_BackTraceFilePath(), O_CREAT | O_TRUNC | O_WRONLY);
if (fd != -1)
{
fprintf(file, "libbacktrace stack trace:\r\n");
Sig_PrintAttempt("write safe libunwind stack trace");
Sig_PrintLine(fd, "safe libunwind stack trace:");
Sig_Unwind_PrintASS(fd);
Sig_PrintDone();
Sig_PrintAttempt("write safe mod stack traces");
Sig_PrintLine(fd, "\r\n\r\nmod stack traces:");
Crash_PrintVMStackTracesASS(fd);
Sig_PrintDone();
// user R+W - group R - others R
chmod(Sig_BackTraceFilePath(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
}
//
// Phase 2: buckle up
//
Sig_PrintAttempt("restore tty settings");
Sys_ConsoleInputShutdown();
Sig_PrintDone();
Sig_PrintAttempt("write JSON report");
Sig_WriteJSON(sig);
Sig_PrintDone();
if (fd != -1)
{
FILE* const file = fdopen(fd, "a");
Sig_PrintAttempt("write backtrace stack trace");
fprintf(file, "\r\n\r\nbacktrace_symbols_fd stack trace:\r\n");
Sig_Backtrace_Print(file);
fflush(file);
backtrace_print(state, 0, file);
}
else
{
fprintf(file, "The call to backtrace_create_state failed\r\n");
Sig_PrintDone();
Sig_PrintAttempt("write detailed libunwind stack trace");
fprintf(file, "\r\n\r\ndetailed libunwind stack trace:\r\n");
Sig_Unwind_Print(file);
fflush(file);
Sig_PrintDone();
}
}
static void Sig_HandleCrashSignal(int sig)
{
static volatile sig_atomic_t counter = 0;
Sig_PrintSignalCaught(sig);
++counter;
if (counter == 1)
Sig_HandleCrash(sig);
else if (counter >= 2)
Sig_PrintLine(STDERR_FILENO, "Double fault! Shutting down immediately.");
_exit(1);
}
static void Sig_RegisterSignals(const int* signals, int count, void (*handler)(int), int flags)
{
sigset_t mask;
sigemptyset(&mask);
fclose(file);
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = handler;
action.sa_mask = mask;
action.sa_flags = flags;
for (int i = 0; i < count; ++i)
sigaction(signals[i], &action, NULL);
}
static qbool sig_crashed = qfalse;
static int sig_signal = 0;
// Every call in there needs to be safe when called more than once.
static void SIG_HandleCrash(qbool restoreSettings)
{
if (restoreSettings)
{
// We crashed and only care about restoring system settings
// that the process clean-up won't handle for us.
Sys_ConsoleInputShutdown();
}
if (sig_crashed)
{
Sig_WriteJSON(sig_signal);
Sig_WriteBacktraceSafe();
Sig_WriteBacktraceUnsafe();
}
}
static void SIG_HandleExit()
{
SIG_HandleCrash(qtrue);
}
static void Sig_HandleSignal(int sig)
{
static int faultCounter = 0;
static qbool crashHandled = qfalse;
sig_signal = sig;
faultCounter++;
if (faultCounter >= 3)
{
// We're here because the double fault handler failed.
// We take no chances this time and exit right away to avoid
// calling this function many more times.
exit(3);
}
sig_crashed = Sig_IsCrashSignal(sig);
if (faultCounter == 2)
{
// The termination handler failed which means that if we exit right now,
// some system settings might still be in a bad state.
printf("DOUBLE SIGNAL FAULT: Received signal %d (%s), exiting...\n", sig, Sig_GetName(sig));
if (sig_crashed && !crashHandled)
{
SIG_HandleCrash(qfalse);
}
exit(2);
}
fprintf(sig_crashed ? stderr : stdout, "Received %s signal %d: %s (%s), exiting...\n",
sig_crashed ? "crash" : "termination", sig, Sig_GetName(sig), Sig_GetDescription(sig));
if (sig_crashed)
{
SIG_HandleCrash(qtrue);
crashHandled = qtrue;
exit(1);
}
else
{
// attempt a proper shutdown sequence
#ifndef DEDICATED
CL_Shutdown();
#endif
SV_Shutdown("Signal caught");
Sys_ConsoleInputShutdown();
exit(0);
}
}
void SIG_Init()
{
void SIG_InitChild()
{
// This is unfortunately needed because some code might
// call exit and bypass all the clean-up work without
// there ever being a real crash.
// This happens for instance with the "fatal IO error"
// of the X server.
atexit(SIG_HandleExit);
signal(SIGILL, Sig_HandleSignal);
signal(SIGIOT, Sig_HandleSignal);
signal(SIGBUS, Sig_HandleSignal);
signal(SIGFPE, Sig_HandleSignal);
signal(SIGSEGV, Sig_HandleSignal);
signal(SIGHUP, Sig_HandleSignal);
signal(SIGQUIT, Sig_HandleSignal);
signal(SIGTRAP, Sig_HandleSignal);
signal(SIGTERM, Sig_HandleSignal);
signal(SIGINT, Sig_HandleSignal);
atexit(Sig_HandleExit);
// The crash handler gets SA_NODEFER so that we can handle faults it generates.
Sig_RegisterSignals(sig_crashSignals, sig_crashSignalCount, Sig_HandleCrashSignal, SA_NODEFER);
Sig_RegisterSignals(sig_termSignals, sig_termSignalCount, Sig_HandleTermSignal, 0);
// Must do this now because it's not safe in a signal handler.
Sig_UpdateFilePaths();
Sig_Unwind_OpenLibrary();
}
void SIG_InitParent()
{
// Signals are sent to both the parent and the child, which can be an issue.
// Our parent process has to ignore all signals handled by the child and
// let the child do whatever needs to be done.
for (int i = 0; i < sig_crashSignalCount; ++i)
signal(sig_crashSignals[i], SIG_IGN);
for (int i = 0; i < sig_termSignalCount; ++i)
signal(sig_termSignals[i], SIG_IGN);
}
void SIG_Frame()
{
// Must do this now because it's not safe in a signal handler.
Sig_UpdateFilePaths();
if (sig_termRequested != 0)
Com_Quit(0);
}
// We require libunwind8 specifically for now.
// Even if there was a general symlink without the number,
// it would still not be safe to load it because
// the library provides no way to query its version.
#define LIBUNWIND_PATH "libunwind-" XSTRING(UNW_TARGET) ".so.8"
struct libUnwind_t
{
unw_context_t context;
int (*getcontext)(unw_context_t *);
int (*init_local)(unw_cursor_t *, unw_context_t *);
int (*step)(unw_cursor_t *);
int (*get_reg)(unw_cursor_t *, unw_regnum_t, unw_word_t *);
int (*get_proc_name)(unw_cursor_t *, char *, size_t, unw_word_t *);
void* handle;
qbool valid;
};
static libUnwind_t unw;
static void Sig_Unwind_GetContext()
{
if (!unw.valid)
return;
if (unw.getcontext(&unw.context) != 0)
{
Sig_PrintLine(STDERR_FILENO, "The call to libunwind's getcontext failed");
unw.valid = qfalse;
}
}
static int Sig_Unwind_GetFunction(void** func, const char* name)
{
*func = dlsym(unw.handle, name);
return *func != NULL;
}
static void Sig_Unwind_OpenLibrary()
{
// dlopen, dlsym, fprintf are not async-signal-safe
unw.valid = qfalse;
unw.handle = dlopen(LIBUNWIND_PATH, RTLD_NOW);
if (unw.handle == NULL)
{
const char* errorMsg = dlerror();
if (errorMsg != NULL)
fprintf(stderr, "\nFailed to load %s: %s\n", LIBUNWIND_PATH, errorMsg);
else
fprintf(stderr, "\nFailed to find/load %s\n", LIBUNWIND_PATH);
return;
}
#define GET2(Var, Name) \
if (!Sig_Unwind_GetFunction((void**)&unw.Var, Name)) \
{ \
fprintf(stderr, "\nFailed to find libunwind function %s\n", Name); \
return; \
}
#define GET(Name) GET2(Name, XSTRING(UNW_OBJ(Name)))
GET2(getcontext, "_Ux86_64_getcontext");
GET(init_local);
GET(step);
GET(get_reg);
GET(get_proc_name);
#undef GET
#undef GET2
unw.valid = qtrue;
}
static int Sig_Unwind_GetFileAndLine(unw_word_t addr, char* file, size_t flen, int* line)
{
static char buf[1024];
sprintf(buf, "addr2line -C -e %s -f -i %lx", q_argv[0], (unsigned long)addr);
FILE* f = popen(buf, "r");
if (f == NULL)
return 0;
fgets(buf, sizeof(buf), f); // function name
fgets(buf, sizeof(buf), f); // file and line
pclose(f);
if (buf[0] == '?')
return 0;
// file name before ':'
char* p = buf;
while (*p != ':')
++p;
*p = '\0';
// skip the folder names
char* name = strrchr(buf, '/');
name = name ? (name + 1) : buf;
// line number
++p;
Q_strncpyz(file, name, flen);
sscanf(p, "%d", line);
return 1;
}
void Sig_Unwind_Print(FILE* fp)
{
static char name[1024];
static char file[1024];
if (!unw.valid)
return;
unw_cursor_t cursor;
if (unw.init_local(&cursor, &unw.context) != 0)
{
fprintf(fp, "The call to libunwind's init_local failed\r\n");
return;
}
while (unw.step(&cursor) > 0)
{
const char* func = name;
unw_word_t ip, sp, offp;
if (unw.get_proc_name(&cursor, name, sizeof(name), &offp))
{
Q_strncpyz(name, "???", sizeof(name));
}
else
{
int status;
char* niceName = abi::__cxa_demangle(name, NULL, NULL, &status);
if (status == 0)
{
Q_strncpyz(name, niceName, sizeof(name));
free(niceName);
}
}
int line = 0;
unw.get_reg(&cursor, UNW_REG_IP, &ip);
if (Sig_Unwind_GetFileAndLine((long)ip, file, sizeof(file), &line))
fprintf(fp, "%s at %s:%d\n", name, file, line);
else
fprintf(fp, "%s\n", name);
}
}
// async-signal-safe
void Sig_Unwind_PrintASS(int fd)
{
static char name[1024];
if (!unw.valid)
return;
unw_cursor_t cursor;
if (unw.init_local(&cursor, &unw.context) != 0)
return;
while (unw.step(&cursor) > 0)
{
unw_word_t offp;
if (unw.get_proc_name(&cursor, name, sizeof(name), &offp))
Q_strncpyz(name, "???", sizeof(name));
Sig_PrintLine(fd, name);
}
}

View file

@ -877,6 +877,8 @@ static void Lin_HardRebootHandler( int argc, char** argv )
args[argc + 0] = (char*)"nohardreboot";
args[argc + 1] = NULL;
SIG_InitParent();
for (;;) {
if (Lin_RunProcess(args) == 0)
_exit(0); // quit without calling atexit handlers
@ -896,7 +898,7 @@ int main( int argc, char** argv )
Lin_HardRebootHandler(argc, argv);
#endif
SIG_Init();
SIG_InitChild();
// merge the command line: we need it in a single chunk
int len = 1, i;
@ -918,6 +920,7 @@ int main( int argc, char** argv )
Sys_ConsoleInputInit();
for (;;) {
SIG_Frame();
Com_Frame();
}

View file

@ -19,7 +19,7 @@ ifeq ($(config),debug_x32)
INCLUDES +=
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS +=
@ -46,7 +46,7 @@ ifeq ($(config),debug_x64)
INCLUDES +=
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS +=

View file

@ -22,7 +22,7 @@ ifeq ($(config),debug_x32)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -x c++ -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += ../../.build/debug_x32/libbotlib.a -ldl -lm -lbacktrace
LIBS += ../../.build/debug_x32/libbotlib.a -ldl -lm
LDDEPS += ../../.build/debug_x32/libbotlib.a
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/debug_x32 -m32
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
@ -53,7 +53,7 @@ ifeq ($(config),debug_x64)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -x c++ -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += ../../.build/debug_x64/libbotlib.a -ldl -lm -lbacktrace
LIBS += ../../.build/debug_x64/libbotlib.a -ldl -lm
LDDEPS += ../../.build/debug_x64/libbotlib.a
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/debug_x64 -m64
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
@ -84,7 +84,7 @@ ifeq ($(config),release_x32)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -x c++ -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += ../../.build/release_x32/libbotlib.a -ldl -lm -lbacktrace
LIBS += ../../.build/release_x32/libbotlib.a -ldl -lm
LDDEPS += ../../.build/release_x32/libbotlib.a
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/release_x32 -m32
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
@ -115,7 +115,7 @@ ifeq ($(config),release_x64)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -x c++ -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += ../../.build/release_x64/libbotlib.a -ldl -lm -lbacktrace
LIBS += ../../.build/release_x64/libbotlib.a -ldl -lm
LDDEPS += ../../.build/release_x64/libbotlib.a
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/release_x64 -m64
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)

View file

@ -22,7 +22,7 @@ ifeq ($(config),debug_x32)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -pthread -x c++ -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
LIBS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
LDDEPS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/debug_x32 -m32
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
@ -53,7 +53,7 @@ ifeq ($(config),debug_x64)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -pthread -x c++ -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
LIBS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
LDDEPS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/debug_x64 -m64
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
@ -84,7 +84,7 @@ ifeq ($(config),release_x32)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -pthread -x c++ -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
LIBS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
LDDEPS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/release_x32 -m32
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
@ -115,7 +115,7 @@ ifeq ($(config),release_x64)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -pthread -x c++ -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
LIBS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
LDDEPS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/release_x64 -m64
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)

View file

@ -19,7 +19,7 @@ ifeq ($(config),debug_x32)
INCLUDES += -I../../code/freetype/include
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS +=
@ -46,7 +46,7 @@ ifeq ($(config),debug_x64)
INCLUDES += -I../../code/freetype/include
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -std=c++98
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS +=

View file

@ -472,7 +472,7 @@ local function ApplyExeProjectSettings(exeName, server)
end
filter "system:not windows"
links { "dl", "m", "backtrace" }
links { "dl", "m" }
if (server == 0) then
buildoptions { "-pthread" }
links { "X11", "pthread" }
@ -655,7 +655,8 @@ solution "cnq3"
kind "WindowedApp"
language "C++"
ApplyExeProjectSettings("cnq3", 0)
buildoptions { "-std=c++98" }
filter "action:gmake"
buildoptions { "-std=c++98" }
project "cnq3-server"
@ -663,7 +664,8 @@ solution "cnq3"
language "C++"
defines { "DEDICATED" }
ApplyExeProjectSettings("cnq3-server", 1)
buildoptions { "-std=c++98" }
filter "action:gmake"
buildoptions { "-std=c++98" }
project "botlib"
@ -672,7 +674,8 @@ solution "cnq3"
defines { "BOTLIB" }
AddSourcesAndHeaders("botlib")
ApplyLibProjectSettings()
buildoptions { "-std=c++98" }
filter "action:gmake"
buildoptions { "-std=c++98" }
project "renderer"
@ -681,7 +684,8 @@ solution "cnq3"
AddSourcesAndHeaders("renderer")
includedirs { path_src.."/freetype/include" }
ApplyLibProjectSettings()
buildoptions { "-std=c++98" }
filter "action:gmake"
buildoptions { "-std=c++98" }
project "libjpeg-turbo"