xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
168d75effSDimitry Andric //===-- sanitizer_symbolizer_mac.cpp --------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is shared between various sanitizers' runtime libraries.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric // Implementation of Mac-specific "atos" symbolizer.
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric 
1468d75effSDimitry Andric #include "sanitizer_platform.h"
1581ad6265SDimitry Andric #if SANITIZER_APPLE
1668d75effSDimitry Andric 
1768d75effSDimitry Andric #  include <dlfcn.h>
1868d75effSDimitry Andric #  include <errno.h>
1968d75effSDimitry Andric #  include <stdlib.h>
2068d75effSDimitry Andric #  include <sys/wait.h>
2168d75effSDimitry Andric #  include <unistd.h>
2268d75effSDimitry Andric #  include <util.h>
2368d75effSDimitry Andric 
2406c3fb27SDimitry Andric #  include "sanitizer_allocator_internal.h"
2506c3fb27SDimitry Andric #  include "sanitizer_mac.h"
2606c3fb27SDimitry Andric #  include "sanitizer_symbolizer_mac.h"
2706c3fb27SDimitry Andric 
2868d75effSDimitry Andric namespace __sanitizer {
2968d75effSDimitry Andric 
3068d75effSDimitry Andric bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
3168d75effSDimitry Andric   Dl_info info;
3268d75effSDimitry Andric   int result = dladdr((const void *)addr, &info);
3368d75effSDimitry Andric   if (!result) return false;
34480093f4SDimitry Andric 
35e8d8bef9SDimitry Andric   // Compute offset if possible. `dladdr()` doesn't always ensure that `addr >=
36e8d8bef9SDimitry Andric   // sym_addr` so only compute the offset when this holds. Failure to find the
37e8d8bef9SDimitry Andric   // function offset is not treated as a failure because it might still be
38e8d8bef9SDimitry Andric   // possible to get the symbol name.
39e8d8bef9SDimitry Andric   uptr sym_addr = reinterpret_cast<uptr>(info.dli_saddr);
40e8d8bef9SDimitry Andric   if (addr >= sym_addr) {
41e8d8bef9SDimitry Andric     stack->info.function_offset = addr - sym_addr;
42e8d8bef9SDimitry Andric   }
43e8d8bef9SDimitry Andric 
4468d75effSDimitry Andric   const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
45*5f757f3fSDimitry Andric   if (!demangled)
46*5f757f3fSDimitry Andric     demangled = info.dli_sname;
4768d75effSDimitry Andric   stack->info.function = internal_strdup(demangled);
4868d75effSDimitry Andric   return true;
4968d75effSDimitry Andric }
5068d75effSDimitry Andric 
5168d75effSDimitry Andric bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
5268d75effSDimitry Andric   Dl_info info;
5368d75effSDimitry Andric   int result = dladdr((const void *)addr, &info);
5468d75effSDimitry Andric   if (!result) return false;
5568d75effSDimitry Andric   const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
56*5f757f3fSDimitry Andric   if (!demangled)
57*5f757f3fSDimitry Andric     demangled = info.dli_sname;
5868d75effSDimitry Andric   datainfo->name = internal_strdup(demangled);
5968d75effSDimitry Andric   datainfo->start = (uptr)info.dli_saddr;
6068d75effSDimitry Andric   return true;
6168d75effSDimitry Andric }
6268d75effSDimitry Andric 
63e8d8bef9SDimitry Andric class AtosSymbolizerProcess final : public SymbolizerProcess {
6468d75effSDimitry Andric  public:
655ffd83dbSDimitry Andric   explicit AtosSymbolizerProcess(const char *path)
6668d75effSDimitry Andric       : SymbolizerProcess(path, /*use_posix_spawn*/ true) {
675ffd83dbSDimitry Andric     pid_str_[0] = '\0';
685ffd83dbSDimitry Andric   }
695ffd83dbSDimitry Andric 
7068d75effSDimitry Andric  private:
7168d75effSDimitry Andric   bool StartSymbolizerSubprocess() override {
725ffd83dbSDimitry Andric     // Put the string command line argument in the object so that it outlives
735ffd83dbSDimitry Andric     // the call to GetArgV.
740eae32dcSDimitry Andric     internal_snprintf(pid_str_, sizeof(pid_str_), "%d", (int)internal_getpid());
755ffd83dbSDimitry Andric 
760eae32dcSDimitry Andric     // Configure sandbox before starting atos process.
7768d75effSDimitry Andric     return SymbolizerProcess::StartSymbolizerSubprocess();
7868d75effSDimitry Andric   }
7968d75effSDimitry Andric 
8068d75effSDimitry Andric   bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
8168d75effSDimitry Andric     return (length >= 1 && buffer[length - 1] == '\n');
8268d75effSDimitry Andric   }
8368d75effSDimitry Andric 
8468d75effSDimitry Andric   void GetArgV(const char *path_to_binary,
8568d75effSDimitry Andric                const char *(&argv)[kArgVMax]) const override {
8668d75effSDimitry Andric     int i = 0;
8768d75effSDimitry Andric     argv[i++] = path_to_binary;
8868d75effSDimitry Andric     argv[i++] = "-p";
8968d75effSDimitry Andric     argv[i++] = &pid_str_[0];
905ffd83dbSDimitry Andric     if (GetMacosAlignedVersion() == MacosVersion(10, 9)) {
9168d75effSDimitry Andric       // On Mavericks atos prints a deprecation warning which we suppress by
9268d75effSDimitry Andric       // passing -d. The warning isn't present on other OSX versions, even the
9368d75effSDimitry Andric       // newer ones.
9468d75effSDimitry Andric       argv[i++] = "-d";
9568d75effSDimitry Andric     }
9668d75effSDimitry Andric     argv[i++] = nullptr;
970eae32dcSDimitry Andric     CHECK_LE(i, kArgVMax);
9868d75effSDimitry Andric   }
9968d75effSDimitry Andric 
10068d75effSDimitry Andric   char pid_str_[16];
10168d75effSDimitry Andric };
10268d75effSDimitry Andric 
1035ffd83dbSDimitry Andric #undef K_ATOS_ENV_VAR
1045ffd83dbSDimitry Andric 
10568d75effSDimitry Andric static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
10668d75effSDimitry Andric                                char **out_module, char **out_file, uptr *line,
10768d75effSDimitry Andric                                uptr *start_address) {
10868d75effSDimitry Andric   // Trim ending newlines.
10968d75effSDimitry Andric   char *trim;
11068d75effSDimitry Andric   ExtractTokenUpToDelimiter(str, "\n", &trim);
11168d75effSDimitry Andric 
11268d75effSDimitry Andric   // The line from `atos` is in one of these formats:
11368d75effSDimitry Andric   //   myfunction (in library.dylib) (sourcefile.c:17)
11468d75effSDimitry Andric   //   myfunction (in library.dylib) + 0x1fe
11568d75effSDimitry Andric   //   myfunction (in library.dylib) + 15
11668d75effSDimitry Andric   //   0xdeadbeef (in library.dylib) + 0x1fe
11768d75effSDimitry Andric   //   0xdeadbeef (in library.dylib) + 15
11868d75effSDimitry Andric   //   0xdeadbeef (in library.dylib)
11968d75effSDimitry Andric   //   0xdeadbeef
12068d75effSDimitry Andric 
12168d75effSDimitry Andric   const char *rest = trim;
12268d75effSDimitry Andric   char *symbol_name;
12368d75effSDimitry Andric   rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
12468d75effSDimitry Andric   if (rest[0] == '\0') {
12568d75effSDimitry Andric     InternalFree(symbol_name);
12668d75effSDimitry Andric     InternalFree(trim);
12768d75effSDimitry Andric     return false;
12868d75effSDimitry Andric   }
12968d75effSDimitry Andric 
13068d75effSDimitry Andric   if (internal_strncmp(symbol_name, "0x", 2) != 0)
13168d75effSDimitry Andric     *out_name = symbol_name;
13268d75effSDimitry Andric   else
13368d75effSDimitry Andric     InternalFree(symbol_name);
13468d75effSDimitry Andric   rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
13568d75effSDimitry Andric 
13668d75effSDimitry Andric   if (rest[0] == '(') {
13768d75effSDimitry Andric     if (out_file) {
13868d75effSDimitry Andric       rest++;
13968d75effSDimitry Andric       rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
14068d75effSDimitry Andric       char *extracted_line_number;
14168d75effSDimitry Andric       rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
14268d75effSDimitry Andric       if (line) *line = (uptr)internal_atoll(extracted_line_number);
14368d75effSDimitry Andric       InternalFree(extracted_line_number);
14468d75effSDimitry Andric     }
14568d75effSDimitry Andric   } else if (rest[0] == '+') {
14668d75effSDimitry Andric     rest += 2;
14768d75effSDimitry Andric     uptr offset = internal_atoll(rest);
14868d75effSDimitry Andric     if (start_address) *start_address = addr - offset;
14968d75effSDimitry Andric   }
15068d75effSDimitry Andric 
15168d75effSDimitry Andric   InternalFree(trim);
15268d75effSDimitry Andric   return true;
15368d75effSDimitry Andric }
15468d75effSDimitry Andric 
15568d75effSDimitry Andric AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
1565ffd83dbSDimitry Andric     : process_(new (*allocator) AtosSymbolizerProcess(path)) {}
15768d75effSDimitry Andric 
15868d75effSDimitry Andric bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
15968d75effSDimitry Andric   if (!process_) return false;
16068d75effSDimitry Andric   if (addr == 0) return false;
16168d75effSDimitry Andric   char command[32];
16268d75effSDimitry Andric   internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
16368d75effSDimitry Andric   const char *buf = process_->SendCommand(command);
16468d75effSDimitry Andric   if (!buf) return false;
16568d75effSDimitry Andric   uptr line;
166480093f4SDimitry Andric   uptr start_address = AddressInfo::kUnknown;
16768d75effSDimitry Andric   if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
168480093f4SDimitry Andric                           &stack->info.file, &line, &start_address)) {
16906c3fb27SDimitry Andric     Report("WARNING: atos failed to symbolize address \"0x%zx\"\n", addr);
17068d75effSDimitry Andric     return false;
17168d75effSDimitry Andric   }
17268d75effSDimitry Andric   stack->info.line = (int)line;
173480093f4SDimitry Andric 
174480093f4SDimitry Andric   if (start_address == AddressInfo::kUnknown) {
175480093f4SDimitry Andric     // Fallback to dladdr() to get function start address if atos doesn't report
176480093f4SDimitry Andric     // it.
177480093f4SDimitry Andric     Dl_info info;
178480093f4SDimitry Andric     int result = dladdr((const void *)addr, &info);
179480093f4SDimitry Andric     if (result)
180480093f4SDimitry Andric       start_address = reinterpret_cast<uptr>(info.dli_saddr);
181480093f4SDimitry Andric   }
182480093f4SDimitry Andric 
183e8d8bef9SDimitry Andric   // Only assign to `function_offset` if we were able to get the function's
184e8d8bef9SDimitry Andric   // start address and we got a sensible `start_address` (dladdr doesn't always
185e8d8bef9SDimitry Andric   // ensure that `addr >= sym_addr`).
186e8d8bef9SDimitry Andric   if (start_address != AddressInfo::kUnknown && addr >= start_address) {
187480093f4SDimitry Andric     stack->info.function_offset = addr - start_address;
188480093f4SDimitry Andric   }
18968d75effSDimitry Andric   return true;
19068d75effSDimitry Andric }
19168d75effSDimitry Andric 
19268d75effSDimitry Andric bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
19368d75effSDimitry Andric   if (!process_) return false;
19468d75effSDimitry Andric   char command[32];
19568d75effSDimitry Andric   internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
19668d75effSDimitry Andric   const char *buf = process_->SendCommand(command);
19768d75effSDimitry Andric   if (!buf) return false;
19868d75effSDimitry Andric   if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
19968d75effSDimitry Andric                           nullptr, &info->start)) {
20068d75effSDimitry Andric     process_ = nullptr;
20168d75effSDimitry Andric     return false;
20268d75effSDimitry Andric   }
20368d75effSDimitry Andric   return true;
20468d75effSDimitry Andric }
20568d75effSDimitry Andric 
20668d75effSDimitry Andric }  // namespace __sanitizer
20768d75effSDimitry Andric 
20881ad6265SDimitry Andric #endif  // SANITIZER_APPLE
209