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