Mon, 30 Jul 2012 10:25:52 -0400
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
1.1 --- a/src/os/bsd/vm/decoder_machO.cpp Thu Jul 19 06:24:46 2012 -0700 1.2 +++ b/src/os/bsd/vm/decoder_machO.cpp Mon Jul 30 10:25:52 2012 -0400 1.3 @@ -26,6 +26,139 @@ 1.4 1.5 #ifdef __APPLE__ 1.6 #include "decoder_machO.hpp" 1.7 + 1.8 +#include <cxxabi.h> 1.9 +#include <mach-o/loader.h> 1.10 +#include <mach-o/nlist.h> 1.11 + 1.12 + 1.13 +bool MachODecoder::demangle(const char* symbol, char *buf, int buflen) { 1.14 + int status; 1.15 + char* result; 1.16 + size_t size = (size_t)buflen; 1.17 + // Don't pass buf to __cxa_demangle. In case of the 'buf' is too small, 1.18 + // __cxa_demangle will call system "realloc" for additional memory, which 1.19 + // may use different malloc/realloc mechanism that allocates 'buf'. 1.20 + if ((result = abi::__cxa_demangle(symbol, NULL, NULL, &status)) != NULL) { 1.21 + jio_snprintf(buf, buflen, "%s", result); 1.22 + // call c library's free 1.23 + ::free(result); 1.24 + return true; 1.25 + } 1.26 + return false; 1.27 +} 1.28 + 1.29 +bool MachODecoder::decode(address addr, char *buf, 1.30 + int buflen, int *offset, const void *mach_base) { 1.31 + struct symtab_command * symt = (struct symtab_command *) 1.32 + mach_find_command((struct mach_header_64 *)mach_base, LC_SYMTAB); 1.33 + if (symt == NULL) { 1.34 + DEBUG_ONLY(tty->print_cr("no symtab in mach file at 0x%lx", mach_base)); 1.35 + return false; 1.36 + } 1.37 + uint32_t off = symt->symoff; /* symbol table offset (within this mach file) */ 1.38 + uint32_t nsyms = symt->nsyms; /* number of symbol table entries */ 1.39 + uint32_t stroff = symt->stroff; /* string table offset */ 1.40 + uint32_t strsize = symt->strsize; /* string table size in bytes */ 1.41 + 1.42 + // iterate through symbol table trying to match our offset 1.43 + 1.44 + uint32_t addr_relative = (uintptr_t) mach_base - (uintptr_t) addr; // offset we seek in the symtab 1.45 + void * symtab_addr = (void*) ((uintptr_t) mach_base + off); 1.46 + struct nlist_64 *cur_nlist = (struct nlist_64 *) symtab_addr; 1.47 + struct nlist_64 *last_nlist = cur_nlist; // no size stored in an entry, so keep previously seen nlist 1.48 + 1.49 + int32_t found_strx = 0; 1.50 + int32_t found_symval = 0; 1.51 + 1.52 + for (uint32_t i=0; i < nsyms; i++) { 1.53 + uint32_t this_value = cur_nlist->n_value; 1.54 + 1.55 + if (addr_relative == this_value) { 1.56 + found_strx = cur_nlist->n_un.n_strx; 1.57 + found_symval = this_value; 1.58 + break; 1.59 + } else if (addr_relative > this_value) { 1.60 + // gone past it, use previously seen nlist: 1.61 + found_strx = last_nlist->n_un.n_strx; 1.62 + found_symval = last_nlist->n_value; 1.63 + break; 1.64 + } 1.65 + last_nlist = cur_nlist; 1.66 + cur_nlist = cur_nlist + sizeof(struct nlist_64); 1.67 + } 1.68 + if (found_strx == 0) { 1.69 + return false; 1.70 + } 1.71 + // write the offset: 1.72 + *offset = addr_relative - found_symval; 1.73 + 1.74 + // lookup found_strx in the string table 1.75 + char * symname = mach_find_in_stringtable((char*) ((uintptr_t)mach_base + stroff), strsize, found_strx); 1.76 + if (symname) { 1.77 + strncpy(buf, symname, buflen); 1.78 + return true; 1.79 + } 1.80 + DEBUG_ONLY(tty->print_cr("no string or null string found.")); 1.81 + return false; 1.82 +} 1.83 + 1.84 +void* MachODecoder::mach_find_command(struct mach_header_64 * mach_base, uint32_t command_wanted) { 1.85 + // possibly verify it is a mach_header, use magic number. 1.86 + // commands begin immediately after the header. 1.87 + struct load_command *pos = (struct load_command *) mach_base + sizeof(struct mach_header_64); 1.88 + for (uint32_t i = 0; i < mach_base->ncmds; i++) { 1.89 + struct load_command *this_cmd = (struct load_command *) pos; 1.90 + if (this_cmd->cmd == command_wanted) { 1.91 + return pos; 1.92 + } 1.93 + int cmdsize = this_cmd->cmdsize; 1.94 + pos += cmdsize; 1.95 + } 1.96 + return NULL; 1.97 +} 1.98 + 1.99 +char* MachODecoder::mach_find_in_stringtable(char *strtab, uint32_t tablesize, int strx_wanted) { 1.100 + 1.101 + if (strx_wanted == 0) { 1.102 + return NULL; 1.103 + } 1.104 + char *strtab_end = strtab + tablesize; 1.105 + 1.106 + // find the first string, skip over the space char 1.107 + // (or the four zero bytes we see e.g. in libclient) 1.108 + if (*strtab == ' ') { 1.109 + strtab++; 1.110 + if (*strtab != 0) { 1.111 + DEBUG_ONLY(tty->print_cr("string table has leading space but no following zero.")); 1.112 + return NULL; 1.113 + } 1.114 + strtab++; 1.115 + } else { 1.116 + if ((uint32_t) *strtab != 0) { 1.117 + DEBUG_ONLY(tty->print_cr("string table without leading space or leading int of zero.")); 1.118 + return NULL; 1.119 + } 1.120 + strtab+=4; 1.121 + } 1.122 + // read the real strings starting at index 1 1.123 + int cur_strx = 1; 1.124 + while (strtab < strtab_end) { 1.125 + if (cur_strx == strx_wanted) { 1.126 + return strtab; 1.127 + } 1.128 + // find start of next string 1.129 + while (*strtab != 0) { 1.130 + strtab++; 1.131 + } 1.132 + strtab++; // skip the terminating zero 1.133 + cur_strx++; 1.134 + } 1.135 + DEBUG_ONLY(tty->print_cr("string number %d not found.", strx_wanted)); 1.136 + return NULL; 1.137 +} 1.138 + 1.139 + 1.140 #endif 1.141 1.142
2.1 --- a/src/os/bsd/vm/decoder_machO.hpp Thu Jul 19 06:24:46 2012 -0700 2.2 +++ b/src/os/bsd/vm/decoder_machO.hpp Mon Jul 30 10:25:52 2012 -0400 2.3 @@ -31,10 +31,25 @@ 2.4 2.5 // Just a placehold for now, a real implementation should derive 2.6 // from AbstractDecoder 2.7 -class MachODecoder : public NullDecoder { 2.8 -public: 2.9 +class MachODecoder : public AbstractDecoder { 2.10 + public: 2.11 MachODecoder() { } 2.12 ~MachODecoder() { } 2.13 + virtual bool can_decode_C_frame_in_vm() const { 2.14 + return true; 2.15 + } 2.16 + virtual bool demangle(const char* symbol, char* buf, int buflen); 2.17 + virtual bool decode(address pc, char* buf, int buflen, int* offset, 2.18 + const void* base); 2.19 + virtual bool decode(address pc, char* buf, int buflen, int* offset, 2.20 + const char* module_path = NULL) { 2.21 + ShouldNotReachHere(); 2.22 + return false; 2.23 + } 2.24 + 2.25 + private: 2.26 + void * mach_find_command(struct mach_header_64 * mach_base, uint32_t command_wanted); 2.27 + char * mach_find_in_stringtable(char *strtab, uint32_t tablesize, int strx_wanted); 2.28 }; 2.29 2.30 #endif
3.1 --- a/src/os/bsd/vm/os_bsd.cpp Thu Jul 19 06:24:46 2012 -0700 3.2 +++ b/src/os/bsd/vm/os_bsd.cpp Mon Jul 30 10:25:52 2012 -0400 3.3 @@ -1946,10 +1946,16 @@ 3.4 return false; 3.5 } 3.6 3.7 + 3.8 +#define MACH_MAXSYMLEN 256 3.9 + 3.10 bool os::dll_address_to_function_name(address addr, char *buf, 3.11 int buflen, int *offset) { 3.12 Dl_info dlinfo; 3.13 - 3.14 + char localbuf[MACH_MAXSYMLEN]; 3.15 + 3.16 + // dladdr will find names of dynamic functions only, but does 3.17 + // it set dli_fbase with mach_header address when it "fails" ? 3.18 if (dladdr((void*)addr, &dlinfo) && dlinfo.dli_sname != NULL) { 3.19 if (buf != NULL) { 3.20 if(!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) { 3.21 @@ -1965,6 +1971,14 @@ 3.22 } 3.23 } 3.24 3.25 + // Handle non-dymanic manually: 3.26 + if (dlinfo.dli_fbase != NULL && 3.27 + Decoder::decode(addr, localbuf, MACH_MAXSYMLEN, offset, dlinfo.dli_fbase)) { 3.28 + if(!Decoder::demangle(localbuf, buf, buflen)) { 3.29 + jio_snprintf(buf, buflen, "%s", localbuf); 3.30 + } 3.31 + return true; 3.32 + } 3.33 if (buf != NULL) buf[0] = '\0'; 3.34 if (offset != NULL) *offset = -1; 3.35 return false;
4.1 --- a/src/os/windows/vm/decoder_windows.cpp Thu Jul 19 06:24:46 2012 -0700 4.2 +++ b/src/os/windows/vm/decoder_windows.cpp Mon Jul 30 10:25:52 2012 -0400 4.3 @@ -72,10 +72,10 @@ 4.4 4.5 // find out if jvm.dll contains private symbols, by decoding 4.6 // current function and comparing the result 4.7 - address addr = (address)Decoder::decode; 4.8 + address addr = (address)Decoder::demangle; 4.9 char buf[MAX_PATH]; 4.10 if (decode(addr, buf, sizeof(buf), NULL)) { 4.11 - _can_decode_in_vm = !strcmp(buf, "Decoder::decode"); 4.12 + _can_decode_in_vm = !strcmp(buf, "Decoder::demangle"); 4.13 } 4.14 } 4.15 }
5.1 --- a/src/os/windows/vm/decoder_windows.hpp Thu Jul 19 06:24:46 2012 -0700 5.2 +++ b/src/os/windows/vm/decoder_windows.hpp Mon Jul 30 10:25:52 2012 -0400 5.3 @@ -45,6 +45,10 @@ 5.4 bool can_decode_C_frame_in_vm() const; 5.5 bool demangle(const char* symbol, char *buf, int buflen); 5.6 bool decode(address addr, char *buf, int buflen, int* offset, const char* modulepath = NULL); 5.7 + bool decode(address addr, char *buf, int buflen, int* offset, const void* base) { 5.8 + ShouldNotReachHere(); 5.9 + return false; 5.10 + } 5.11 5.12 private: 5.13 void initialize();
6.1 --- a/src/share/vm/utilities/decoder.cpp Thu Jul 19 06:24:46 2012 -0700 6.2 +++ b/src/share/vm/utilities/decoder.cpp Mon Jul 30 10:25:52 2012 -0400 6.3 @@ -91,6 +91,18 @@ 6.4 return decoder->decode(addr, buf, buflen, offset, modulepath); 6.5 } 6.6 6.7 +bool Decoder::decode(address addr, char* buf, int buflen, int* offset, const void* base) { 6.8 + assert(_shared_decoder_lock != NULL, "Just check"); 6.9 + bool error_handling_thread = os::current_thread_id() == VMError::first_error_tid; 6.10 + MutexLockerEx locker(error_handling_thread ? NULL : _shared_decoder_lock, true); 6.11 + AbstractDecoder* decoder = error_handling_thread ? 6.12 + get_error_handler_instance(): get_shared_instance(); 6.13 + assert(decoder != NULL, "null decoder"); 6.14 + 6.15 + return decoder->decode(addr, buf, buflen, offset, base); 6.16 +} 6.17 + 6.18 + 6.19 bool Decoder::demangle(const char* symbol, char* buf, int buflen) { 6.20 assert(_shared_decoder_lock != NULL, "Just check"); 6.21 bool error_handling_thread = os::current_thread_id() == VMError::first_error_tid;
7.1 --- a/src/share/vm/utilities/decoder.hpp Thu Jul 19 06:24:46 2012 -0700 7.2 +++ b/src/share/vm/utilities/decoder.hpp Mon Jul 30 10:25:52 2012 -0400 7.3 @@ -47,6 +47,8 @@ 7.4 // the function 7.5 virtual bool decode(address pc, char* buf, int buflen, int* offset, 7.6 const char* modulepath = NULL) = 0; 7.7 + virtual bool decode(address pc, char* buf, int buflen, int* offset, const void* base) = 0; 7.8 + 7.9 // demangle a C++ symbol 7.10 virtual bool demangle(const char* symbol, char* buf, int buflen) = 0; 7.11 // if the decoder can decode symbols in vm 7.12 @@ -82,6 +84,10 @@ 7.13 return false; 7.14 } 7.15 7.16 + virtual bool decode(address pc, char* buf, int buflen, int* offset, const void* base) { 7.17 + return false; 7.18 + } 7.19 + 7.20 virtual bool demangle(const char* symbol, char* buf, int buflen) { 7.21 return false; 7.22 } 7.23 @@ -95,6 +101,7 @@ 7.24 class Decoder : AllStatic { 7.25 public: 7.26 static bool decode(address pc, char* buf, int buflen, int* offset, const char* modulepath = NULL); 7.27 + static bool decode(address pc, char* buf, int buflen, int* offset, const void* base); 7.28 static bool demangle(const char* symbol, char* buf, int buflen); 7.29 static bool can_decode_C_frame_in_vm(); 7.30
8.1 --- a/src/share/vm/utilities/decoder_elf.hpp Thu Jul 19 06:24:46 2012 -0700 8.2 +++ b/src/share/vm/utilities/decoder_elf.hpp Mon Jul 30 10:25:52 2012 -0400 8.3 @@ -43,6 +43,10 @@ 8.4 8.5 bool demangle(const char* symbol, char *buf, int buflen); 8.6 bool decode(address addr, char *buf, int buflen, int* offset, const char* filepath = NULL); 8.7 + bool decode(address addr, char *buf, int buflen, int* offset, const void *base) { 8.8 + ShouldNotReachHere(); 8.9 + return false; 8.10 + } 8.11 8.12 private: 8.13 ElfFile* get_elf_file(const char* filepath);