Home | History | Annotate | Download | only in sanitizer_common
      1 //===-- sanitizer_symbolizer_mac.cc ---------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is shared between various sanitizers' runtime libraries.
     11 //
     12 // Implementation of Mac-specific "atos" symbolizer.
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "sanitizer_platform.h"
     16 #if SANITIZER_MAC
     17 
     18 #include "sanitizer_allocator_internal.h"
     19 #include "sanitizer_mac.h"
     20 #include "sanitizer_symbolizer_mac.h"
     21 
     22 namespace __sanitizer {
     23 
     24 #include <dlfcn.h>
     25 #include <errno.h>
     26 #include <stdlib.h>
     27 #include <sys/wait.h>
     28 #include <unistd.h>
     29 #include <util.h>
     30 
     31 bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
     32   Dl_info info;
     33   int result = dladdr((const void *)addr, &info);
     34   if (!result) return false;
     35   const char *demangled = DemangleCXXABI(info.dli_sname);
     36   stack->info.function = demangled ? internal_strdup(demangled) : nullptr;
     37   return true;
     38 }
     39 
     40 bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
     41   Dl_info info;
     42   int result = dladdr((const void *)addr, &info);
     43   if (!result) return false;
     44   const char *demangled = DemangleCXXABI(info.dli_sname);
     45   datainfo->name = internal_strdup(demangled);
     46   datainfo->start = (uptr)info.dli_saddr;
     47   return true;
     48 }
     49 
     50 class AtosSymbolizerProcess : public SymbolizerProcess {
     51  public:
     52   explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
     53       : SymbolizerProcess(path, /*use_forkpty*/ true) {
     54     // Put the string command line argument in the object so that it outlives
     55     // the call to GetArgV.
     56     internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
     57   }
     58 
     59  private:
     60   bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
     61     return (length >= 1 && buffer[length - 1] == '\n');
     62   }
     63 
     64   void GetArgV(const char *path_to_binary,
     65                const char *(&argv)[kArgVMax]) const override {
     66     int i = 0;
     67     argv[i++] = path_to_binary;
     68     argv[i++] = "-p";
     69     argv[i++] = &pid_str_[0];
     70     if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
     71       // On Mavericks atos prints a deprecation warning which we suppress by
     72       // passing -d. The warning isn't present on other OSX versions, even the
     73       // newer ones.
     74       argv[i++] = "-d";
     75     }
     76     argv[i++] = nullptr;
     77   }
     78 
     79   char pid_str_[16];
     80 };
     81 
     82 static const char *kAtosErrorMessages[] = {
     83   "atos cannot examine process",
     84   "unable to get permission to examine process",
     85   "An admin user name and password is required",
     86   "could not load inserted library",
     87   "architecture mismatch between analysis process",
     88 };
     89 
     90 static bool IsAtosErrorMessage(const char *str) {
     91   for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
     92     if (internal_strstr(str, kAtosErrorMessages[i])) {
     93       return true;
     94     }
     95   }
     96   return false;
     97 }
     98 
     99 static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
    100                                char **out_module, char **out_file, uptr *line,
    101                                uptr *start_address) {
    102   // Trim ending newlines.
    103   char *trim;
    104   ExtractTokenUpToDelimiter(str, "\n", &trim);
    105 
    106   // The line from `atos` is in one of these formats:
    107   //   myfunction (in library.dylib) (sourcefile.c:17)
    108   //   myfunction (in library.dylib) + 0x1fe
    109   //   myfunction (in library.dylib) + 15
    110   //   0xdeadbeef (in library.dylib) + 0x1fe
    111   //   0xdeadbeef (in library.dylib) + 15
    112   //   0xdeadbeef (in library.dylib)
    113   //   0xdeadbeef
    114 
    115   if (IsAtosErrorMessage(trim)) {
    116     Report("atos returned an error: %s\n", trim);
    117     InternalFree(trim);
    118     return false;
    119   }
    120 
    121   const char *rest = trim;
    122   char *symbol_name;
    123   rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
    124   if (rest[0] == '\0') {
    125     InternalFree(symbol_name);
    126     InternalFree(trim);
    127     return false;
    128   }
    129 
    130   if (internal_strncmp(symbol_name, "0x", 2) != 0)
    131     *out_name = symbol_name;
    132   else
    133     InternalFree(symbol_name);
    134   rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
    135 
    136   if (rest[0] == '(') {
    137     if (out_file) {
    138       rest++;
    139       rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
    140       char *extracted_line_number;
    141       rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
    142       if (line) *line = (uptr)internal_atoll(extracted_line_number);
    143       InternalFree(extracted_line_number);
    144     }
    145   } else if (rest[0] == '+') {
    146     rest += 2;
    147     uptr offset = internal_atoll(rest);
    148     if (start_address) *start_address = addr - offset;
    149   }
    150 
    151   InternalFree(trim);
    152   return true;
    153 }
    154 
    155 AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
    156     : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
    157 
    158 bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
    159   if (!process_) return false;
    160   char command[32];
    161   internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
    162   const char *buf = process_->SendCommand(command);
    163   if (!buf) return false;
    164   uptr line;
    165   if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
    166                           &stack->info.file, &line, nullptr)) {
    167     process_ = nullptr;
    168     return false;
    169   }
    170   stack->info.line = (int)line;
    171   return true;
    172 }
    173 
    174 bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
    175   if (!process_) return false;
    176   char command[32];
    177   internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
    178   const char *buf = process_->SendCommand(command);
    179   if (!buf) return false;
    180   if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
    181                           nullptr, &info->start)) {
    182     process_ = nullptr;
    183     return false;
    184   }
    185   return true;
    186 }
    187 
    188 }  // namespace __sanitizer
    189 
    190 #endif  // SANITIZER_MAC
    191