sanitizer_symbolizer_mac.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. //===-- sanitizer_symbolizer_mac.cpp --------------------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file is shared between various sanitizers' runtime libraries.
  10. //
  11. // Implementation of Mac-specific "atos" symbolizer.
  12. //===----------------------------------------------------------------------===//
  13. #include "sanitizer_platform.h"
  14. #if SANITIZER_APPLE
  15. #include "sanitizer_allocator_internal.h"
  16. #include "sanitizer_mac.h"
  17. #include "sanitizer_symbolizer_mac.h"
  18. #include <dlfcn.h>
  19. #include <errno.h>
  20. #include <stdlib.h>
  21. #include <sys/wait.h>
  22. #include <unistd.h>
  23. #include <util.h>
  24. namespace __sanitizer {
  25. bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
  26. Dl_info info;
  27. int result = dladdr((const void *)addr, &info);
  28. if (!result) return false;
  29. // Compute offset if possible. `dladdr()` doesn't always ensure that `addr >=
  30. // sym_addr` so only compute the offset when this holds. Failure to find the
  31. // function offset is not treated as a failure because it might still be
  32. // possible to get the symbol name.
  33. uptr sym_addr = reinterpret_cast<uptr>(info.dli_saddr);
  34. if (addr >= sym_addr) {
  35. stack->info.function_offset = addr - sym_addr;
  36. }
  37. const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
  38. if (!demangled) return false;
  39. stack->info.function = internal_strdup(demangled);
  40. return true;
  41. }
  42. bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
  43. Dl_info info;
  44. int result = dladdr((const void *)addr, &info);
  45. if (!result) return false;
  46. const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
  47. datainfo->name = internal_strdup(demangled);
  48. datainfo->start = (uptr)info.dli_saddr;
  49. return true;
  50. }
  51. class AtosSymbolizerProcess final : public SymbolizerProcess {
  52. public:
  53. explicit AtosSymbolizerProcess(const char *path)
  54. : SymbolizerProcess(path, /*use_posix_spawn*/ true) {
  55. pid_str_[0] = '\0';
  56. }
  57. private:
  58. bool StartSymbolizerSubprocess() override {
  59. // Put the string command line argument in the object so that it outlives
  60. // the call to GetArgV.
  61. internal_snprintf(pid_str_, sizeof(pid_str_), "%d", (int)internal_getpid());
  62. // Configure sandbox before starting atos process.
  63. return SymbolizerProcess::StartSymbolizerSubprocess();
  64. }
  65. bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
  66. return (length >= 1 && buffer[length - 1] == '\n');
  67. }
  68. void GetArgV(const char *path_to_binary,
  69. const char *(&argv)[kArgVMax]) const override {
  70. int i = 0;
  71. argv[i++] = path_to_binary;
  72. argv[i++] = "-p";
  73. argv[i++] = &pid_str_[0];
  74. if (GetMacosAlignedVersion() == MacosVersion(10, 9)) {
  75. // On Mavericks atos prints a deprecation warning which we suppress by
  76. // passing -d. The warning isn't present on other OSX versions, even the
  77. // newer ones.
  78. argv[i++] = "-d";
  79. }
  80. argv[i++] = nullptr;
  81. CHECK_LE(i, kArgVMax);
  82. }
  83. char pid_str_[16];
  84. };
  85. #undef K_ATOS_ENV_VAR
  86. static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
  87. char **out_module, char **out_file, uptr *line,
  88. uptr *start_address) {
  89. // Trim ending newlines.
  90. char *trim;
  91. ExtractTokenUpToDelimiter(str, "\n", &trim);
  92. // The line from `atos` is in one of these formats:
  93. // myfunction (in library.dylib) (sourcefile.c:17)
  94. // myfunction (in library.dylib) + 0x1fe
  95. // myfunction (in library.dylib) + 15
  96. // 0xdeadbeef (in library.dylib) + 0x1fe
  97. // 0xdeadbeef (in library.dylib) + 15
  98. // 0xdeadbeef (in library.dylib)
  99. // 0xdeadbeef
  100. const char *rest = trim;
  101. char *symbol_name;
  102. rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
  103. if (rest[0] == '\0') {
  104. InternalFree(symbol_name);
  105. InternalFree(trim);
  106. return false;
  107. }
  108. if (internal_strncmp(symbol_name, "0x", 2) != 0)
  109. *out_name = symbol_name;
  110. else
  111. InternalFree(symbol_name);
  112. rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
  113. if (rest[0] == '(') {
  114. if (out_file) {
  115. rest++;
  116. rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
  117. char *extracted_line_number;
  118. rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
  119. if (line) *line = (uptr)internal_atoll(extracted_line_number);
  120. InternalFree(extracted_line_number);
  121. }
  122. } else if (rest[0] == '+') {
  123. rest += 2;
  124. uptr offset = internal_atoll(rest);
  125. if (start_address) *start_address = addr - offset;
  126. }
  127. InternalFree(trim);
  128. return true;
  129. }
  130. AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
  131. : process_(new (*allocator) AtosSymbolizerProcess(path)) {}
  132. bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
  133. if (!process_) return false;
  134. if (addr == 0) return false;
  135. char command[32];
  136. internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
  137. const char *buf = process_->SendCommand(command);
  138. if (!buf) return false;
  139. uptr line;
  140. uptr start_address = AddressInfo::kUnknown;
  141. if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
  142. &stack->info.file, &line, &start_address)) {
  143. process_ = nullptr;
  144. return false;
  145. }
  146. stack->info.line = (int)line;
  147. if (start_address == AddressInfo::kUnknown) {
  148. // Fallback to dladdr() to get function start address if atos doesn't report
  149. // it.
  150. Dl_info info;
  151. int result = dladdr((const void *)addr, &info);
  152. if (result)
  153. start_address = reinterpret_cast<uptr>(info.dli_saddr);
  154. }
  155. // Only assign to `function_offset` if we were able to get the function's
  156. // start address and we got a sensible `start_address` (dladdr doesn't always
  157. // ensure that `addr >= sym_addr`).
  158. if (start_address != AddressInfo::kUnknown && addr >= start_address) {
  159. stack->info.function_offset = addr - start_address;
  160. }
  161. return true;
  162. }
  163. bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
  164. if (!process_) return false;
  165. char command[32];
  166. internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
  167. const char *buf = process_->SendCommand(command);
  168. if (!buf) return false;
  169. if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
  170. nullptr, &info->start)) {
  171. process_ = nullptr;
  172. return false;
  173. }
  174. return true;
  175. }
  176. } // namespace __sanitizer
  177. #endif // SANITIZER_APPLE