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