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:
LLDBSentry()49   LLDBSentry() {
50     // Initialize LLDB
51     SBDebugger::Initialize();
52   }
~LLDBSentry()53   ~LLDBSentry() {
54     // Terminate LLDB
55     SBDebugger::Terminate();
56   }
57 };
58 
59 static struct option g_long_options[] = {
60     {"help", no_argument, NULL, 'h'},
61     {"verbose", no_argument, NULL, 'v'},
62     {"arch", required_argument, NULL, 'a'},
63     {"platform", required_argument, NULL, 'p'},
64     {NULL, 0, NULL, 0}};
65 
66 #define PROGRAM_NAME "lldb-lookup"
usage()67 void usage() {
68   puts("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(0);
84 }
main(int argc,char const * argv[])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, "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(1, (char)opt->val);
103       switch (opt->has_arg) {
104       case no_argument:
105         break;
106       case required_argument:
107         short_options.append(1, ':');
108         break;
109       case optional_argument:
110         short_options.append(2, ':');
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, (char *const *)argv,
123                                 short_options.c_str(), g_long_options, 0)) !=
124          -1) {
125     switch (ch) {
126     case 0:
127       break;
128 
129     case 'a':
130       if (arch != NULL) {
131         fprintf(stderr,
132                 "error: the --arch option can only be specified once\n");
133         exit(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(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