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 executable target without adding dependent shared
34 // libraries. It will then set a regular expression breakpoint to get
35 // breakpoint locations for all functions in the module, and use the
36 // locations to extract the symbol context for each location. Then it
37 // dumps all // information about the function: its name, file address
38 // range, the return type (if any), and all argument types.
39 //
40 // To build the program, type (while in this directory):
41 //
42 //    $ make
43 //
44 // then to run this on MacOSX, specify the path to your LLDB.framework
45 // library using the DYLD_FRAMEWORK_PATH option and run the executable
46 //
47 //    $ DYLD_FRAMEWORK_PATH=/Volumes/data/lldb/tot/build/Debug ./a.out
48 //    executable_path1 [executable_path2 ...]
49 class LLDBSentry {
50 public:
LLDBSentry()51   LLDBSentry() {
52     // Initialize LLDB
53     SBDebugger::Initialize();
54   }
~LLDBSentry()55   ~LLDBSentry() {
56     // Terminate LLDB
57     SBDebugger::Terminate();
58   }
59 };
60 
61 static struct option g_long_options[] = {
62     {"arch", required_argument, NULL, 'a'},
63     {"canonical", no_argument, NULL, 'c'},
64     {"extern", no_argument, NULL, 'x'},
65     {"help", no_argument, NULL, 'h'},
66     {"platform", required_argument, NULL, 'p'},
67     {"verbose", no_argument, NULL, 'v'},
68     {NULL, 0, NULL, 0}};
69 
70 #define PROGRAM_NAME "lldb-functions"
usage()71 void usage() {
72   puts("NAME\n"
73        "    " PROGRAM_NAME
74        " -- extract all function signatures from one or more binaries.\n"
75        "\n"
76        "SYNOPSIS\n"
77        "    " PROGRAM_NAME " [[--arch=<ARCH>] [--platform=<PLATFORM>] "
78                            "[--verbose] [--help] [--canonical] --] <PATH> "
79                            "[<PATH>....]\n"
80        "\n"
81        "DESCRIPTION\n"
82        "    Loads the executable pointed to by <PATH> and dumps complete "
83        "signatures for all functions that have debug information.\n"
84        "\n"
85        "EXAMPLE\n"
86        "   " PROGRAM_NAME " --arch=x86_64 /usr/lib/dyld\n");
87   exit(0);
88 }
main(int argc,char const * argv[])89 int main(int argc, char const *argv[]) {
90   // Use a sentry object to properly initialize/terminate LLDB.
91   LLDBSentry sentry;
92 
93   SBDebugger debugger(SBDebugger::Create());
94 
95   // Create a debugger instance so we can create a target
96   if (!debugger.IsValid())
97     fprintf(stderr, "error: failed to create a debugger object\n");
98 
99   bool show_usage = false;
100   bool verbose = false;
101   bool canonical = false;
102   bool external_only = false;
103   const char *arch = NULL;
104   const char *platform = NULL;
105   std::string short_options("h?");
106   for (const struct option *opt = g_long_options; opt->name; ++opt) {
107     if (isprint(opt->val)) {
108       short_options.append(1, (char)opt->val);
109       switch (opt->has_arg) {
110       case no_argument:
111         break;
112       case required_argument:
113         short_options.append(1, ':');
114         break;
115       case optional_argument:
116         short_options.append(2, ':');
117         break;
118       }
119     }
120   }
121 #ifdef __GLIBC__
122   optind = 0;
123 #else
124   optreset = 1;
125   optind = 1;
126 #endif
127   char ch;
128   while ((ch = getopt_long_only(argc, (char *const *)argv,
129                                 short_options.c_str(), g_long_options, 0)) !=
130          -1) {
131     switch (ch) {
132     case 0:
133       break;
134 
135     case 'a':
136       if (arch != NULL) {
137         fprintf(stderr,
138                 "error: the --arch option can only be specified once\n");
139         exit(1);
140       }
141       arch = optarg;
142       break;
143 
144     case 'c':
145       canonical = true;
146       break;
147 
148     case 'x':
149       external_only = true;
150       break;
151 
152     case 'p':
153       platform = optarg;
154       break;
155 
156     case 'v':
157       verbose = true;
158       break;
159 
160     case 'h':
161     case '?':
162     default:
163       show_usage = true;
164       break;
165     }
166   }
167   argc -= optind;
168   argv += optind;
169 
170   const bool add_dependent_libs = false;
171   SBError error;
172   for (int arg_idx = 0; arg_idx < argc; ++arg_idx) {
173     // The first argument is the file path we want to look something up in
174     const char *exe_file_path = argv[arg_idx];
175 
176     // Create a target using the executable.
177     SBTarget target = debugger.CreateTarget(exe_file_path, arch, platform,
178                                             add_dependent_libs, error);
179 
180     if (error.Success()) {
181       if (target.IsValid()) {
182         SBFileSpec exe_file_spec(exe_file_path, true);
183         SBModule module(target.FindModule(exe_file_spec));
184         SBFileSpecList comp_unit_list;
185 
186         if (module.IsValid()) {
187           char command[1024];
188           lldb::SBCommandReturnObject command_result;
189           snprintf(command, sizeof(command), "add-dsym --uuid %s",
190                    module.GetUUIDString());
191           debugger.GetCommandInterpreter().HandleCommand(command,
192                                                          command_result);
193           if (!command_result.Succeeded()) {
194             fprintf(stderr, "error: couldn't locate debug symbols for '%s'\n",
195                     exe_file_path);
196             exit(1);
197           }
198 
199           SBFileSpecList module_list;
200           module_list.Append(exe_file_spec);
201           SBBreakpoint bp =
202               target.BreakpointCreateByRegex(".", module_list, comp_unit_list);
203 
204           const size_t num_locations = bp.GetNumLocations();
205           for (uint32_t bp_loc_idx = 0; bp_loc_idx < num_locations;
206                ++bp_loc_idx) {
207             SBBreakpointLocation bp_loc = bp.GetLocationAtIndex(bp_loc_idx);
208             SBSymbolContext sc(
209                 bp_loc.GetAddress().GetSymbolContext(eSymbolContextEverything));
210             if (sc.IsValid()) {
211               if (sc.GetBlock().GetContainingInlinedBlock().IsValid()) {
212                 // Skip inlined functions
213                 continue;
214               }
215               SBFunction function(sc.GetFunction());
216               if (function.IsValid()) {
217                 addr_t lo_pc = function.GetStartAddress().GetFileAddress();
218                 if (lo_pc == LLDB_INVALID_ADDRESS) {
219                   // Skip functions that don't have concrete instances in the
220                   // binary
221                   continue;
222                 }
223                 addr_t hi_pc = function.GetEndAddress().GetFileAddress();
224                 const char *func_demangled_name = function.GetName();
225                 const char *func_mangled_name = function.GetMangledName();
226 
227                 bool dump = true;
228                 const bool is_objc_method = ((func_demangled_name[0] == '-') ||
229                                              (func_demangled_name[0] == '+')) &&
230                                             (func_demangled_name[1] == '[');
231                 if (external_only) {
232                   // Dump all objective C methods, or external symbols
233                   dump = is_objc_method;
234                   if (!dump)
235                     dump = sc.GetSymbol().IsExternal();
236                 }
237 
238                 if (dump) {
239                   if (verbose) {
240                     printf("\n   name: %s\n", func_demangled_name);
241                     if (func_mangled_name)
242                       printf("mangled: %s\n", func_mangled_name);
243                     printf("  range: [0x%16.16llx - 0x%16.16llx)\n   type: ",
244                            lo_pc, hi_pc);
245                   } else {
246                     printf("[0x%16.16llx - 0x%16.16llx) ", lo_pc, hi_pc);
247                   }
248                   SBType function_type = function.GetType();
249                   SBType return_type = function_type.GetFunctionReturnType();
250 
251                   if (canonical)
252                     return_type = return_type.GetCanonicalType();
253 
254                   if (func_mangled_name && func_mangled_name[0] == '_' &&
255                       func_mangled_name[1] == 'Z') {
256                     printf("%s %s\n", return_type.GetName(),
257                            func_demangled_name);
258                   } else {
259                     SBTypeList function_args =
260                         function_type.GetFunctionArgumentTypes();
261                     const size_t num_function_args = function_args.GetSize();
262 
263                     if (is_objc_method) {
264                       const char *class_name_start = func_demangled_name + 2;
265 
266                       if (num_function_args == 0) {
267                         printf("%c(%s)[%s\n", func_demangled_name[0],
268                                return_type.GetName(), class_name_start);
269                       } else {
270                         const char *class_name_end =
271                             strchr(class_name_start, ' ');
272                         const int class_name_len =
273                             class_name_end - class_name_start;
274                         printf("%c(%s)[%*.*s", func_demangled_name[0],
275                                return_type.GetName(), class_name_len,
276                                class_name_len, class_name_start);
277 
278                         const char *selector_pos = class_name_end + 1;
279                         for (uint32_t function_arg_idx = 0;
280                              function_arg_idx < num_function_args;
281                              ++function_arg_idx) {
282                           const char *selector_end =
283                               strchr(selector_pos, ':') + 1;
284                           const int selector_len = selector_end - selector_pos;
285                           SBType function_arg_type =
286                               function_args.GetTypeAtIndex(function_arg_idx);
287 
288                           if (canonical)
289                             function_arg_type =
290                                 function_arg_type.GetCanonicalType();
291 
292                           printf(" %*.*s", selector_len, selector_len,
293                                  selector_pos);
294                           if (function_arg_type.IsValid()) {
295                             printf("(%s)", function_arg_type.GetName());
296                           } else {
297                             printf("(?)");
298                           }
299                           selector_pos = selector_end;
300                         }
301                         printf("]\n");
302                       }
303                     } else {
304                       printf("%s ", return_type.GetName());
305                       if (strchr(func_demangled_name, '('))
306                         printf("(*)(");
307                       else
308                         printf("%s(", func_demangled_name);
309 
310                       for (uint32_t function_arg_idx = 0;
311                            function_arg_idx < num_function_args;
312                            ++function_arg_idx) {
313                         SBType function_arg_type =
314                             function_args.GetTypeAtIndex(function_arg_idx);
315 
316                         if (canonical)
317                           function_arg_type =
318                               function_arg_type.GetCanonicalType();
319 
320                         if (function_arg_type.IsValid()) {
321                           printf("%s%s", function_arg_idx > 0 ? ", " : "",
322                                  function_arg_type.GetName());
323                         } else {
324                           printf("%s???", function_arg_idx > 0 ? ", " : "");
325                         }
326                       }
327                       printf(")\n");
328                     }
329                   }
330                 }
331               }
332             }
333           }
334         }
335       }
336     } else {
337       fprintf(stderr, "error: %s\n", error.GetCString());
338       exit(1);
339     }
340   }
341 
342   return 0;
343 }
344