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 | |