1 // SPDX-License-Identifier: GPL-2.0 2 3 /* 4 * Must come before the linux/compiler.h include, which defines several 5 * macros (e.g. noinline) that conflict with compiler builtins used 6 * by LLVM. 7 */ 8 #pragma GCC diagnostic push 9 #pragma GCC diagnostic ignored "-Wunused-parameter" /* Needed for LLVM <= 15 */ 10 #include <llvm/DebugInfo/Symbolize/Symbolize.h> 11 #include <llvm/Support/TargetSelect.h> 12 #pragma GCC diagnostic pop 13 14 #include <inttypes.h> 15 #include <stdio.h> 16 #include <sys/types.h> 17 #include <linux/compiler.h> 18 extern "C" { 19 #include <linux/zalloc.h> 20 } 21 #include "llvm-c-helpers.h" 22 23 extern "C" 24 char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name); 25 26 using namespace llvm; 27 using llvm::symbolize::LLVMSymbolizer; 28 29 /* 30 * Allocate a static LLVMSymbolizer, which will live to the end of the program. 31 * Unlike the bfd paths, LLVMSymbolizer has its own cache, so we do not need 32 * to store anything in the dso struct. 33 */ 34 static LLVMSymbolizer *get_symbolizer() 35 { 36 static LLVMSymbolizer *instance = nullptr; 37 if (instance == nullptr) { 38 LLVMSymbolizer::Options opts; 39 /* 40 * LLVM sometimes demangles slightly different from the rest 41 * of the code, and this mismatch can cause new_inline_sym() 42 * to get confused and mark non-inline symbol as inlined 43 * (since the name does not properly match up with base_sym). 44 * Thus, disable the demangling and let the rest of the code 45 * handle it. 46 */ 47 opts.Demangle = false; 48 instance = new LLVMSymbolizer(opts); 49 } 50 return instance; 51 } 52 53 /* Returns 0 on error, 1 on success. */ 54 static int extract_file_and_line(const DILineInfo &line_info, char **file, 55 unsigned int *line) 56 { 57 if (file) { 58 if (line_info.FileName == "<invalid>") { 59 /* Match the convention of libbfd. */ 60 *file = nullptr; 61 } else { 62 /* The caller expects to get something it can free(). */ 63 *file = strdup(line_info.FileName.c_str()); 64 if (*file == nullptr) 65 return 0; 66 } 67 } 68 if (line) 69 *line = line_info.Line; 70 return 1; 71 } 72 73 extern "C" 74 int llvm_addr2line(const char *dso_name, u64 addr, 75 char **file, unsigned int *line, 76 bool unwind_inlines, 77 llvm_a2l_frame **inline_frames) 78 { 79 LLVMSymbolizer *symbolizer = get_symbolizer(); 80 object::SectionedAddress sectioned_addr = { 81 addr, 82 object::SectionedAddress::UndefSection 83 }; 84 85 if (unwind_inlines) { 86 Expected<DIInliningInfo> res_or_err = 87 symbolizer->symbolizeInlinedCode(dso_name, 88 sectioned_addr); 89 if (!res_or_err) 90 return 0; 91 unsigned num_frames = res_or_err->getNumberOfFrames(); 92 if (num_frames == 0) 93 return 0; 94 95 if (extract_file_and_line(res_or_err->getFrame(0), 96 file, line) == 0) 97 return 0; 98 99 *inline_frames = (llvm_a2l_frame *)calloc( 100 num_frames, sizeof(**inline_frames)); 101 if (*inline_frames == nullptr) 102 return 0; 103 104 for (unsigned i = 0; i < num_frames; ++i) { 105 const DILineInfo &src = res_or_err->getFrame(i); 106 107 llvm_a2l_frame &dst = (*inline_frames)[i]; 108 if (src.FileName == "<invalid>") 109 /* Match the convention of libbfd. */ 110 dst.filename = nullptr; 111 else 112 dst.filename = strdup(src.FileName.c_str()); 113 dst.funcname = strdup(src.FunctionName.c_str()); 114 dst.line = src.Line; 115 116 if (dst.filename == nullptr || 117 dst.funcname == nullptr) { 118 for (unsigned j = 0; j <= i; ++j) { 119 zfree(&(*inline_frames)[j].filename); 120 zfree(&(*inline_frames)[j].funcname); 121 } 122 zfree(inline_frames); 123 return 0; 124 } 125 } 126 127 return num_frames; 128 } else { 129 if (inline_frames) 130 *inline_frames = nullptr; 131 132 Expected<DILineInfo> res_or_err = 133 symbolizer->symbolizeCode(dso_name, sectioned_addr); 134 if (!res_or_err) 135 return 0; 136 return extract_file_and_line(*res_or_err, file, line); 137 } 138 } 139 140 static char * 141 make_symbol_relative_string(struct dso *dso, const char *sym_name, 142 u64 addr, u64 base_addr) 143 { 144 if (!strcmp(sym_name, "<invalid>")) 145 return NULL; 146 147 char *demangled = dso__demangle_sym(dso, 0, sym_name); 148 if (base_addr && base_addr != addr) { 149 char buf[256]; 150 snprintf(buf, sizeof(buf), "%s+0x%" PRIx64, 151 demangled ? demangled : sym_name, addr - base_addr); 152 free(demangled); 153 return strdup(buf); 154 } else { 155 if (demangled) 156 return demangled; 157 else 158 return strdup(sym_name); 159 } 160 } 161 162 extern "C" 163 char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr) 164 { 165 LLVMSymbolizer *symbolizer = get_symbolizer(); 166 object::SectionedAddress sectioned_addr = { 167 addr, 168 object::SectionedAddress::UndefSection 169 }; 170 Expected<DILineInfo> res_or_err = 171 symbolizer->symbolizeCode(dso_name, sectioned_addr); 172 if (!res_or_err) { 173 return NULL; 174 } 175 return make_symbol_relative_string( 176 dso, res_or_err->FunctionName.c_str(), 177 addr, res_or_err->StartAddress ? *res_or_err->StartAddress : 0); 178 } 179 180 extern "C" 181 char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr) 182 { 183 LLVMSymbolizer *symbolizer = get_symbolizer(); 184 object::SectionedAddress sectioned_addr = { 185 addr, 186 object::SectionedAddress::UndefSection 187 }; 188 Expected<DIGlobal> res_or_err = 189 symbolizer->symbolizeData(dso_name, sectioned_addr); 190 if (!res_or_err) { 191 return NULL; 192 } 193 return make_symbol_relative_string( 194 dso, res_or_err->Name.c_str(), 195 addr, res_or_err->Start); 196 } 197