| 1 | //===-- main.cpp ------------------------------------------------*- C++ -*-===// |
| 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 | #include <getopt.h> |
| 10 | #include <stdint.h> |
| 11 | #include <stdlib.h> |
| 12 | |
| 13 | #if defined(__APPLE__) |
| 14 | #include <LLDB/LLDB.h> |
| 15 | #else |
| 16 | #include "LLDB/SBBlock.h" |
| 17 | #include "LLDB/SBCompileUnit.h" |
| 18 | #include "LLDB/SBDebugger.h" |
| 19 | #include "LLDB/SBFunction.h" |
| 20 | #include "LLDB/SBModule.h" |
| 21 | #include "LLDB/SBProcess.h" |
| 22 | #include "LLDB/SBStream.h" |
| 23 | #include "LLDB/SBSymbol.h" |
| 24 | #include "LLDB/SBTarget.h" |
| 25 | #include "LLDB/SBThread.h" |
| 26 | #endif |
| 27 | |
| 28 | #include <string> |
| 29 | |
| 30 | using namespace lldb; |
| 31 | |
| 32 | // This quick sample code shows how to create a debugger instance and |
| 33 | // create an "i386" executable target. Then we can lookup the executable |
| 34 | // module and resolve a file address into a section offset address, |
| 35 | // and find all symbol context objects (if any) for that address: |
| 36 | // compile unit, function, deepest block, line table entry and the |
| 37 | // symbol. |
| 38 | // |
| 39 | // To build the program, type (while in this directory): |
| 40 | // |
| 41 | // $ make |
| 42 | // |
| 43 | // then (for example): |
| 44 | // |
| 45 | // $ DYLD_FRAMEWORK_PATH=/Volumes/data/lldb/svn/ToT/build/Debug ./a.out |
| 46 | // executable_path file_address |
| 47 | class LLDBSentry { |
| 48 | public: |
| 49 | LLDBSentry() { |
| 50 | // Initialize LLDB |
| 51 | SBDebugger::Initialize(); |
| 52 | } |
| 53 | ~LLDBSentry() { |
| 54 | // Terminate LLDB |
| 55 | SBDebugger::Terminate(); |
| 56 | } |
| 57 | }; |
| 58 | |
| 59 | static struct option g_long_options[] = { |
| 60 | {.name: "help" , no_argument, NULL, .val: 'h'}, |
| 61 | {.name: "verbose" , no_argument, NULL, .val: 'v'}, |
| 62 | {.name: "arch" , required_argument, NULL, .val: 'a'}, |
| 63 | {.name: "platform" , required_argument, NULL, .val: 'p'}, |
| 64 | {NULL, .has_arg: 0, NULL, .val: 0}}; |
| 65 | |
| 66 | #define PROGRAM_NAME "lldb-lookup" |
| 67 | void usage() { |
| 68 | puts(s: "NAME\n" |
| 69 | " " PROGRAM_NAME " -- symbolicate addresses using lldb.\n" |
| 70 | "\n" |
| 71 | "SYNOPSIS\n" |
| 72 | " " PROGRAM_NAME " [[--arch=<ARCH>] [--platform=<PLATFORM>] " |
| 73 | "[--verbose] [--help] --] <PATH> <ADDRESS> " |
| 74 | "[<ADDRESS>....]\n" |
| 75 | "\n" |
| 76 | "DESCRIPTION\n" |
| 77 | " Loads the executable pointed to by <PATH> and looks up and " |
| 78 | "<ADDRESS>\n" |
| 79 | " arguments\n" |
| 80 | "\n" |
| 81 | "EXAMPLE\n" |
| 82 | " " PROGRAM_NAME " --arch=x86_64 -- /usr/lib/dyld 0x100000000\n" ); |
| 83 | exit(status: 0); |
| 84 | } |
| 85 | int main(int argc, char const *argv[]) { |
| 86 | // Use a sentry object to properly initialize/terminate LLDB. |
| 87 | LLDBSentry sentry; |
| 88 | |
| 89 | SBDebugger debugger(SBDebugger::Create()); |
| 90 | |
| 91 | // Create a debugger instance so we can create a target |
| 92 | if (!debugger.IsValid()) |
| 93 | fprintf(stderr, format: "error: failed to create a debugger object\n" ); |
| 94 | |
| 95 | bool show_usage = false; |
| 96 | bool verbose = false; |
| 97 | const char *arch = NULL; |
| 98 | const char *platform = NULL; |
| 99 | std::string short_options("h?" ); |
| 100 | for (const struct option *opt = g_long_options; opt->name; ++opt) { |
| 101 | if (isprint(opt->val)) { |
| 102 | short_options.append(n: 1, c: (char)opt->val); |
| 103 | switch (opt->has_arg) { |
| 104 | case no_argument: |
| 105 | break; |
| 106 | case required_argument: |
| 107 | short_options.append(n: 1, c: ':'); |
| 108 | break; |
| 109 | case optional_argument: |
| 110 | short_options.append(n: 2, c: ':'); |
| 111 | break; |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | #ifdef __GLIBC__ |
| 116 | optind = 0; |
| 117 | #else |
| 118 | optreset = 1; |
| 119 | optind = 1; |
| 120 | #endif |
| 121 | char ch; |
| 122 | while ((ch = getopt_long_only(argc: argc, argv: (char *const *)argv, |
| 123 | shortopts: short_options.c_str(), longopts: g_long_options, longind: 0)) != |
| 124 | -1) { |
| 125 | switch (ch) { |
| 126 | case 0: |
| 127 | break; |
| 128 | |
| 129 | case 'a': |
| 130 | if (arch != NULL) { |
| 131 | fprintf(stderr, |
| 132 | format: "error: the --arch option can only be specified once\n" ); |
| 133 | exit(status: 1); |
| 134 | } |
| 135 | arch = optarg; |
| 136 | break; |
| 137 | |
| 138 | case 'p': |
| 139 | platform = optarg; |
| 140 | break; |
| 141 | |
| 142 | case 'v': |
| 143 | verbose = true; |
| 144 | break; |
| 145 | |
| 146 | case 'h': |
| 147 | case '?': |
| 148 | default: |
| 149 | show_usage = true; |
| 150 | break; |
| 151 | } |
| 152 | } |
| 153 | argc -= optind; |
| 154 | argv += optind; |
| 155 | |
| 156 | if (show_usage || argc < 2) |
| 157 | usage(); |
| 158 | |
| 159 | int arg_idx = 0; |
| 160 | // The first argument is the file path we want to look something up in |
| 161 | const char *exe_file_path = argv[arg_idx]; |
| 162 | const char *addr_cstr; |
| 163 | const bool add_dependent_libs = false; |
| 164 | SBError error; |
| 165 | SBStream strm; |
| 166 | strm.RedirectToFileHandle(stdout, false); |
| 167 | |
| 168 | while ((addr_cstr = argv[++arg_idx]) != NULL) { |
| 169 | // The second argument in the address that we want to lookup |
| 170 | lldb::addr_t file_addr = strtoull(addr_cstr, NULL, 0); |
| 171 | |
| 172 | // Create a target using the executable. |
| 173 | SBTarget target = debugger.CreateTarget(exe_file_path, arch, platform, |
| 174 | add_dependent_libs, error); |
| 175 | if (!error.Success()) { |
| 176 | fprintf(stderr, "error: %s\n" , error.GetCString()); |
| 177 | exit(status: 1); |
| 178 | } |
| 179 | |
| 180 | printf("%sLooking up 0x%llx in '%s':\n" , (arg_idx > 1) ? "\n" : "" , |
| 181 | file_addr, exe_file_path); |
| 182 | |
| 183 | if (target.IsValid()) { |
| 184 | // Find the executable module so we can do a lookup inside it |
| 185 | SBFileSpec exe_file_spec(exe_file_path, true); |
| 186 | SBModule module(target.FindModule(exe_file_spec)); |
| 187 | |
| 188 | // Take a file virtual address and resolve it to a section offset |
| 189 | // address that can be used to do a symbol lookup by address |
| 190 | SBAddress addr = module.ResolveFileAddress(file_addr); |
| 191 | bool success = addr.IsValid() && addr.GetSection().IsValid(); |
| 192 | if (success) { |
| 193 | // We can resolve a section offset address in the module |
| 194 | // and only ask for what we need. You can logical or together |
| 195 | // bits from the SymbolContextItem enumeration found in |
| 196 | // lldb-enumeration.h to request only what you want. Here we |
| 197 | // are asking for everything. |
| 198 | // |
| 199 | // NOTE: the less you ask for, the less LLDB will parse as |
| 200 | // LLDB does partial parsing on just about everything. |
| 201 | SBSymbolContext sc(module.ResolveSymbolContextForAddress( |
| 202 | addr, eSymbolContextEverything)); |
| 203 | |
| 204 | strm.Printf(" Address: %s + 0x%llx\n Summary: " , |
| 205 | addr.GetSection().GetName(), addr.GetOffset()); |
| 206 | addr.GetDescription(strm); |
| 207 | strm.Printf("\n" ); |
| 208 | if (verbose) |
| 209 | sc.GetDescription(strm); |
| 210 | } else { |
| 211 | printf( |
| 212 | "error: 0x%llx does not resolve to a valid file address in '%s'\n" , |
| 213 | file_addr, exe_file_path); |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | return 0; |
| 219 | } |
| 220 | |