blob: 13c2c2d0b5346824731b6b47426762555dc1a299 [file] [log] [blame]
Chris Lattner24943d22010-06-08 16:52:24 +00001//===-- Symbols.cpp ---------------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/Host/Symbols.h"
11
12// C Includes
13#include <dirent.h>
Greg Clayton1674b122010-07-21 22:12:05 +000014#include "llvm/Support/MachO.h"
Chris Lattner24943d22010-06-08 16:52:24 +000015
16// C++ Includes
17// Other libraries and framework includes
18#include <CoreFoundation/CoreFoundation.h>
19
20// Project includes
Chris Lattner24943d22010-06-08 16:52:24 +000021#include "lldb/Core/ArchSpec.h"
22#include "lldb/Core/DataBuffer.h"
23#include "lldb/Core/DataExtractor.h"
24#include "lldb/Core/Timer.h"
25#include "lldb/Core/UUID.h"
26
Greg Clayton54e7afa2010-07-09 20:39:50 +000027#include "Host/macosx/cfcpp/CFCReleaser.h"
Chris Lattner5bc7b672010-09-08 23:01:14 +000028#include "mach/machine.h"
Greg Clayton54e7afa2010-07-09 20:39:50 +000029
Chris Lattner24943d22010-06-08 16:52:24 +000030using namespace lldb;
31using namespace lldb_private;
Greg Clayton1674b122010-07-21 22:12:05 +000032using namespace llvm::MachO;
Chris Lattner24943d22010-06-08 16:52:24 +000033
34extern "C" {
Greg Clayton54e7afa2010-07-09 20:39:50 +000035
Chris Lattner24943d22010-06-08 16:52:24 +000036CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url);
37CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
Greg Clayton54e7afa2010-07-09 20:39:50 +000038
39}
Chris Lattner24943d22010-06-08 16:52:24 +000040
41static bool
42SkinnyMachOFileContainsArchAndUUID
43(
44 const FileSpec &file_spec,
45 const ArchSpec *arch,
46 const UUID *uuid, // the UUID we are looking for
47 off_t file_offset,
48 DataExtractor& data,
49 uint32_t data_offset,
50 const uint32_t magic
51)
52{
Greg Clayton1674b122010-07-21 22:12:05 +000053 assert(magic == HeaderMagic32 || magic == HeaderMagic32Swapped || magic == HeaderMagic64 || magic == HeaderMagic64Swapped);
54 if (magic == HeaderMagic32 || magic == HeaderMagic64)
Chris Lattner24943d22010-06-08 16:52:24 +000055 data.SetByteOrder (eByteOrderHost);
56 else if (eByteOrderHost == eByteOrderBig)
57 data.SetByteOrder (eByteOrderLittle);
58 else
59 data.SetByteOrder (eByteOrderBig);
60
61 uint32_t i;
62 const uint32_t cputype = data.GetU32(&data_offset); // cpu specifier
63 const uint32_t cpusubtype = data.GetU32(&data_offset); // machine specifier
64 data_offset+=4; // Skip mach file type
65 const uint32_t ncmds = data.GetU32(&data_offset); // number of load commands
66 const uint32_t sizeofcmds = data.GetU32(&data_offset); // the size of all the load commands
67 data_offset+=4; // Skip flags
68
69 // Check the architecture if we have a valid arch pointer
70 if (arch)
71 {
Greg Claytoncf015052010-06-11 03:25:34 +000072 ArchSpec file_arch(eArchTypeMachO, cputype, cpusubtype);
Chris Lattner24943d22010-06-08 16:52:24 +000073
74 if (file_arch != *arch)
75 return false;
76 }
77
78 // The file exists, and if a valid arch pointer was passed in we know
79 // if already matches, so we can return if we aren't looking for a specific
80 // UUID
81 if (uuid == NULL)
82 return true;
83
Greg Clayton1674b122010-07-21 22:12:05 +000084 if (magic == HeaderMagic64Swapped || magic == HeaderMagic64)
Chris Lattner24943d22010-06-08 16:52:24 +000085 data_offset += 4; // Skip reserved field for in mach_header_64
86
87 // Make sure we have enough data for all the load commands
Greg Clayton1674b122010-07-21 22:12:05 +000088 if (magic == HeaderMagic64Swapped || magic == HeaderMagic64)
Chris Lattner24943d22010-06-08 16:52:24 +000089 {
90 if (data.GetByteSize() < sizeof(struct mach_header_64) + sizeofcmds)
91 {
92 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header_64) + sizeofcmds));
93 data.SetData (data_buffer_sp);
94 }
95 }
96 else
97 {
98 if (data.GetByteSize() < sizeof(struct mach_header) + sizeofcmds)
99 {
100 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header) + sizeofcmds));
101 data.SetData (data_buffer_sp);
102 }
103 }
104
105 for (i=0; i<ncmds; i++)
106 {
107 const uint32_t cmd_offset = data_offset; // Save this data_offset in case parsing of the segment goes awry!
108 uint32_t cmd = data.GetU32(&data_offset);
109 uint32_t cmd_size = data.GetU32(&data_offset);
Greg Clayton1674b122010-07-21 22:12:05 +0000110 if (cmd == LoadCommandUUID)
Chris Lattner24943d22010-06-08 16:52:24 +0000111 {
112 UUID file_uuid (data.GetData(&data_offset, 16), 16);
113 return file_uuid == *uuid;
114 }
115 data_offset = cmd_offset + cmd_size;
116 }
117 return false;
118}
119
120bool
121UniversalMachOFileContainsArchAndUUID
122(
123 const FileSpec &file_spec,
124 const ArchSpec *arch,
125 const UUID *uuid,
126 off_t file_offset,
127 DataExtractor& data,
128 uint32_t data_offset,
129 const uint32_t magic
130)
131{
Greg Clayton1674b122010-07-21 22:12:05 +0000132 assert(magic == UniversalMagic || magic == UniversalMagicSwapped);
Chris Lattner24943d22010-06-08 16:52:24 +0000133
134 // Universal mach-o files always have their headers encoded as BIG endian
135 data.SetByteOrder(eByteOrderBig);
136
137 uint32_t i;
138 const uint32_t nfat_arch = data.GetU32(&data_offset); // number of structs that follow
139 const uint32_t fat_header_and_arch_size = sizeof(struct fat_header) + nfat_arch * sizeof(struct fat_arch);
140 if (data.GetByteSize() < fat_header_and_arch_size)
141 {
142 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, fat_header_and_arch_size));
143 data.SetData (data_buffer_sp);
144 }
145
146 for (i=0; i<nfat_arch; i++)
147 {
148 cpu_type_t arch_cputype = data.GetU32(&data_offset); // cpu specifier (int)
149 cpu_subtype_t arch_cpusubtype = data.GetU32(&data_offset); // machine specifier (int)
150 uint32_t arch_offset = data.GetU32(&data_offset); // file offset to this object file
151 // uint32_t arch_size = data.GetU32(&data_offset); // size of this object file
152 // uint32_t arch_align = data.GetU32(&data_offset); // alignment as a power of 2
153 data_offset += 8; // Skip size and align as we don't need those
154 // Only process this slice if the cpu type/subtype matches
155 if (arch)
156 {
Greg Claytoncf015052010-06-11 03:25:34 +0000157 ArchSpec fat_arch(eArchTypeMachO, arch_cputype, arch_cpusubtype);
Chris Lattner24943d22010-06-08 16:52:24 +0000158 if (fat_arch != *arch)
159 continue;
160 }
161
162 // Create a buffer with only the arch slice date in it
163 DataExtractor arch_data;
164 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset + arch_offset, 0x1000));
165 arch_data.SetData(data_buffer_sp);
166 uint32_t arch_data_offset = 0;
167 uint32_t arch_magic = arch_data.GetU32(&arch_data_offset);
168
169 switch (arch_magic)
170 {
Greg Clayton1674b122010-07-21 22:12:05 +0000171 case HeaderMagic32:
172 case HeaderMagic32Swapped:
173 case HeaderMagic64:
174 case HeaderMagic64Swapped:
Chris Lattner24943d22010-06-08 16:52:24 +0000175 if (SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset + arch_offset, arch_data, arch_data_offset, arch_magic))
176 return true;
177 break;
178 }
179 }
180 return false;
181}
182
183static bool
184FileAtPathContainsArchAndUUID
185(
186 const FileSpec &file_spec,
187 const ArchSpec *arch,
188 const UUID *uuid
189)
190{
191 DataExtractor data;
192 off_t file_offset = 0;
193 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, 0x1000));
194
195 if (data_buffer_sp && data_buffer_sp->GetByteSize() > 0)
196 {
197 data.SetData(data_buffer_sp);
198
199 uint32_t data_offset = 0;
200 uint32_t magic = data.GetU32(&data_offset);
201
202 switch (magic)
203 {
204 // 32 bit mach-o file
Greg Clayton1674b122010-07-21 22:12:05 +0000205 case HeaderMagic32:
206 case HeaderMagic32Swapped:
207 case HeaderMagic64:
208 case HeaderMagic64Swapped:
Chris Lattner24943d22010-06-08 16:52:24 +0000209 return SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic);
210
211 // fat mach-o file
Greg Clayton1674b122010-07-21 22:12:05 +0000212 case UniversalMagic:
213 case UniversalMagicSwapped:
Chris Lattner24943d22010-06-08 16:52:24 +0000214 return UniversalMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic);
215
216 default:
217 break;
218 }
219 }
220 return false;
221}
222
223static FileSpec
224LocateDSYMMachFileInDSYMBundle
225(
226 const FileSpec& dsym_bundle_fspec,
227 const UUID *uuid,
228 const ArchSpec *arch)
229{
230 char path[PATH_MAX];
231
232 FileSpec dsym_fspec;
233
234 if (dsym_bundle_fspec.GetPath(path, sizeof(path)))
235 {
236 ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
237
238 DIR* dirp = ::opendir(path);
239 if (dirp != NULL)
240 {
241 const size_t path_len = strlen(path);
242 const int bytes_left = sizeof(path) - path_len - 1;
243 struct dirent* dp;
244 while ((dp = readdir(dirp)) != NULL)
245 {
246 // Only search directories
247 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
248 {
249 if (dp->d_namlen == 1 && dp->d_name[0] == '.')
250 continue;
251
252 if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
253 continue;
254 }
255
256 if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN)
257 {
258 ::strncpy (&path[path_len], dp->d_name, bytes_left);
259
260 dsym_fspec.SetFile(path);
261 if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid))
262 return dsym_fspec;
263 }
264 }
265 }
266 }
267 dsym_fspec.Clear();
268 return dsym_fspec;
269}
270
271static int
272LocateMacOSXFilesUsingDebugSymbols
273(
274 const FileSpec *exec_fspec, // An executable path that may or may not be correct if UUID is specified
275 const ArchSpec* arch, // Limit the search to files with this architecture if non-NULL
276 const UUID *uuid, // Match the UUID value if non-NULL,
277 FileSpec *out_exec_fspec, // If non-NULL, try and find the executable
278 FileSpec *out_dsym_fspec // If non-NULL try and find the debug symbol file
279)
280{
281 int items_found = 0;
282
283 if (out_exec_fspec)
284 out_exec_fspec->Clear();
285
286 if (out_dsym_fspec)
287 out_dsym_fspec->Clear();
288
289 if (uuid && uuid->IsValid())
290 {
291 // Try and locate the dSYM file using DebugSymbols first
292 const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes();
293 if (module_uuid != NULL)
294 {
295 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes ( NULL,
296 module_uuid[0],
297 module_uuid[1],
298 module_uuid[2],
299 module_uuid[3],
300 module_uuid[4],
301 module_uuid[5],
302 module_uuid[6],
303 module_uuid[7],
304 module_uuid[8],
305 module_uuid[9],
306 module_uuid[10],
307 module_uuid[11],
308 module_uuid[12],
309 module_uuid[13],
310 module_uuid[14],
311 module_uuid[15]));
312
313 if (module_uuid_ref.get())
314 {
315 CFCReleaser<CFURLRef> exec_url;
316
317 if (exec_fspec)
318 {
319 char exec_cf_path[PATH_MAX];
320 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
321 exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL,
322 (const UInt8 *)exec_cf_path,
323 strlen(exec_cf_path),
324 FALSE));
325 }
326
327 CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
328 char path[PATH_MAX];
329
330 if (dsym_url.get())
331 {
332 if (out_dsym_fspec)
333 {
334 if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
335 {
336 out_dsym_fspec->SetFile(path);
337
338 if (out_dsym_fspec->GetFileType () == FileSpec::eFileTypeDirectory)
339 {
340 *out_dsym_fspec = LocateDSYMMachFileInDSYMBundle (*out_dsym_fspec, uuid, arch);
341 if (*out_dsym_fspec)
342 ++items_found;
343 }
344 else
345 {
346 ++items_found;
347 }
348 }
349 }
350
351 if (out_exec_fspec)
352 {
353 CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));;
354 if (dict.get())
355 {
356 CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (dict.get(), CFSTR("DBGSymbolRichExecutable")));
357 if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path)))
358 {
359 ++items_found;
360 out_dsym_fspec->SetFile(path);
361 }
362 }
363 }
364 }
365 }
366 }
367 }
368 return items_found;
369}
370
371static bool
372LocateDSYMInVincinityOfExecutable (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid, FileSpec &dsym_fspec)
373{
374 if (exec_fspec)
375 {
376 char path[PATH_MAX];
377 if (exec_fspec->GetPath(path, sizeof(path)))
378 {
379 // Make sure the module isn't already just a dSYM file...
380 if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL)
381 {
382 size_t obj_file_path_length = strlen(path);
383 strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path));
384 strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path));
385
386 dsym_fspec.SetFile(path);
387
388 if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid))
389 {
390 return true;
391 }
392 else
393 {
394 path[obj_file_path_length] = '\0';
395
396 char *last_dot = strrchr(path, '.');
397 while (last_dot != NULL && last_dot[0])
398 {
399 char *next_slash = strchr(last_dot, '/');
400 if (next_slash != NULL)
401 {
402 *next_slash = '\0';
403 strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path));
404 strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path));
405 dsym_fspec.SetFile(path);
406 if (dsym_fspec.Exists())
407 return true;
408 else
409 {
410 *last_dot = '\0';
411 char *prev_slash = strrchr(path, '/');
412 if (prev_slash != NULL)
413 *prev_slash = '\0';
414 else
415 break;
416 }
417 }
418 else
419 {
420 break;
421 }
422 }
423 }
424 }
425 }
426 }
427 dsym_fspec.Clear();
428 return false;
429}
430
431FileSpec
432Symbols::LocateExecutableObjectFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid)
433{
434 Timer scoped_timer (__PRETTY_FUNCTION__,
435 "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)",
436 exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>",
437 arch ? arch->AsCString() : "<NULL>",
438 uuid);
439
440 FileSpec objfile_fspec;
441 if (exec_fspec && FileAtPathContainsArchAndUUID (*exec_fspec, arch, uuid))
442 objfile_fspec = *exec_fspec;
443 else
444 LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, &objfile_fspec, NULL);
445 return objfile_fspec;
446}
447
448FileSpec
449Symbols::LocateExecutableSymbolFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid)
450{
451 Timer scoped_timer (__PRETTY_FUNCTION__,
452 "LocateExecutableSymbolFile (file = %s, arch = %s, uuid = %p)",
453 exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>",
454 arch ? arch->AsCString() : "<NULL>",
455 uuid);
456
457 FileSpec symbol_fspec;
458 // First try and find the dSYM in the same directory as the executable or in
459 // an appropriate parent directory
460 if (LocateDSYMInVincinityOfExecutable (exec_fspec, arch, uuid, symbol_fspec) == false)
461 {
462 // We failed to easily find the dSYM above, so use DebugSymbols
463 LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, NULL, &symbol_fspec);
464 }
465 return symbol_fspec;
466}