7186778: MachO decoder implementation for MacOSX

Mon, 30 Jul 2012 10:25:52 -0400

author
zgu
date
Mon, 30 Jul 2012 10:25:52 -0400
changeset 3961
3b01d0321dfa
parent 3940
950ed41429e5
child 3962
4bfef6df8881

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

src/os/bsd/vm/decoder_machO.cpp file | annotate | diff | comparison | revisions
src/os/bsd/vm/decoder_machO.hpp file | annotate | diff | comparison | revisions
src/os/bsd/vm/os_bsd.cpp file | annotate | diff | comparison | revisions
src/os/windows/vm/decoder_windows.cpp file | annotate | diff | comparison | revisions
src/os/windows/vm/decoder_windows.hpp file | annotate | diff | comparison | revisions
src/share/vm/utilities/decoder.cpp file | annotate | diff | comparison | revisions
src/share/vm/utilities/decoder.hpp file | annotate | diff | comparison | revisions
src/share/vm/utilities/decoder_elf.hpp file | annotate | diff | comparison | revisions
     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);

mercurial