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.
This commit is contained in:
parent
b01acce45f
commit
7d4af5979b
@ -9,6 +9,18 @@
|
||||
-- directory in which all library subdirectories reside.
|
||||
libraries_dir = "../../../libraries/"
|
||||
|
||||
local function add_extern_lib_paths(extern_lib)
|
||||
-- Add '<libraries root>/<libraryname>/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 '<libraries root>/<libraryname>/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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -6,7 +6,11 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#endif
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
||||
#include "ogghack.h" // HACK: must be included after al.h (bad interface)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,15 +1,17 @@
|
||||
#include "precompiled.h"
|
||||
#include "bsd.h"
|
||||
|
||||
#if OS_BSD
|
||||
#if OS_BSD || OS_MACOSX
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 <fam.h>
|
||||
|
||||
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
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
#include "ucpu.h"
|
||||
|
||||
#if OS_MACOSX
|
||||
#include <sys/sysctl.h>
|
||||
#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()
|
||||
|
@ -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 <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <cassert>
|
||||
|
||||
#include <bfd.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
#ifndef bfd_get_section_size
|
||||
#define bfd_get_section_size bfd_get_section_size_before_reloc
|
||||
#endif
|
||||
|
||||
#if OS_LINUX
|
||||
#define GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <execinfo.h>
|
||||
#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 <skip> frames in the backtrace");
|
||||
|
||||
// Assumed max length of a single print-out
|
||||
static const uint MAX_OUT_CHARS=1024;
|
||||
|
||||
for (uint i=skip;(int)i<bt_size && bufpos+MAX_OUT_CHARS < bufend;i++)
|
||||
{
|
||||
char file[DBG_FILE_LEN];
|
||||
char symbol[DBG_SYMBOL_LEN];
|
||||
int line;
|
||||
int len;
|
||||
|
||||
if (debug_resolve_symbol(bt[i], symbol, file, &line) == 0)
|
||||
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x) %hs:%d %hs\n", bt[i], file, line, symbol);
|
||||
else
|
||||
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x)\n", bt[i]);
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
// MAX_OUT_CHARS exceeded, realistically this was caused by some
|
||||
// mindbogglingly long symbol name... replace the end with an
|
||||
// ellipsis and a newline
|
||||
memcpy(&bufpos[MAX_OUT_CHARS-6], L"...\n", 5*sizeof(wchar_t));
|
||||
len = MAX_OUT_CHARS;
|
||||
}
|
||||
|
||||
bufpos += len;
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
static int slurp_symtab(symbol_file_context *ctx)
|
||||
{
|
||||
bfd *abfd=ctx->abfd;
|
||||
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<bfd_vma>(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...)
|
||||
|
||||
|
382
source/lib/sysdep/unix/udbg_bfd.cpp
Normal file
382
source/lib/sysdep/unix/udbg_bfd.cpp
Normal file
@ -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 <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <cassert>
|
||||
|
||||
#include <bfd.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
#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 <dlfcn.h>
|
||||
#include <execinfo.h>
|
||||
#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 <skip> frames in the backtrace");
|
||||
|
||||
// Assumed max length of a single print-out
|
||||
static const uint MAX_OUT_CHARS=1024;
|
||||
|
||||
for (uint i=skip;(int)i<bt_size && bufpos+MAX_OUT_CHARS < bufend;i++)
|
||||
{
|
||||
char file[DBG_FILE_LEN];
|
||||
char symbol[DBG_SYMBOL_LEN];
|
||||
int line;
|
||||
int len;
|
||||
|
||||
if (debug_resolve_symbol(bt[i], symbol, file, &line) == 0)
|
||||
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x) %hs:%d %hs\n", bt[i], file, line, symbol);
|
||||
else
|
||||
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x)\n", bt[i]);
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
// MAX_OUT_CHARS exceeded, realistically this was caused by some
|
||||
// mindbogglingly long symbol name... replace the end with an
|
||||
// ellipsis and a newline
|
||||
memcpy(&bufpos[MAX_OUT_CHARS-6], L"...\n", 5*sizeof(wchar_t));
|
||||
len = MAX_OUT_CHARS;
|
||||
}
|
||||
|
||||
bufpos += len;
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
static int slurp_symtab(symbol_file_context *ctx)
|
||||
{
|
||||
bfd *abfd=ctx->abfd;
|
||||
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<bfd_vma>(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
|
@ -4,7 +4,7 @@
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "lib/sdl.h"
|
||||
#include "lib/external_libraries/sdl.h"
|
||||
#include "lib/sysdep/sysdep.h"
|
||||
#include "udbg.h"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user