1 // -*- mode: c++ -*-
2
3 // Copyright 2011 Google LLC
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google LLC nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 // dump_syms_tool.cc: Command line tool that uses the DumpSymbols class.
32 // TODO(waylonis): accept stdin
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h> // Must come first
36 #endif
37
38 #include <mach-o/arch.h>
39 #include <unistd.h>
40
41 #include <algorithm>
42 #include <iostream>
43 #include <memory>
44 #include <utility>
45 #include <vector>
46
47 #include "common/mac/dump_syms.h"
48 #include "common/mac/arch_utilities.h"
49 #include "common/mac/macho_utilities.h"
50 #include "common/scoped_ptr.h"
51
52 using google_breakpad::DumpSymbols;
53 using google_breakpad::Module;
54 using google_breakpad::scoped_ptr;
55 using std::vector;
56
57 struct Options {
OptionsOptions58 Options()
59 : srcPath(),
60 dsymPath(),
61 arch(),
62 header_only(false),
63 cfi(true),
64 handle_inter_cu_refs(true),
65 handle_inlines(false),
66 enable_multiple(false),
67 module_name(),
68 prefer_extern_name(false) {}
69
70 string srcPath;
71 string dsymPath;
72 std::optional<ArchInfo> arch;
73 bool header_only;
74 bool cfi;
75 bool handle_inter_cu_refs;
76 bool handle_inlines;
77 bool enable_multiple;
78 string module_name;
79 bool prefer_extern_name;
80 };
81
StackFrameEntryComparator(const Module::StackFrameEntry * a,const Module::StackFrameEntry * b)82 static bool StackFrameEntryComparator(const Module::StackFrameEntry* a,
83 const Module::StackFrameEntry* b) {
84 return a->address < b->address;
85 }
86
87 // Copy the CFI data from |from_module| into |to_module|, for any non-
88 // overlapping ranges.
CopyCFIDataBetweenModules(Module * to_module,const Module * from_module)89 static void CopyCFIDataBetweenModules(Module* to_module,
90 const Module* from_module) {
91 typedef vector<Module::StackFrameEntry*>::const_iterator Iterator;
92
93 // Get the CFI data from both the source and destination modules and ensure
94 // it is sorted by start address.
95 vector<Module::StackFrameEntry*> from_data;
96 from_module->GetStackFrameEntries(&from_data);
97 std::sort(from_data.begin(), from_data.end(), &StackFrameEntryComparator);
98
99 vector<Module::StackFrameEntry*> to_data;
100 to_module->GetStackFrameEntries(&to_data);
101 std::sort(to_data.begin(), to_data.end(), &StackFrameEntryComparator);
102
103 Iterator to_it = to_data.begin();
104
105 for (Iterator it = from_data.begin(); it != from_data.end(); ++it) {
106 Module::StackFrameEntry* from_entry = *it;
107 Module::Address from_entry_end = from_entry->address + from_entry->size;
108
109 // Find the first CFI record in the |to_module| that does not have an
110 // address less than the entry to be copied.
111 while (to_it != to_data.end()) {
112 if (from_entry->address > (*to_it)->address)
113 ++to_it;
114 else
115 break;
116 }
117
118 // If the entry does not overlap, then it is safe to copy to |to_module|.
119 if (to_it == to_data.end() || (from_entry->address < (*to_it)->address &&
120 from_entry_end < (*to_it)->address)) {
121 to_module->AddStackFrameEntry(
122 std::make_unique<Module::StackFrameEntry>(*from_entry));
123 }
124 }
125 }
126
SetArchitecture(DumpSymbols & dump_symbols,const ArchInfo & arch,const std::string & filename)127 static bool SetArchitecture(DumpSymbols& dump_symbols,
128 const ArchInfo& arch,
129 const std::string& filename) {
130 if (!dump_symbols.SetArchitecture(arch)) {
131 fprintf(stderr, "%s: no architecture '%s' is present in file.\n",
132 filename.c_str(),
133 GetNameFromCPUType(arch.cputype, arch.cpusubtype));
134 size_t available_size;
135 const SuperFatArch* available =
136 dump_symbols.AvailableArchitectures(&available_size);
137 if (available_size == 1)
138 fprintf(stderr, "the file's architecture is: ");
139 else
140 fprintf(stderr, "architectures present in the file are:\n");
141 for (size_t i = 0; i < available_size; i++) {
142 const SuperFatArch* arch = &available[i];
143 fprintf(stderr, "%s\n",
144 GetNameFromCPUType(arch->cputype, arch->cpusubtype));
145 }
146 return false;
147 }
148 return true;
149 }
150
Start(const Options & options)151 static bool Start(const Options& options) {
152 SymbolData symbol_data =
153 (options.handle_inlines ? INLINES : NO_DATA) |
154 (options.cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES;
155 DumpSymbols dump_symbols(symbol_data, options.handle_inter_cu_refs,
156 options.enable_multiple, options.module_name,
157 options.prefer_extern_name);
158
159 // For x86_64 binaries, the CFI data is in the __TEXT,__eh_frame of the
160 // Mach-O file, which is not copied into the dSYM. Whereas in i386, the CFI
161 // data is in the __DWARF,__debug_frame section, which is moved into the
162 // dSYM. Therefore, to get x86_64 CFI data, dump_syms needs to look at both
163 // the dSYM and the Mach-O file. If both paths are present and CFI was
164 // requested, then consider the Module as "split" and dump all the debug data
165 // from the primary debug info file, the dSYM, and then dump additional CFI
166 // data from the source Mach-O file.
167 bool split_module =
168 !options.dsymPath.empty() && !options.srcPath.empty() && options.cfi;
169 const string& primary_file =
170 split_module ? options.dsymPath : options.srcPath;
171
172 if (!dump_symbols.Read(primary_file))
173 return false;
174
175 if (options.arch &&
176 !SetArchitecture(dump_symbols, *options.arch, primary_file)) {
177 return false;
178 }
179
180 if (options.header_only)
181 return dump_symbols.WriteSymbolFileHeader(std::cout);
182
183 // Read the primary file into a Breakpad Module.
184 Module* module = NULL;
185 if (!dump_symbols.ReadSymbolData(&module))
186 return false;
187 scoped_ptr<Module> scoped_module(module);
188
189 // If this is a split module, read the secondary Mach-O file, from which the
190 // CFI data will be extracted.
191 if (split_module && primary_file == options.dsymPath) {
192 if (!dump_symbols.Read(options.srcPath))
193 return false;
194
195 if (options.arch &&
196 !SetArchitecture(dump_symbols, *options.arch, options.srcPath)) {
197 return false;
198 }
199 Module* cfi_module = NULL;
200 if (!dump_symbols.ReadSymbolData(&cfi_module))
201 return false;
202 scoped_ptr<Module> scoped_cfi_module(cfi_module);
203
204 bool name_matches;
205 if (!options.module_name.empty()) {
206 // Ignore the basename of the dSYM and binary and use the passed-in module
207 // name.
208 name_matches = true;
209 } else {
210 name_matches = cfi_module->name() == module->name();
211 }
212
213 // Ensure that the modules are for the same debug code file.
214 if (!name_matches || cfi_module->os() != module->os() ||
215 cfi_module->architecture() != module->architecture() ||
216 cfi_module->identifier() != module->identifier()) {
217 fprintf(stderr, "Cannot generate a symbol file from split sources that do"
218 " not match.\n");
219 if (!name_matches) {
220 fprintf(stderr, "Name mismatch: binary=[%s], dSYM=[%s]\n",
221 cfi_module->name().c_str(), module->name().c_str());
222 }
223 if (cfi_module->os() != module->os()) {
224 fprintf(stderr, "OS mismatch: binary=[%s], dSYM=[%s]\n",
225 cfi_module->os().c_str(), module->os().c_str());
226 }
227 if (cfi_module->architecture() != module->architecture()) {
228 fprintf(stderr, "Architecture mismatch: binary=[%s], dSYM=[%s]\n",
229 cfi_module->architecture().c_str(),
230 module->architecture().c_str());
231 }
232 if (cfi_module->identifier() != module->identifier()) {
233 fprintf(stderr, "Identifier mismatch: binary=[%s], dSYM=[%s]\n",
234 cfi_module->identifier().c_str(), module->identifier().c_str());
235 }
236 return false;
237 }
238
239 CopyCFIDataBetweenModules(module, cfi_module);
240 }
241
242 return module->Write(std::cout, symbol_data);
243 }
244
245 //=============================================================================
Usage(int argc,const char * argv[])246 static void Usage(int argc, const char *argv[]) {
247 fprintf(stderr, "Output a Breakpad symbol file from a Mach-o file.\n");
248 fprintf(stderr,
249 "Usage: %s [-a ARCHITECTURE] [-c] [-g dSYM path] "
250 "[-n MODULE] [-x] <Mach-o file>\n",
251 argv[0]);
252 fprintf(stderr, "\t-i: Output module header information only.\n");
253 fprintf(stderr, "\t-a: Architecture type [default: native, or whatever is\n");
254 fprintf(stderr, "\t in the file, if it contains only one architecture]\n");
255 fprintf(stderr, "\t-g: Debug symbol file (dSYM) to dump in addition to the "
256 "Mach-o file\n");
257 fprintf(stderr, "\t-c: Do not generate CFI section\n");
258 fprintf(stderr, "\t-r: Do not handle inter-compilation unit references\n");
259 fprintf(stderr, "\t-d: Generate INLINE and INLINE_ORIGIN records\n");
260 fprintf(stderr,
261 "\t-m: Enable writing the optional 'm' field on FUNC "
262 "and PUBLIC, denoting multiple symbols for the address.\n");
263 fprintf(stderr,
264 "\t-n: Use MODULE as the name of the module rather than \n"
265 "the basename of the Mach-O file/dSYM.\n");
266 fprintf(stderr,
267 "\t-x: Prefer the PUBLIC (extern) name over the FUNC if\n"
268 "they do not match.\n");
269 fprintf(stderr, "\t-h: Usage\n");
270 fprintf(stderr, "\t-?: Usage\n");
271 }
272
273 //=============================================================================
SetupOptions(int argc,const char * argv[],Options * options)274 static void SetupOptions(int argc, const char *argv[], Options *options) {
275 extern int optind;
276 signed char ch;
277
278 while ((ch = getopt(argc, (char* const*)argv, "ia:g:crdm?hn:x")) != -1) {
279 switch (ch) {
280 case 'i':
281 options->header_only = true;
282 break;
283 case 'a': {
284 std::optional<ArchInfo> arch_info = GetArchInfoFromName(optarg);
285 if (!arch_info) {
286 fprintf(stderr, "%s: Invalid architecture: %s\n", argv[0], optarg);
287 Usage(argc, argv);
288 exit(1);
289 }
290 options->arch = arch_info;
291 break;
292 }
293 case 'g':
294 options->dsymPath = optarg;
295 break;
296 case 'c':
297 options->cfi = false;
298 break;
299 case 'r':
300 options->handle_inter_cu_refs = false;
301 break;
302 case 'd':
303 options->handle_inlines = true;
304 break;
305 case 'm':
306 options->enable_multiple = true;
307 break;
308 case 'n':
309 options->module_name = optarg;
310 break;
311 case 'x':
312 options->prefer_extern_name = true;
313 break;
314 case '?':
315 case 'h':
316 Usage(argc, argv);
317 exit(0);
318 break;
319 }
320 }
321
322 if ((argc - optind) != 1) {
323 fprintf(stderr, "Must specify Mach-o file\n");
324 Usage(argc, argv);
325 exit(1);
326 }
327
328 options->srcPath = argv[optind];
329 }
330
331 //=============================================================================
main(int argc,const char * argv[])332 int main (int argc, const char * argv[]) {
333 Options options;
334 bool result;
335
336 SetupOptions(argc, argv, &options);
337 result = Start(options);
338
339 return !result;
340 }
341