diff --git a/code/libunwind/libunwind-common.h b/code/libunwind/libunwind-common.h new file mode 100644 index 0000000..0ea4118 --- /dev/null +++ b/code/libunwind/libunwind-common.h @@ -0,0 +1,257 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +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; diff --git a/code/libunwind/libunwind-coredump.h b/code/libunwind/libunwind-coredump.h new file mode 100644 index 0000000..d2b05e7 --- /dev/null +++ b/code/libunwind/libunwind-coredump.h @@ -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 + +#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 */ diff --git a/code/libunwind/libunwind-dynamic.h b/code/libunwind/libunwind-dynamic.h new file mode 100644 index 0000000..584f392 --- /dev/null +++ b/code/libunwind/libunwind-dynamic.h @@ -0,0 +1,210 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2002-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +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 diff --git a/code/libunwind/libunwind-ptrace.h b/code/libunwind/libunwind-ptrace.h new file mode 100644 index 0000000..7fca205 --- /dev/null +++ b/code/libunwind/libunwind-ptrace.h @@ -0,0 +1,63 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +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 + +#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 */ diff --git a/code/libunwind/libunwind-x86_64.h b/code/libunwind/libunwind-x86_64.h new file mode 100644 index 0000000..526b1ea --- /dev/null +++ b/code/libunwind/libunwind-x86_64.h @@ -0,0 +1,139 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2002-2004 Hewlett-Packard Co + Contributed by David Mosberger-Tang + + Modified for x86_64 by Max Asbock + +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 +#include +#include + +#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 */ diff --git a/code/libunwind/libunwind.h b/code/libunwind/libunwind.h new file mode 100644 index 0000000..808b2ae --- /dev/null +++ b/code/libunwind/libunwind.h @@ -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 */ diff --git a/code/qcommon/crash.cpp b/code/qcommon/crash.cpp index 0b3bd6d..fd5474f 100644 --- a/code/qcommon/crash.cpp +++ b/code/qcommon/crash.cpp @@ -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 + +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 diff --git a/code/qcommon/crash.h b/code/qcommon/crash.h index 5945f69..6813728 100644 --- a/code/qcommon/crash.h +++ b/code/qcommon/crash.h @@ -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 diff --git a/code/unix/linux_local.h b/code/unix/linux_local.h index 51d88cb..b609ee4 100644 --- a/code/unix/linux_local.h +++ b/code/unix/linux_local.h @@ -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; diff --git a/code/unix/linux_signals.cpp b/code/unix/linux_signals.cpp index f3d10bf..beb0591 100644 --- a/code/unix/linux_signals.cpp +++ b/code/unix/linux_signals.cpp @@ -22,6 +22,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#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); + } } diff --git a/code/unix/unix_main.cpp b/code/unix/unix_main.cpp index e6ee243..b1d0a80 100644 --- a/code/unix/unix_main.cpp +++ b/code/unix/unix_main.cpp @@ -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(); } diff --git a/makefiles/gmake/botlib.make b/makefiles/gmake/botlib.make index 10ba5be..6faea70 100644 --- a/makefiles/gmake/botlib.make +++ b/makefiles/gmake/botlib.make @@ -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 += diff --git a/makefiles/gmake/cnq3-server.make b/makefiles/gmake/cnq3-server.make index 080be68..7a0005e 100644 --- a/makefiles/gmake/cnq3-server.make +++ b/makefiles/gmake/cnq3-server.make @@ -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) diff --git a/makefiles/gmake/cnq3.make b/makefiles/gmake/cnq3.make index 2ba56eb..66bda8a 100644 --- a/makefiles/gmake/cnq3.make +++ b/makefiles/gmake/cnq3.make @@ -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) diff --git a/makefiles/gmake/renderer.make b/makefiles/gmake/renderer.make index bccf7f0..d21e876 100644 --- a/makefiles/gmake/renderer.make +++ b/makefiles/gmake/renderer.make @@ -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 += diff --git a/makefiles/premake5.lua b/makefiles/premake5.lua index f18b09f..3429c9e 100644 --- a/makefiles/premake5.lua +++ b/makefiles/premake5.lua @@ -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"