1 // SPDX-License-Identifier: GPL-2.0 2 #include "llvm.h" 3 #include "annotate.h" 4 #include "debug.h" 5 #include "dso.h" 6 #include "map.h" 7 #include "namespaces.h" 8 #include "srcline.h" 9 #include "symbol.h" 10 #include <fcntl.h> 11 #include <unistd.h> 12 #include <linux/zalloc.h> 13 14 #ifdef HAVE_LIBLLVM_SUPPORT 15 #include "llvm-c-helpers.h" 16 #include <llvm-c/Disassembler.h> 17 #include <llvm-c/Target.h> 18 #endif 19 20 #ifdef HAVE_LIBLLVM_SUPPORT 21 static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames, 22 int num_frames) 23 { 24 if (inline_frames != NULL) { 25 for (int i = 0; i < num_frames; ++i) { 26 zfree(&inline_frames[i].filename); 27 zfree(&inline_frames[i].funcname); 28 } 29 zfree(&inline_frames); 30 } 31 } 32 #endif 33 34 int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused, 35 char **file __maybe_unused, unsigned int *line __maybe_unused, 36 struct dso *dso __maybe_unused, bool unwind_inlines __maybe_unused, 37 struct inline_node *node __maybe_unused, struct symbol *sym __maybe_unused) 38 { 39 #ifdef HAVE_LIBLLVM_SUPPORT 40 struct llvm_a2l_frame *inline_frames = NULL; 41 int num_frames = llvm_addr2line(dso_name, addr, file, line, 42 node && unwind_inlines, &inline_frames); 43 44 if (num_frames == 0 || !inline_frames) { 45 /* Error, or we didn't want inlines. */ 46 return num_frames; 47 } 48 49 for (int i = 0; i < num_frames; ++i) { 50 struct symbol *inline_sym = 51 new_inline_sym(dso, sym, inline_frames[i].funcname); 52 char *srcline = NULL; 53 54 if (inline_frames[i].filename) { 55 srcline = 56 srcline_from_fileline(inline_frames[i].filename, 57 inline_frames[i].line); 58 } 59 if (inline_list__append(inline_sym, srcline, node) != 0) { 60 free_llvm_inline_frames(inline_frames, num_frames); 61 return 0; 62 } 63 } 64 free_llvm_inline_frames(inline_frames, num_frames); 65 66 return num_frames; 67 #else 68 return -1; 69 #endif 70 } 71 72 void dso__free_a2l_llvm(struct dso *dso __maybe_unused) 73 { 74 /* Nothing to free. */ 75 } 76 77 #ifdef HAVE_LIBLLVM_SUPPORT 78 static void init_llvm(void) 79 { 80 static bool init; 81 82 if (!init) { 83 LLVMInitializeAllTargetInfos(); 84 LLVMInitializeAllTargetMCs(); 85 LLVMInitializeAllDisassemblers(); 86 init = true; 87 } 88 } 89 90 /* 91 * Whenever LLVM wants to resolve an address into a symbol, it calls this 92 * callback. We don't ever actually _return_ anything (in particular, because 93 * it puts quotation marks around what we return), but we use this as a hint 94 * that there is a branch or PC-relative address in the expression that we 95 * should add some textual annotation for after the instruction. The caller 96 * will use this information to add the actual annotation. 97 */ 98 struct symbol_lookup_storage { 99 u64 branch_addr; 100 u64 pcrel_load_addr; 101 }; 102 103 static const char * 104 symbol_lookup_callback(void *disinfo, uint64_t value, 105 uint64_t *ref_type, 106 uint64_t address __maybe_unused, 107 const char **ref __maybe_unused) 108 { 109 struct symbol_lookup_storage *storage = disinfo; 110 111 if (*ref_type == LLVMDisassembler_ReferenceType_In_Branch) 112 storage->branch_addr = value; 113 else if (*ref_type == LLVMDisassembler_ReferenceType_In_PCrel_Load) 114 storage->pcrel_load_addr = value; 115 *ref_type = LLVMDisassembler_ReferenceType_InOut_None; 116 return NULL; 117 } 118 #endif 119 120 int symbol__disassemble_llvm(const char *filename, struct symbol *sym, 121 struct annotate_args *args __maybe_unused) 122 { 123 #ifdef HAVE_LIBLLVM_SUPPORT 124 struct annotation *notes = symbol__annotation(sym); 125 struct map *map = args->ms.map; 126 struct dso *dso = map__dso(map); 127 u64 start = map__rip_2objdump(map, sym->start); 128 /* Malloc-ed buffer containing instructions read from disk. */ 129 u8 *code_buf = NULL; 130 /* Pointer to code to be disassembled. */ 131 const u8 *buf; 132 u64 buf_len; 133 u64 pc; 134 bool is_64bit; 135 char disasm_buf[2048]; 136 size_t disasm_len; 137 struct disasm_line *dl; 138 LLVMDisasmContextRef disasm = NULL; 139 struct symbol_lookup_storage storage; 140 char *line_storage = NULL; 141 size_t line_storage_len = 0; 142 int ret = -1; 143 144 if (args->options->objdump_path) 145 return -1; 146 147 buf = dso__read_symbol(dso, filename, map, sym, 148 &code_buf, &buf_len, &is_64bit); 149 if (buf == NULL) 150 return -1; 151 152 init_llvm(); 153 if (arch__is(args->arch, "x86")) { 154 const char *triplet = is_64bit ? "x86_64-pc-linux" : "i686-pc-linux"; 155 156 disasm = LLVMCreateDisasm(triplet, &storage, /*tag_type=*/0, 157 /*get_op_info=*/NULL, symbol_lookup_callback); 158 } else { 159 char triplet[64]; 160 161 scnprintf(triplet, sizeof(triplet), "%s-linux-gnu", 162 args->arch->name); 163 disasm = LLVMCreateDisasm(triplet, &storage, /*tag_type=*/0, 164 /*get_op_info=*/NULL, symbol_lookup_callback); 165 } 166 167 if (disasm == NULL) 168 goto err; 169 170 if (args->options->disassembler_style && 171 !strcmp(args->options->disassembler_style, "intel")) 172 LLVMSetDisasmOptions(disasm, 173 LLVMDisassembler_Option_AsmPrinterVariant); 174 175 /* 176 * This needs to be set after AsmPrinterVariant, due to a bug in LLVM; 177 * setting AsmPrinterVariant makes a new instruction printer, making it 178 * forget about the PrintImmHex flag (which is applied before if both 179 * are given to the same call). 180 */ 181 LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex); 182 183 /* add the function address and name */ 184 scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:", 185 start, sym->name); 186 187 args->offset = -1; 188 args->line = disasm_buf; 189 args->line_nr = 0; 190 args->fileloc = NULL; 191 args->ms.sym = sym; 192 193 dl = disasm_line__new(args); 194 if (dl == NULL) 195 goto err; 196 197 annotation_line__add(&dl->al, ¬es->src->source); 198 199 pc = start; 200 for (u64 offset = 0; offset < buf_len; ) { 201 unsigned int ins_len; 202 203 storage.branch_addr = 0; 204 storage.pcrel_load_addr = 0; 205 206 /* 207 * LLVM's API has the code be disassembled as non-const, cast 208 * here as we may be disassembling from mapped read-only memory. 209 */ 210 ins_len = LLVMDisasmInstruction(disasm, (u8 *)(buf + offset), 211 buf_len - offset, pc, 212 disasm_buf, sizeof(disasm_buf)); 213 if (ins_len == 0) 214 goto err; 215 disasm_len = strlen(disasm_buf); 216 217 if (storage.branch_addr != 0) { 218 char *name = llvm_name_for_code(dso, filename, 219 storage.branch_addr); 220 if (name != NULL) { 221 disasm_len += scnprintf(disasm_buf + disasm_len, 222 sizeof(disasm_buf) - 223 disasm_len, 224 " <%s>", name); 225 free(name); 226 } 227 } 228 if (storage.pcrel_load_addr != 0) { 229 char *name = llvm_name_for_data(dso, filename, 230 storage.pcrel_load_addr); 231 disasm_len += scnprintf(disasm_buf + disasm_len, 232 sizeof(disasm_buf) - disasm_len, 233 " # %#"PRIx64, 234 storage.pcrel_load_addr); 235 if (name) { 236 disasm_len += scnprintf(disasm_buf + disasm_len, 237 sizeof(disasm_buf) - 238 disasm_len, 239 " <%s>", name); 240 free(name); 241 } 242 } 243 244 args->offset = offset; 245 args->line = expand_tabs(disasm_buf, &line_storage, 246 &line_storage_len); 247 args->line_nr = 0; 248 args->fileloc = NULL; 249 args->ms.sym = sym; 250 251 llvm_addr2line(filename, pc, &args->fileloc, 252 (unsigned int *)&args->line_nr, false, NULL); 253 254 dl = disasm_line__new(args); 255 if (dl == NULL) 256 goto err; 257 258 annotation_line__add(&dl->al, ¬es->src->source); 259 260 free(args->fileloc); 261 pc += ins_len; 262 offset += ins_len; 263 } 264 265 ret = 0; 266 267 err: 268 LLVMDisasmDispose(disasm); 269 free(code_buf); 270 free(line_storage); 271 return ret; 272 #else // HAVE_LIBLLVM_SUPPORT 273 pr_debug("The LLVM disassembler isn't linked in for %s in %s\n", 274 sym->name, filename); 275 return -1; 276 #endif 277 } 278