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