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