From 7d4af5979bc3b3a37eea679bc09739af0f135002 Mon Sep 17 00:00:00 2001 From: olsner Date: Thu, 17 May 2007 03:37:49 +0000 Subject: [PATCH] Fixes for the Mac. # The engine now builds and runs on Mac OS X. Most changes were localized to the sysdep/ folder and the build system. This is what porting should be like! Hopefully nothing breaks on the other platforms though ;-) This was SVN commit r5081. --- build/premake/extern_libs.lua | 50 ++- build/premake/premake.lua | 18 +- build/premake/src/Src/gnu_cpp.c | 21 +- source/lib/byte_order.cpp | 4 +- source/lib/config.h | 2 +- source/lib/res/sound/ogghack.cpp | 4 + source/lib/secure_crt.cpp | 5 +- source/lib/sysdep/cpu.cpp | 8 +- source/lib/sysdep/ia32/ia32.inc | 6 +- source/lib/sysdep/unix/bsd.cpp | 11 +- source/lib/sysdep/unix/dir_watch_fam.cpp | 23 +- source/lib/sysdep/unix/ucpu.cpp | 14 + source/lib/sysdep/unix/udbg.cpp | 400 ++--------------------- source/lib/sysdep/unix/udbg_bfd.cpp | 382 ++++++++++++++++++++++ source/lib/sysdep/unix/unix.cpp | 2 +- 15 files changed, 538 insertions(+), 412 deletions(-) create mode 100644 source/lib/sysdep/unix/udbg_bfd.cpp diff --git a/build/premake/extern_libs.lua b/build/premake/extern_libs.lua index d7d7286b60..d4170b5cbf 100644 --- a/build/premake/extern_libs.lua +++ b/build/premake/extern_libs.lua @@ -9,6 +9,18 @@ -- directory in which all library subdirectories reside. libraries_dir = "../../../libraries/" +local function add_extern_lib_paths(extern_lib) + -- Add '//lib' and '/include' to the includepaths and libpaths + + -- Often, the headers in libraries/ are windows-specific (always, except + -- for cxxtest and fcollada). So don't add the include dir unless on + -- windows or processing one of those libs. + if OS == "windows" or extern_lib == 'cxxtest' or extern_lib == 'fcollada' then + tinsert(package.includepaths, libraries_dir .. extern_lib .. "/include") + end + tinsert(package.libpaths, libraries_dir .. extern_lib .. "/lib") + +end -- library definitions -- in a perfect world, libraries would have a common installation template, @@ -85,6 +97,7 @@ extern_lib_defs = { openal = { win_names = { "openal32" }, unix_names = { "openal" }, + osx_frameworks = { "OpenAL" }, dbg_suffix = "", }, opengl = { @@ -127,7 +140,13 @@ extern_lib_defs = { }, sdl = { - unix_names = { "SDL" }, + add_func = function() + add_extern_lib_paths("sdl") + if OS ~= "windows" then + tinsert(package.buildoptions, "`sdl-config --cflags`") + tinsert(package.linkoptions, "`sdl-config --libs`") + end + end } } @@ -159,14 +178,9 @@ local function add_delayload(name, suffix, def) end - local function add_extern_lib(extern_lib, def) - -- Add '//lib' and '/include' to the includepaths and libpaths - if OS ~= "linux" or extern_lib == 'cxxtest' or extern_lib == 'fcollada' then - tinsert(package.includepaths, libraries_dir .. extern_lib .. "/include") - end - tinsert(package.libpaths, libraries_dir .. extern_lib .. "/lib") + add_extern_lib_paths(extern_lib) -- careful: make sure to only use *_names when on the correct platform. local names = {} @@ -192,13 +206,21 @@ local function add_extern_lib(extern_lib, def) suffix = "" end - for i,name in names do - tinsert(package.config["Debug" ].links, name .. suffix) - -- 'Testing' config uses 'Debug' DLLs - tinsert(package.config["Testing"].links, name .. suffix) - tinsert(package.config["Release"].links, name) - - add_delayload(name, suffix, def) + -- OS X "Frameworks" need to be added in a special way to the link + -- i.e. by linkoptions += "-framework ..." + if OS == "macosx" and def.osx_frameworks then + for i,name in def.osx_frameworks do + tinsert(package.linkoptions, "-framework " .. name) + end + else + for i,name in names do + tinsert(package.config["Debug" ].links, name .. suffix) + -- 'Testing' config uses 'Debug' DLLs + tinsert(package.config["Testing"].links, name .. suffix) + tinsert(package.config["Release"].links, name) + + add_delayload(name, suffix, def) + end end end diff --git a/build/premake/premake.lua b/build/premake/premake.lua index c10c8ed036..a211711b03 100755 --- a/build/premake/premake.lua +++ b/build/premake/premake.lua @@ -118,16 +118,26 @@ function package_set_build_flags() "-fvisibility-inlines-hidden", }) + -- Include and lib paths: + -- X11 includes may be installed in one of a gaszillion of three places + -- And MacPorts uses /opt/local as its prefix + -- Famous last words: "You can't include too much! ;-)" + package.includepaths = { "/usr/X11R6/include/X11", + "/usr/X11R6/include", "/usr/include/X11", + "/opt/local/include", } package.libpaths = { - "/usr/X11R6/lib", - "/usr/i686-pc-linux-gnu/lib", -- needed for ICC to find libbfd + "/opt/local/lib", + "/usr/X11R6/lib" } + if OS=="linux" and options["icc"] then + tinsert(package.libpaths, + "/usr/i686-pc-linux-gnu/lib") -- needed for ICC to find libbfd + end package.defines = { - "__STDC_VERSION__=199901L", -- "CONFIG_USE_MMGR", } end @@ -385,7 +395,7 @@ function setup_all_libs () linux = { "lib/sysdep/unix" }, -- note: RC file must be added to main_exe package. windows = { "lib/sysdep/win", "lib/sysdep/win/wposix" }, - macosx = { "lib/sysdep/osx" }, + macosx = { "lib/sysdep/osx", "lib/sysdep/unix" }, } tinsert(package.files, sourcesfromdirs(source_root, sysdep_dirs[OS])); end diff --git a/build/premake/src/Src/gnu_cpp.c b/build/premake/src/Src/gnu_cpp.c index 9a9db53fce..778fe77dee 100644 --- a/build/premake/src/Src/gnu_cpp.c +++ b/build/premake/src/Src/gnu_cpp.c @@ -311,8 +311,16 @@ int gnu_cpp() if (!prj_is_kind("cxxtestgen")) { /* Include the automatically generated dependency lists */ - io_print("-include $(OBJECTS:%%.o=%%.d)\n\n"); - io_print("-include $(PCHS:%%.%s=%%.d)\n\n", pchExt); + /* But skip that if OBJECTS would have been empty - make doesn't like + * empty include lists */ + if (*prj_get_files()) + io_print("-include $(OBJECTS:%%.o=%%.d)\n\n"); + + /* Include the dependency list for the precompiled header. But skip + * it if we're not doing PCH's, to get rid of "no file name for + * -include" warnings from make */ + if (prj_get_pch_source() != NULL) + io_print("-include $(PCHS:%%.%s=%%.d)\n\n", pchExt); } io_closefile(); @@ -477,12 +485,17 @@ static const char* listCppTargets(const char* name) strcat(input_dir, "/"); opts = ""; - if (!os_is("windows")) + if (!os_is("windows") && !os_is("macosx")) opts = "-dDONT_USE_UNDERLINE=1 "; strcat(g_buffer, "nasm "); strcat(g_buffer, opts); strcat(g_buffer, " -i"); strcat(g_buffer,input_dir ); - strcat(g_buffer, " -f elf -o $@ $<\n"); + strcat(g_buffer, " -f "); + if (os_is("macosx")) + strcat(g_buffer, "macho"); + else + strcat(g_buffer, "elf"); + strcat(g_buffer, " -o $@ $<\n"); strcat(g_buffer, "\t"); if (!g_verbose) diff --git a/source/lib/byte_order.cpp b/source/lib/byte_order.cpp index 45b68b761c..ca7c67891a 100644 --- a/source/lib/byte_order.cpp +++ b/source/lib/byte_order.cpp @@ -167,7 +167,7 @@ void write_be64(void* p, u64 x) u64 movzx_le64(const u8* p, size_t size_bytes) { u64 number = 0; - for(uint i = 0; i < std::min(size_bytes, 8u); i++) + for(uint i = 0; i < std::min(size_bytes, (size_t)8u); i++) number |= ((u64)p[i]) << (i*8); return number; @@ -176,7 +176,7 @@ u64 movzx_le64(const u8* p, size_t size_bytes) u64 movzx_be64(const u8* p, size_t size_bytes) { u64 number = 0; - for(uint i = 0; i < std::min(size_bytes, 8u); i++) + for(uint i = 0; i < std::min(size_bytes, (size_t)8u); i++) { number <<= 8; number |= p[i]; diff --git a/source/lib/config.h b/source/lib/config.h index 398c7f51df..a6321d20c1 100644 --- a/source/lib/config.h +++ b/source/lib/config.h @@ -408,7 +408,7 @@ // safe CRT functions: strcpy_s, fopen_s, etc. // these are always available to users: if not provided by the CRT, we // implement them ourselves. this flag is only used to skip our impl. -#if MSC_VERSION >= 1400 || GCC_VERSION +#if MSC_VERSION >= 1400 # define HAVE_SECURE_CRT 1 #else # define HAVE_SECURE_CRT 0 diff --git a/source/lib/res/sound/ogghack.cpp b/source/lib/res/sound/ogghack.cpp index 63d1b09e63..f21fdc826d 100644 --- a/source/lib/res/sound/ogghack.cpp +++ b/source/lib/res/sound/ogghack.cpp @@ -6,7 +6,11 @@ #include #include +#ifdef __APPLE__ +#include +#else #include +#endif #include #include "ogghack.h" // HACK: must be included after al.h (bad interface) diff --git a/source/lib/secure_crt.cpp b/source/lib/secure_crt.cpp index 2d195da4a1..75e7aed537 100644 --- a/source/lib/secure_crt.cpp +++ b/source/lib/secure_crt.cpp @@ -222,7 +222,9 @@ int tprintf_s(tchar* buf, size_t max_chars, const tchar* fmt, ...) return len; } - +#if OS_WIN || !defined(WSECURE_CRT) +// FIXME this doesn't work in the wchar_t version, for the platforms where it's +// supposed to do good, since wfopen is microsoft-specific. errno_t tfopen_s(FILE** pfile, const tchar* filename, const tchar* mode) { *pfile = NULL; @@ -232,5 +234,6 @@ errno_t tfopen_s(FILE** pfile, const tchar* filename, const tchar* mode) *pfile = file; return 0; } +#endif #endif // #if !HAVE_SECURE_CRT diff --git a/source/lib/sysdep/cpu.cpp b/source/lib/sysdep/cpu.cpp index 31a776f776..c41faefc3e 100644 --- a/source/lib/sysdep/cpu.cpp +++ b/source/lib/sysdep/cpu.cpp @@ -17,7 +17,7 @@ # include "lib/sysdep/ia32/ia32.h" # include "lib/sysdep/ia32/ia32_memcpy.h" #endif -#if OS_BSD +#if OS_BSD || OS_MACOSX # include "lib/sysdep/unix/bsd.h" #endif #if OS_WIN @@ -249,6 +249,10 @@ LibError cpu_CallByEachCPU(CpuCallback cb, void* param) { #if OS_WIN return wcpu_CallByEachCPU(cb, param); +#else + UNUSED2(cb); + UNUSED2(param); + return ERR::NOT_IMPLEMENTED; #endif } @@ -309,4 +313,4 @@ size_t cpu_MemorySize(CpuMemoryIndicators mem_type) #else return bsd_MemorySize(mem_type); #endif -} \ No newline at end of file +} diff --git a/source/lib/sysdep/ia32/ia32.inc b/source/lib/sysdep/ia32/ia32.inc index 22f742d55f..8434dead91 100644 --- a/source/lib/sysdep/ia32/ia32.inc +++ b/source/lib/sysdep/ia32/ia32.inc @@ -1,8 +1,8 @@ ; set section attributes -section .data data align=32 use32 -section .bss bss align=16 use32 -section .text code align=64 use32 +; section .data data align=32 use32 +; section .bss bss align=16 use32 +; section .text code align=64 use32 ; activate .text (needs to be separate because __SECT__ will otherwise ; complain that the above definition is redeclaring attributes) section .text diff --git a/source/lib/sysdep/unix/bsd.cpp b/source/lib/sysdep/unix/bsd.cpp index c4700d9c52..b0dc01e854 100644 --- a/source/lib/sysdep/unix/bsd.cpp +++ b/source/lib/sysdep/unix/bsd.cpp @@ -1,15 +1,17 @@ #include "precompiled.h" #include "bsd.h" -#if OS_BSD +#if OS_BSD || OS_MACOSX + +#include static int SysctlFromMemType(CpuMemoryIndicators mem_type) { switch(mem_type) { - case MEM_TOTAL: + case CPU_MEM_TOTAL: return HW_PHYSMEM; - case MEM_AVAILABLE: + case CPU_MEM_AVAILABLE: return HW_USERMEM; } UNREACHABLE; @@ -19,7 +21,8 @@ size_t bsd_MemorySize(CpuMemoryIndicators mem_type) { size_t memory_size = 0; size_t len = sizeof(memory_size); - const int mib[2] = { CTL_HW, SysctlFromMemType(mem_type) }; + // Argh, the API doesn't seem to be const-correct + /*const*/ int mib[2] = { CTL_HW, SysctlFromMemType(mem_type) }; sysctl(mib, 2, &memory_size, &len, 0, 0); return memory_size; } diff --git a/source/lib/sysdep/unix/dir_watch_fam.cpp b/source/lib/sysdep/unix/dir_watch_fam.cpp index dcb4398d33..849a865788 100644 --- a/source/lib/sysdep/unix/dir_watch_fam.cpp +++ b/source/lib/sysdep/unix/dir_watch_fam.cpp @@ -5,8 +5,28 @@ #include "lib/res/file/file.h" #include "lib/sysdep/sysdep.h" +#include "lib/sysdep/dir_watch.h" #include "ps/CLogger.h" +#if !OS_LINUX + +LibError dir_add_watch(const char * const n_full_path, intptr_t* const watch) +{ + return INFO::OK; +} + +LibError dir_cancel_watch(const intptr_t watch) +{ + return INFO::OK; +} + +LibError dir_get_changed_file(char *) +{ + return INFO::OK; +} + +#else + #include static FAMConnection fc; @@ -73,7 +93,7 @@ LibError dir_cancel_watch(const intptr_t watch) return INFO::OK; } -int dir_get_changed_file(char* fn) +LibError dir_get_changed_file(char* fn) { if(initialized == -1) return ERR::FAIL; // NOWARN @@ -99,3 +119,4 @@ int dir_get_changed_file(char* fn) // just nothing new; try again later return ERR::AGAIN; // NOWARN } +#endif diff --git a/source/lib/sysdep/unix/ucpu.cpp b/source/lib/sysdep/unix/ucpu.cpp index 933922f550..fee26ccd1e 100644 --- a/source/lib/sysdep/unix/ucpu.cpp +++ b/source/lib/sysdep/unix/ucpu.cpp @@ -2,6 +2,10 @@ #include "ucpu.h" +#if OS_MACOSX +#include +#endif + int ucpu_IsThrottlingPossible() { return -1; // don't know @@ -9,11 +13,21 @@ int ucpu_IsThrottlingPossible() int ucpu_NumPackages() { +#if OS_MACOSX + int mib[]={CTL_HW, HW_NCPU}; + int ncpus; + size_t len = sizeof(ncpus); + if (sysctl(mib, 2, &ncpus, &len, NULL, 0) == -1) + return -1; // don't know + else + return ncpus; +#else long res = sysconf(_SC_NPROCESSORS_CONF); if (res == -1) return 1; else return (int)res; +#endif } double ucpu_ClockFrequency() diff --git a/source/lib/sysdep/unix/udbg.cpp b/source/lib/sysdep/unix/udbg.cpp index 33f302ad0a..35856fdc33 100644 --- a/source/lib/sysdep/unix/udbg.cpp +++ b/source/lib/sysdep/unix/udbg.cpp @@ -1,77 +1,44 @@ +/* udbg.cpp + +This file contains debug helpers that are common for all unix systems. See +udbg_bfd.cpp for the linux-specific stuff (Using BFD and backtrace() for symbol +lookups and backtraces) +*/ + #include "precompiled.h" #include "lib/timer.h" #include "lib/sysdep/sysdep.h" #include "lib/debug.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifndef bfd_get_section_size -#define bfd_get_section_size bfd_get_section_size_before_reloc -#endif - -#if OS_LINUX -#define GNU_SOURCE -#include -#include -#endif - -#if OS_LINUX -# define DEBUGGER_WAIT 3 -# define DEBUGGER_CMD "gdb" -# define DEBUGGER_ARG_FORMAT "--pid=%d" -# define DEBUGGER_BREAK_AFTER_WAIT 0 -#else -# error "port" -#endif - -#define PROFILE_RESOLVE_SYMBOL 0 - -// Hard-coded - yuck :P -// These should only be used as fallbacks -#if defined(TESTING) -#define EXE_NAME "pyrogenesis_test" -#elif defined(NDEBUG) -#define EXE_NAME "pyrogenesis" -#else -#define EXE_NAME "pyrogenesis_dbg" -#endif - -struct symbol_file_context +#if !OS_LINUX +// udbg stubs implementation. This is what's used on non-linux, so far. +void* debug_get_nth_caller(uint UNUSED(n), void *UNUSED(context)) { - asymbol **syms; - bfd *abfd; -}; -symbol_file_context ps_dbg_context; -bool udbg_initialized=false; + return NULL; +} -struct symbol_lookup_context +LibError debug_dump_stack(wchar_t* UNUSED(buf), size_t UNUSED(max_chars), uint UNUSED(skip), void* UNUSED(context)) { - symbol_file_context *file_ctx; + return ERR::NOT_IMPLEMENTED; +} - bfd_vma address; - const char* symbol; - const char* filename; - uint line; - - bool found; -}; +LibError debug_resolve_symbol(void* UNUSED(ptr_of_interest), char* UNUSED(sym_name), char* UNUSED(file), int* UNUSED(line)) +{ + return ERR::NOT_IMPLEMENTED; +} +#endif void unix_debug_break() { kill(getpid(), SIGTRAP); } +#define DEBUGGER_WAIT 3 +#define DEBUGGER_CMD "gdb" +#define DEBUGGER_ARG_FORMAT "--pid=%d" +#define DEBUGGER_BREAK_AFTER_WAIT 0 + /* Start the debugger and tell it to attach to the current process/thread (called by display_error) @@ -105,329 +72,12 @@ void udbg_launch_debugger() } } -void* debug_get_nth_caller(uint n, void *UNUSED(context)) -{ - // bt[0] == debug_get_nth_caller - // bt[1] == caller of get_nth_caller - // bt[2] == 1:st caller (n==1) - void *bt[n+2]; - int bt_size; - - bt_size=backtrace(bt, n+2); - assert(bt_size >= (int)(n+2) && "Need at least n+2 frames in get_nth_caller"); - return bt[n+1]; // n==1 => bt[2], and so forth -} - -LibError debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* UNUSED(context)) -{ - ++skip; // Skip ourselves too - - // bt[0..skip] == skipped - // bt[skip..N_FRAMES+skip] == print - static const uint N_FRAMES = 16; - void *bt[skip+N_FRAMES]; - int bt_size; - wchar_t *bufpos = buf; - wchar_t *bufend = buf + max_chars; - - bt_size=backtrace(bt, ARRAY_SIZE(bt)); - // did we get enough backtraced frames? - assert((bt_size >= (int)skip) && "Need at least frames in the backtrace"); - - // Assumed max length of a single print-out - static const uint MAX_OUT_CHARS=1024; - - for (uint i=skip;(int)iabfd; - asymbol ***syms=&ctx->syms; - long symcount; - int size=0; - - if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0) - { - printf("slurp_symtab(): Huh? Has no symbols...\n"); - return -1; - } - - size = bfd_get_symtab_upper_bound(abfd); - if (size < 0) - { - bfd_perror("symtab_upper_bound"); - return -1; - } - *syms = (asymbol **)malloc(size); - if (!syms) - return -1; - symcount = bfd_canonicalize_symtab(abfd, *syms); - - if (symcount == 0) - symcount = bfd_read_minisymbols (abfd, FALSE, (void **)syms, (uint *)&size); - if (symcount == 0) - symcount = bfd_read_minisymbols (abfd, TRUE /* dynamic */, (void **)syms, (uint *)&size); - - if (symcount < 0) - { - bfd_perror("slurp_symtab"); - return -1; - } - - return 0; -} - -static int read_symbols(const char *file_name, symbol_file_context *ctx) -{ - char **matching=NULL; - - ONCE(bfd_init()); - - ctx->abfd = bfd_openr (file_name, NULL); - if (ctx->abfd == NULL) - { - bfd_perror("udbg.cpp: bfd_openr"); - return -1; - } - - if (! bfd_check_format_matches (ctx->abfd, bfd_object, &matching)) - { - if (bfd_get_error () == bfd_error_file_ambiguously_recognized) - { - printf("Error reading symbols from %s: ambiguous format\n", file_name); - while (*matching) - printf("\tPotential matching format: %s\n", *matching++); - free(matching); - } - else - { - bfd_perror("bfd_check_format_matches"); - } - return -1; - } - - int res=slurp_symtab(ctx); - if (res == 0) - return res; - else - { - bfd_perror("udbg.cpp: slurp_symtab"); - bfd_close(ctx->abfd); - return -1; - } -} - -void udbg_init(void) -{ - char n_path[PATH_MAX]; - char *exename=n_path; - if (sys_get_executable_name(n_path, sizeof(n_path)) != INFO::OK) - { - debug_printf("sys_get_executable_name didn't work, using hard-coded guess %s.\n", EXE_NAME); - exename=EXE_NAME; - } - - debug_printf("udbg_init: loading symbols from %s.\n", exename); - - if (read_symbols(exename, &ps_dbg_context)==0) - udbg_initialized=true; - -#if PROFILE_RESOLVE_SYMBOL - { - TIMER(udbg_init_benchmark) - char symbol[DBG_SYMBOL_LEN]; - char file[DBG_FILE_LEN]; - int line; - debug_resolve_symbol(debug_get_nth_caller(3), symbol, file, &line); - printf("%s (%s:%d)\n", symbol, file, line); - for (int i=0;i<1000000;i++) - { - debug_resolve_symbol(debug_get_nth_caller(1), symbol, file, &line); - } - } -#endif -} - -static void find_address_in_section (bfd *abfd, asection *section, void *data) -{ - symbol_lookup_context *ctx=(symbol_lookup_context *)data; - asymbol **syms=ctx->file_ctx->syms; - - bfd_vma pc=ctx->address; - bfd_vma vma; - bfd_size_type size; - - if (ctx->found) return; - - if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0) - return; - - vma = bfd_get_section_vma (abfd, section); - if (pc < vma) - return; - - size = bfd_get_section_size (section); - if (pc >= vma + size) - return; - - ctx->found = bfd_find_nearest_line (abfd, section, syms, - pc - vma, &ctx->filename, &ctx->symbol, &ctx->line); -} - -// BFD functions perform allocs with real malloc - we need to free that data -#include "lib/nommgr.h" -void demangle_buf(char *buf, const char *symbol, size_t n) -{ - int status=0; - char *alloc=NULL; - if (symbol == NULL || *symbol == '\0') - { - symbol = "??"; - status = -1; - } - else - { - alloc=abi::__cxa_demangle(symbol, NULL, NULL, &status); - } - // status is 0 on success and a negative value on failure - if (status == 0) - symbol=alloc; - strncpy(buf, symbol, n); - buf[n-1]=0; - if (alloc) - free(alloc); -} - -static LibError debug_resolve_symbol_dladdr(void *ptr, char* sym_name, char* file, int* line) -{ - Dl_info syminfo; - - int res=dladdr(ptr, &syminfo); - if (res == 0) - WARN_RETURN(ERR::FAIL); - - if (sym_name) - { - if (syminfo.dli_sname) - demangle_buf(sym_name, syminfo.dli_sname, DBG_SYMBOL_LEN); - else - { - snprintf(sym_name, DBG_SYMBOL_LEN, "0x%08x", (uint)ptr); - sym_name[DBG_SYMBOL_LEN-1]=0; - } - } - - if (file) - { - strncpy(file, syminfo.dli_fname, DBG_FILE_LEN); - file[DBG_FILE_LEN-1]=0; - } - - if (line) - { - *line=0; - } - - return INFO::OK; -} - -LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line) -{ - ONCE(udbg_init()); - - // We use our default context - in the future we could do something like - // mapping library -> file context to support more detailed reporting on - // external libraries - symbol_file_context *file_ctx=&ps_dbg_context; - bfd *abfd=file_ctx->abfd; - - // Reset here if we fail later on - if (sym_name) - *sym_name=0; - if (file) - *file=0; - if (line) - *line=0; - - if (!udbg_initialized) - return debug_resolve_symbol_dladdr(ptr_of_interest, sym_name, file, line); - - symbol_lookup_context ctx; - memset(&ctx, 0, sizeof(ctx)); - ctx.address=reinterpret_cast(ptr_of_interest); - ctx.file_ctx=file_ctx; - - bfd_map_over_sections (abfd, find_address_in_section, &ctx); - - // This will happen for addresses in external files. What one *could* do - // here is to figure out the originating library file and load that through - // BFD... but how often will external libraries have debugging info? really? - // At least attempt to find out the symbol name through dladdr. - if (!ctx.found) - return debug_resolve_symbol_dladdr(ptr_of_interest, sym_name, file, line); - - if (sym_name) - { - demangle_buf(sym_name, ctx.symbol, DBG_SYMBOL_LEN); - } - - if (file) - { - if (ctx.filename != NULL) - { - const char *h; - - h = strrchr (ctx.filename, '/'); - if (h != NULL) - ctx.filename = h + 1; - - strncpy(file, ctx.filename, DBG_FILE_LEN); - file[DBG_FILE_LEN]=0; - } - else - strcpy(file, "none"); - } - - if (line) - { - *line = ctx.line; - } - - return INFO::OK; -} - void debug_puts(const char* text) { fputs(text, stdout); fflush(stdout); } - // TODO: Do these properly. (I don't know what I'm doing; I just // know that these functions are required in order to compile...) diff --git a/source/lib/sysdep/unix/udbg_bfd.cpp b/source/lib/sysdep/unix/udbg_bfd.cpp new file mode 100644 index 0000000000..a1c4eb134e --- /dev/null +++ b/source/lib/sysdep/unix/udbg_bfd.cpp @@ -0,0 +1,382 @@ +#include "precompiled.h" + +// Only use BFD on linux (where it is supported) +#if OS_LINUX + +#include "lib/timer.h" +#include "lib/sysdep/sysdep.h" +#include "lib/debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef bfd_get_section_size +#define bfd_get_section_size bfd_get_section_size_before_reloc +#endif + +// This is a no-op. But we *could* use the BFD stuff on other platforms too, if +// we saw the need for it +#if OS_LINUX +#define GNU_SOURCE +#include +#include +#endif + +// Hard-coded - yuck :P +// These should only be used as fallbacks +#if defined(TESTING) +#define EXE_NAME "pyrogenesis_test" +#elif defined(NDEBUG) +#define EXE_NAME "pyrogenesis" +#else +#define EXE_NAME "pyrogenesis_dbg" +#endif + +#define PROFILE_RESOLVE_SYMBOL 0 + +struct symbol_file_context +{ + asymbol **syms; + bfd *abfd; +}; +symbol_file_context ps_dbg_context; +bool udbg_initialized=false; + +struct symbol_lookup_context +{ + symbol_file_context *file_ctx; + + bfd_vma address; + const char* symbol; + const char* filename; + uint line; + + bool found; +}; + +void* debug_get_nth_caller(uint n, void *UNUSED(context)) +{ + // bt[0] == debug_get_nth_caller + // bt[1] == caller of get_nth_caller + // bt[2] == 1:st caller (n==1) + void *bt[n+2]; + int bt_size; + + bt_size=backtrace(bt, n+2); + assert(bt_size >= (int)(n+2) && "Need at least n+2 frames in get_nth_caller"); + return bt[n+1]; // n==1 => bt[2], and so forth +} + +LibError debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* UNUSED(context)) +{ + ++skip; // Skip ourselves too + + // bt[0..skip] == skipped + // bt[skip..N_FRAMES+skip] == print + static const uint N_FRAMES = 16; + void *bt[skip+N_FRAMES]; + int bt_size=0; + wchar_t *bufpos = buf; + wchar_t *bufend = buf + max_chars; + + bt_size=backtrace(bt, ARRAY_SIZE(bt)); + // did we get enough backtraced frames? + assert((bt_size >= (int)skip) && "Need at least frames in the backtrace"); + + // Assumed max length of a single print-out + static const uint MAX_OUT_CHARS=1024; + + for (uint i=skip;(int)iabfd; + asymbol ***syms=&ctx->syms; + long symcount; + int size=0; + + if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0) + { + printf("slurp_symtab(): Huh? Has no symbols...\n"); + return -1; + } + + size = bfd_get_symtab_upper_bound(abfd); + if (size < 0) + { + bfd_perror("symtab_upper_bound"); + return -1; + } + *syms = (asymbol **)malloc(size); + if (!syms) + return -1; + symcount = bfd_canonicalize_symtab(abfd, *syms); + + if (symcount == 0) + symcount = bfd_read_minisymbols (abfd, FALSE, (void **)syms, (uint *)&size); + if (symcount == 0) + symcount = bfd_read_minisymbols (abfd, TRUE /* dynamic */, (void **)syms, (uint *)&size); + + if (symcount < 0) + { + bfd_perror("slurp_symtab"); + return -1; + } + + return 0; +} + +static int read_symbols(const char *file_name, symbol_file_context *ctx) +{ + char **matching=NULL; + + ONCE(bfd_init()); + + ctx->abfd = bfd_openr (file_name, NULL); + if (ctx->abfd == NULL) + { + bfd_perror("udbg.cpp: bfd_openr"); + return -1; + } + + if (! bfd_check_format_matches (ctx->abfd, bfd_object, &matching)) + { + if (bfd_get_error () == bfd_error_file_ambiguously_recognized) + { + printf("Error reading symbols from %s: ambiguous format\n", file_name); + while (*matching) + printf("\tPotential matching format: %s\n", *matching++); + free(matching); + } + else + { + bfd_perror("bfd_check_format_matches"); + } + return -1; + } + + int res=slurp_symtab(ctx); + if (res == 0) + return res; + else + { + bfd_perror("udbg.cpp: slurp_symtab"); + bfd_close(ctx->abfd); + return -1; + } +} + +void udbg_bfd_init(void) +{ + char n_path[PATH_MAX]; + char *exename=n_path; + if (sys_get_executable_name(n_path, sizeof(n_path)) != INFO::OK) + { + debug_printf("sys_get_executable_name didn't work, using hard-coded guess %s.\n", EXE_NAME); + exename=EXE_NAME; + } + + debug_printf("udbg_init: loading symbols from %s.\n", exename); + + if (read_symbols(exename, &ps_dbg_context)==0) + udbg_initialized=true; + +#if PROFILE_RESOLVE_SYMBOL + { + TIMER(udbg_init_benchmark) + char symbol[DBG_SYMBOL_LEN]; + char file[DBG_FILE_LEN]; + int line; + debug_resolve_symbol(debug_get_nth_caller(3), symbol, file, &line); + printf("%s (%s:%d)\n", symbol, file, line); + for (int i=0;i<1000000;i++) + { + debug_resolve_symbol(debug_get_nth_caller(1), symbol, file, &line); + } + } +#endif +} + +static void find_address_in_section (bfd *abfd, asection *section, void *data) +{ + symbol_lookup_context *ctx=(symbol_lookup_context *)data; + asymbol **syms=ctx->file_ctx->syms; + + bfd_vma pc=ctx->address; + bfd_vma vma; + bfd_size_type size; + + if (ctx->found) return; + + if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0) + return; + + vma = bfd_get_section_vma (abfd, section); + if (pc < vma) + return; + + size = bfd_get_section_size (section); + if (pc >= vma + size) + return; + + ctx->found = bfd_find_nearest_line (abfd, section, syms, + pc - vma, &ctx->filename, &ctx->symbol, &ctx->line); +} + +// BFD functions perform allocs with real malloc - we need to free that data +#include "lib/nommgr.h" +void demangle_buf(char *buf, const char *symbol, size_t n) +{ + int status=0; + char *alloc=NULL; + if (symbol == NULL || *symbol == '\0') + { + symbol = "??"; + status = -1; + } + else + { + alloc=abi::__cxa_demangle(symbol, NULL, NULL, &status); + } + // status is 0 on success and a negative value on failure + if (status == 0) + symbol=alloc; + strncpy(buf, symbol, n); + buf[n-1]=0; + if (alloc) + free(alloc); +} + +static LibError debug_resolve_symbol_dladdr(void *ptr, char* sym_name, char* file, int* line) +{ + Dl_info syminfo; + + int res=dladdr(ptr, &syminfo); + if (res == 0) + WARN_RETURN(ERR::FAIL); + + if (sym_name) + { + if (syminfo.dli_sname) + demangle_buf(sym_name, syminfo.dli_sname, DBG_SYMBOL_LEN); + else + { + snprintf(sym_name, DBG_SYMBOL_LEN, "0x%08x", (uint)ptr); + sym_name[DBG_SYMBOL_LEN-1]=0; + } + } + + if (file) + { + strncpy(file, syminfo.dli_fname, DBG_FILE_LEN); + file[DBG_FILE_LEN-1]=0; + } + + if (line) + { + *line=0; + } + + return INFO::OK; +} + +LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line) +{ + ONCE(udbg_init()); + + // We use our default context - in the future we could do something like + // mapping library -> file context to support more detailed reporting on + // external libraries + symbol_file_context *file_ctx=&ps_dbg_context; + bfd *abfd=file_ctx->abfd; + + // Reset here if we fail later on + if (sym_name) + *sym_name=0; + if (file) + *file=0; + if (line) + *line=0; + + if (!udbg_initialized) + return debug_resolve_symbol_dladdr(ptr_of_interest, sym_name, file, line); + + symbol_lookup_context ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.address=reinterpret_cast(ptr_of_interest); + ctx.file_ctx=file_ctx; + + bfd_map_over_sections (abfd, find_address_in_section, &ctx); + + // This will happen for addresses in external files. What one *could* do + // here is to figure out the originating library file and load that through + // BFD... but how often will external libraries have debugging info? really? + // At least attempt to find out the symbol name through dladdr. + if (!ctx.found) + return debug_resolve_symbol_dladdr(ptr_of_interest, sym_name, file, line); + + if (sym_name) + { + demangle_buf(sym_name, ctx.symbol, DBG_SYMBOL_LEN); + } + + if (file) + { + if (ctx.filename != NULL) + { + const char *h; + + h = strrchr (ctx.filename, '/'); + if (h != NULL) + ctx.filename = h + 1; + + strncpy(file, ctx.filename, DBG_FILE_LEN); + file[DBG_FILE_LEN]=0; + } + else + strcpy(file, "none"); + } + + if (line) + { + *line = ctx.line; + } + + return INFO::OK; +} + +#endif \ No newline at end of file diff --git a/source/lib/sysdep/unix/unix.cpp b/source/lib/sysdep/unix/unix.cpp index a0191532d8..8494b5eeac 100644 --- a/source/lib/sysdep/unix/unix.cpp +++ b/source/lib/sysdep/unix/unix.cpp @@ -4,7 +4,7 @@ #include #include -#include "lib/sdl.h" +#include "lib/external_libraries/sdl.h" #include "lib/sysdep/sysdep.h" #include "udbg.h"