# HG changeset patch # User zgu # Date 1343658352 14400 # Node ID 40c424a6ff5333742ed4f030f20a41560f22bf9f # Parent c6204b2b4209905049423948a4cd6d3bde443400 7186778: MachO decoder implementation for MacOSX Summary: Implementation of decoder for Apple's MacOSX. The implementation is based on the patch provided by Kevin Walls. Reviewed-by: coleenp, kamg, kevinw diff -r c6204b2b4209 -r 40c424a6ff53 hotspot/src/os/bsd/vm/decoder_machO.cpp --- a/hotspot/src/os/bsd/vm/decoder_machO.cpp Thu Jul 19 06:24:46 2012 -0700 +++ b/hotspot/src/os/bsd/vm/decoder_machO.cpp Mon Jul 30 10:25:52 2012 -0400 @@ -26,6 +26,139 @@ #ifdef __APPLE__ #include "decoder_machO.hpp" + +#include +#include +#include + + +bool MachODecoder::demangle(const char* symbol, char *buf, int buflen) { + int status; + char* result; + size_t size = (size_t)buflen; + // Don't pass buf to __cxa_demangle. In case of the 'buf' is too small, + // __cxa_demangle will call system "realloc" for additional memory, which + // may use different malloc/realloc mechanism that allocates 'buf'. + if ((result = abi::__cxa_demangle(symbol, NULL, NULL, &status)) != NULL) { + jio_snprintf(buf, buflen, "%s", result); + // call c library's free + ::free(result); + return true; + } + return false; +} + +bool MachODecoder::decode(address addr, char *buf, + int buflen, int *offset, const void *mach_base) { + struct symtab_command * symt = (struct symtab_command *) + mach_find_command((struct mach_header_64 *)mach_base, LC_SYMTAB); + if (symt == NULL) { + DEBUG_ONLY(tty->print_cr("no symtab in mach file at 0x%lx", mach_base)); + return false; + } + uint32_t off = symt->symoff; /* symbol table offset (within this mach file) */ + uint32_t nsyms = symt->nsyms; /* number of symbol table entries */ + uint32_t stroff = symt->stroff; /* string table offset */ + uint32_t strsize = symt->strsize; /* string table size in bytes */ + + // iterate through symbol table trying to match our offset + + uint32_t addr_relative = (uintptr_t) mach_base - (uintptr_t) addr; // offset we seek in the symtab + void * symtab_addr = (void*) ((uintptr_t) mach_base + off); + struct nlist_64 *cur_nlist = (struct nlist_64 *) symtab_addr; + struct nlist_64 *last_nlist = cur_nlist; // no size stored in an entry, so keep previously seen nlist + + int32_t found_strx = 0; + int32_t found_symval = 0; + + for (uint32_t i=0; i < nsyms; i++) { + uint32_t this_value = cur_nlist->n_value; + + if (addr_relative == this_value) { + found_strx = cur_nlist->n_un.n_strx; + found_symval = this_value; + break; + } else if (addr_relative > this_value) { + // gone past it, use previously seen nlist: + found_strx = last_nlist->n_un.n_strx; + found_symval = last_nlist->n_value; + break; + } + last_nlist = cur_nlist; + cur_nlist = cur_nlist + sizeof(struct nlist_64); + } + if (found_strx == 0) { + return false; + } + // write the offset: + *offset = addr_relative - found_symval; + + // lookup found_strx in the string table + char * symname = mach_find_in_stringtable((char*) ((uintptr_t)mach_base + stroff), strsize, found_strx); + if (symname) { + strncpy(buf, symname, buflen); + return true; + } + DEBUG_ONLY(tty->print_cr("no string or null string found.")); + return false; +} + +void* MachODecoder::mach_find_command(struct mach_header_64 * mach_base, uint32_t command_wanted) { + // possibly verify it is a mach_header, use magic number. + // commands begin immediately after the header. + struct load_command *pos = (struct load_command *) mach_base + sizeof(struct mach_header_64); + for (uint32_t i = 0; i < mach_base->ncmds; i++) { + struct load_command *this_cmd = (struct load_command *) pos; + if (this_cmd->cmd == command_wanted) { + return pos; + } + int cmdsize = this_cmd->cmdsize; + pos += cmdsize; + } + return NULL; +} + +char* MachODecoder::mach_find_in_stringtable(char *strtab, uint32_t tablesize, int strx_wanted) { + + if (strx_wanted == 0) { + return NULL; + } + char *strtab_end = strtab + tablesize; + + // find the first string, skip over the space char + // (or the four zero bytes we see e.g. in libclient) + if (*strtab == ' ') { + strtab++; + if (*strtab != 0) { + DEBUG_ONLY(tty->print_cr("string table has leading space but no following zero.")); + return NULL; + } + strtab++; + } else { + if ((uint32_t) *strtab != 0) { + DEBUG_ONLY(tty->print_cr("string table without leading space or leading int of zero.")); + return NULL; + } + strtab+=4; + } + // read the real strings starting at index 1 + int cur_strx = 1; + while (strtab < strtab_end) { + if (cur_strx == strx_wanted) { + return strtab; + } + // find start of next string + while (*strtab != 0) { + strtab++; + } + strtab++; // skip the terminating zero + cur_strx++; + } + DEBUG_ONLY(tty->print_cr("string number %d not found.", strx_wanted)); + return NULL; +} + + #endif diff -r c6204b2b4209 -r 40c424a6ff53 hotspot/src/os/bsd/vm/decoder_machO.hpp --- a/hotspot/src/os/bsd/vm/decoder_machO.hpp Thu Jul 19 06:24:46 2012 -0700 +++ b/hotspot/src/os/bsd/vm/decoder_machO.hpp Mon Jul 30 10:25:52 2012 -0400 @@ -31,10 +31,25 @@ // Just a placehold for now, a real implementation should derive // from AbstractDecoder -class MachODecoder : public NullDecoder { -public: +class MachODecoder : public AbstractDecoder { + public: MachODecoder() { } ~MachODecoder() { } + virtual bool can_decode_C_frame_in_vm() const { + return true; + } + virtual bool demangle(const char* symbol, char* buf, int buflen); + virtual bool decode(address pc, char* buf, int buflen, int* offset, + const void* base); + virtual bool decode(address pc, char* buf, int buflen, int* offset, + const char* module_path = NULL) { + ShouldNotReachHere(); + return false; + } + + private: + void * mach_find_command(struct mach_header_64 * mach_base, uint32_t command_wanted); + char * mach_find_in_stringtable(char *strtab, uint32_t tablesize, int strx_wanted); }; #endif diff -r c6204b2b4209 -r 40c424a6ff53 hotspot/src/os/bsd/vm/os_bsd.cpp --- a/hotspot/src/os/bsd/vm/os_bsd.cpp Thu Jul 19 06:24:46 2012 -0700 +++ b/hotspot/src/os/bsd/vm/os_bsd.cpp Mon Jul 30 10:25:52 2012 -0400 @@ -1946,10 +1946,16 @@ return false; } + +#define MACH_MAXSYMLEN 256 + bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset) { Dl_info dlinfo; - + char localbuf[MACH_MAXSYMLEN]; + + // dladdr will find names of dynamic functions only, but does + // it set dli_fbase with mach_header address when it "fails" ? if (dladdr((void*)addr, &dlinfo) && dlinfo.dli_sname != NULL) { if (buf != NULL) { if(!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) { @@ -1965,6 +1971,14 @@ } } + // Handle non-dymanic manually: + if (dlinfo.dli_fbase != NULL && + Decoder::decode(addr, localbuf, MACH_MAXSYMLEN, offset, dlinfo.dli_fbase)) { + if(!Decoder::demangle(localbuf, buf, buflen)) { + jio_snprintf(buf, buflen, "%s", localbuf); + } + return true; + } if (buf != NULL) buf[0] = '\0'; if (offset != NULL) *offset = -1; return false; diff -r c6204b2b4209 -r 40c424a6ff53 hotspot/src/os/windows/vm/decoder_windows.cpp --- a/hotspot/src/os/windows/vm/decoder_windows.cpp Thu Jul 19 06:24:46 2012 -0700 +++ b/hotspot/src/os/windows/vm/decoder_windows.cpp Mon Jul 30 10:25:52 2012 -0400 @@ -72,10 +72,10 @@ // find out if jvm.dll contains private symbols, by decoding // current function and comparing the result - address addr = (address)Decoder::decode; + address addr = (address)Decoder::demangle; char buf[MAX_PATH]; if (decode(addr, buf, sizeof(buf), NULL)) { - _can_decode_in_vm = !strcmp(buf, "Decoder::decode"); + _can_decode_in_vm = !strcmp(buf, "Decoder::demangle"); } } } diff -r c6204b2b4209 -r 40c424a6ff53 hotspot/src/os/windows/vm/decoder_windows.hpp --- a/hotspot/src/os/windows/vm/decoder_windows.hpp Thu Jul 19 06:24:46 2012 -0700 +++ b/hotspot/src/os/windows/vm/decoder_windows.hpp Mon Jul 30 10:25:52 2012 -0400 @@ -45,6 +45,10 @@ bool can_decode_C_frame_in_vm() const; bool demangle(const char* symbol, char *buf, int buflen); bool decode(address addr, char *buf, int buflen, int* offset, const char* modulepath = NULL); + bool decode(address addr, char *buf, int buflen, int* offset, const void* base) { + ShouldNotReachHere(); + return false; + } private: void initialize(); diff -r c6204b2b4209 -r 40c424a6ff53 hotspot/src/share/vm/utilities/decoder.cpp --- a/hotspot/src/share/vm/utilities/decoder.cpp Thu Jul 19 06:24:46 2012 -0700 +++ b/hotspot/src/share/vm/utilities/decoder.cpp Mon Jul 30 10:25:52 2012 -0400 @@ -91,6 +91,18 @@ return decoder->decode(addr, buf, buflen, offset, modulepath); } +bool Decoder::decode(address addr, char* buf, int buflen, int* offset, const void* base) { + assert(_shared_decoder_lock != NULL, "Just check"); + bool error_handling_thread = os::current_thread_id() == VMError::first_error_tid; + MutexLockerEx locker(error_handling_thread ? NULL : _shared_decoder_lock, true); + AbstractDecoder* decoder = error_handling_thread ? + get_error_handler_instance(): get_shared_instance(); + assert(decoder != NULL, "null decoder"); + + return decoder->decode(addr, buf, buflen, offset, base); +} + + bool Decoder::demangle(const char* symbol, char* buf, int buflen) { assert(_shared_decoder_lock != NULL, "Just check"); bool error_handling_thread = os::current_thread_id() == VMError::first_error_tid; diff -r c6204b2b4209 -r 40c424a6ff53 hotspot/src/share/vm/utilities/decoder.hpp --- a/hotspot/src/share/vm/utilities/decoder.hpp Thu Jul 19 06:24:46 2012 -0700 +++ b/hotspot/src/share/vm/utilities/decoder.hpp Mon Jul 30 10:25:52 2012 -0400 @@ -47,6 +47,8 @@ // the function virtual bool decode(address pc, char* buf, int buflen, int* offset, const char* modulepath = NULL) = 0; + virtual bool decode(address pc, char* buf, int buflen, int* offset, const void* base) = 0; + // demangle a C++ symbol virtual bool demangle(const char* symbol, char* buf, int buflen) = 0; // if the decoder can decode symbols in vm @@ -82,6 +84,10 @@ return false; } + virtual bool decode(address pc, char* buf, int buflen, int* offset, const void* base) { + return false; + } + virtual bool demangle(const char* symbol, char* buf, int buflen) { return false; } @@ -95,6 +101,7 @@ class Decoder : AllStatic { public: static bool decode(address pc, char* buf, int buflen, int* offset, const char* modulepath = NULL); + static bool decode(address pc, char* buf, int buflen, int* offset, const void* base); static bool demangle(const char* symbol, char* buf, int buflen); static bool can_decode_C_frame_in_vm(); diff -r c6204b2b4209 -r 40c424a6ff53 hotspot/src/share/vm/utilities/decoder_elf.hpp --- a/hotspot/src/share/vm/utilities/decoder_elf.hpp Thu Jul 19 06:24:46 2012 -0700 +++ b/hotspot/src/share/vm/utilities/decoder_elf.hpp Mon Jul 30 10:25:52 2012 -0400 @@ -43,6 +43,10 @@ bool demangle(const char* symbol, char *buf, int buflen); bool decode(address addr, char *buf, int buflen, int* offset, const char* filepath = NULL); + bool decode(address addr, char *buf, int buflen, int* offset, const void *base) { + ShouldNotReachHere(); + return false; + } private: ElfFile* get_elf_file(const char* filepath);