blob: 87b1dbbdb99a242d0b9eb70e0b3f2fe7256f7426 [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"
Greg Claytonad400272011-02-01 05:15:02 +000028#include "lldb/Utility/CleanUp.h"
Chris Lattner24943d22010-06-08 16:52:24 +000029
Greg Clayton54e7afa2010-07-09 20:39:50 +000030#include "Host/macosx/cfcpp/CFCReleaser.h"
Chris Lattner5bc7b672010-09-08 23:01:14 +000031#include "mach/machine.h"
Greg Clayton54e7afa2010-07-09 20:39:50 +000032
Chris Lattner24943d22010-06-08 16:52:24 +000033using namespace lldb;
34using namespace lldb_private;
Greg Clayton1674b122010-07-21 22:12:05 +000035using namespace llvm::MachO;
Chris Lattner24943d22010-06-08 16:52:24 +000036
37extern "C" {
Greg Clayton54e7afa2010-07-09 20:39:50 +000038
Chris Lattner24943d22010-06-08 16:52:24 +000039CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url);
40CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
Greg Clayton54e7afa2010-07-09 20:39:50 +000041
42}
Chris Lattner24943d22010-06-08 16:52:24 +000043
44static bool
45SkinnyMachOFileContainsArchAndUUID
46(
47 const FileSpec &file_spec,
48 const ArchSpec *arch,
49 const UUID *uuid, // the UUID we are looking for
50 off_t file_offset,
51 DataExtractor& data,
52 uint32_t data_offset,
53 const uint32_t magic
54)
55{
Greg Clayton1674b122010-07-21 22:12:05 +000056 assert(magic == HeaderMagic32 || magic == HeaderMagic32Swapped || magic == HeaderMagic64 || magic == HeaderMagic64Swapped);
57 if (magic == HeaderMagic32 || magic == HeaderMagic64)
Greg Claytoncd548032011-02-01 01:31:41 +000058 data.SetByteOrder (lldb::endian::InlHostByteOrder());
59 else if (lldb::endian::InlHostByteOrder() == eByteOrderBig)
Chris Lattner24943d22010-06-08 16:52:24 +000060 data.SetByteOrder (eByteOrderLittle);
61 else
62 data.SetByteOrder (eByteOrderBig);
63
64 uint32_t i;
65 const uint32_t cputype = data.GetU32(&data_offset); // cpu specifier
66 const uint32_t cpusubtype = data.GetU32(&data_offset); // machine specifier
67 data_offset+=4; // Skip mach file type
68 const uint32_t ncmds = data.GetU32(&data_offset); // number of load commands
69 const uint32_t sizeofcmds = data.GetU32(&data_offset); // the size of all the load commands
70 data_offset+=4; // Skip flags
71
72 // Check the architecture if we have a valid arch pointer
73 if (arch)
74 {
Greg Claytoncf015052010-06-11 03:25:34 +000075 ArchSpec file_arch(eArchTypeMachO, cputype, cpusubtype);
Chris Lattner24943d22010-06-08 16:52:24 +000076
77 if (file_arch != *arch)
78 return false;
79 }
80
81 // The file exists, and if a valid arch pointer was passed in we know
82 // if already matches, so we can return if we aren't looking for a specific
83 // UUID
84 if (uuid == NULL)
85 return true;
86
Greg Clayton1674b122010-07-21 22:12:05 +000087 if (magic == HeaderMagic64Swapped || magic == HeaderMagic64)
Chris Lattner24943d22010-06-08 16:52:24 +000088 data_offset += 4; // Skip reserved field for in mach_header_64
89
90 // Make sure we have enough data for all the load commands
Greg Clayton1674b122010-07-21 22:12:05 +000091 if (magic == HeaderMagic64Swapped || magic == HeaderMagic64)
Chris Lattner24943d22010-06-08 16:52:24 +000092 {
93 if (data.GetByteSize() < sizeof(struct mach_header_64) + sizeofcmds)
94 {
95 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header_64) + sizeofcmds));
96 data.SetData (data_buffer_sp);
97 }
98 }
99 else
100 {
101 if (data.GetByteSize() < sizeof(struct mach_header) + sizeofcmds)
102 {
103 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header) + sizeofcmds));
104 data.SetData (data_buffer_sp);
105 }
106 }
107
108 for (i=0; i<ncmds; i++)
109 {
110 const uint32_t cmd_offset = data_offset; // Save this data_offset in case parsing of the segment goes awry!
111 uint32_t cmd = data.GetU32(&data_offset);
112 uint32_t cmd_size = data.GetU32(&data_offset);
Greg Clayton1674b122010-07-21 22:12:05 +0000113 if (cmd == LoadCommandUUID)
Chris Lattner24943d22010-06-08 16:52:24 +0000114 {
115 UUID file_uuid (data.GetData(&data_offset, 16), 16);
116 return file_uuid == *uuid;
117 }
118 data_offset = cmd_offset + cmd_size;
119 }
120 return false;
121}
122
123bool
124UniversalMachOFileContainsArchAndUUID
125(
126 const FileSpec &file_spec,
127 const ArchSpec *arch,
128 const UUID *uuid,
129 off_t file_offset,
130 DataExtractor& data,
131 uint32_t data_offset,
132 const uint32_t magic
133)
134{
Greg Clayton1674b122010-07-21 22:12:05 +0000135 assert(magic == UniversalMagic || magic == UniversalMagicSwapped);
Chris Lattner24943d22010-06-08 16:52:24 +0000136
137 // Universal mach-o files always have their headers encoded as BIG endian
138 data.SetByteOrder(eByteOrderBig);
139
140 uint32_t i;
141 const uint32_t nfat_arch = data.GetU32(&data_offset); // number of structs that follow
142 const uint32_t fat_header_and_arch_size = sizeof(struct fat_header) + nfat_arch * sizeof(struct fat_arch);
143 if (data.GetByteSize() < fat_header_and_arch_size)
144 {
145 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, fat_header_and_arch_size));
146 data.SetData (data_buffer_sp);
147 }
148
149 for (i=0; i<nfat_arch; i++)
150 {
151 cpu_type_t arch_cputype = data.GetU32(&data_offset); // cpu specifier (int)
152 cpu_subtype_t arch_cpusubtype = data.GetU32(&data_offset); // machine specifier (int)
153 uint32_t arch_offset = data.GetU32(&data_offset); // file offset to this object file
154 // uint32_t arch_size = data.GetU32(&data_offset); // size of this object file
155 // uint32_t arch_align = data.GetU32(&data_offset); // alignment as a power of 2
156 data_offset += 8; // Skip size and align as we don't need those
157 // Only process this slice if the cpu type/subtype matches
158 if (arch)
159 {
Greg Claytoncf015052010-06-11 03:25:34 +0000160 ArchSpec fat_arch(eArchTypeMachO, arch_cputype, arch_cpusubtype);
Chris Lattner24943d22010-06-08 16:52:24 +0000161 if (fat_arch != *arch)
162 continue;
163 }
164
165 // Create a buffer with only the arch slice date in it
166 DataExtractor arch_data;
167 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset + arch_offset, 0x1000));
168 arch_data.SetData(data_buffer_sp);
169 uint32_t arch_data_offset = 0;
170 uint32_t arch_magic = arch_data.GetU32(&arch_data_offset);
171
172 switch (arch_magic)
173 {
Greg Clayton1674b122010-07-21 22:12:05 +0000174 case HeaderMagic32:
175 case HeaderMagic32Swapped:
176 case HeaderMagic64:
177 case HeaderMagic64Swapped:
Chris Lattner24943d22010-06-08 16:52:24 +0000178 if (SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset + arch_offset, arch_data, arch_data_offset, arch_magic))
179 return true;
180 break;
181 }
182 }
183 return false;
184}
185
186static bool
187FileAtPathContainsArchAndUUID
188(
189 const FileSpec &file_spec,
190 const ArchSpec *arch,
191 const UUID *uuid
192)
193{
194 DataExtractor data;
195 off_t file_offset = 0;
196 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, 0x1000));
197
198 if (data_buffer_sp && data_buffer_sp->GetByteSize() > 0)
199 {
200 data.SetData(data_buffer_sp);
201
202 uint32_t data_offset = 0;
203 uint32_t magic = data.GetU32(&data_offset);
204
205 switch (magic)
206 {
207 // 32 bit mach-o file
Greg Clayton1674b122010-07-21 22:12:05 +0000208 case HeaderMagic32:
209 case HeaderMagic32Swapped:
210 case HeaderMagic64:
211 case HeaderMagic64Swapped:
Chris Lattner24943d22010-06-08 16:52:24 +0000212 return SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic);
213
214 // fat mach-o file
Greg Clayton1674b122010-07-21 22:12:05 +0000215 case UniversalMagic:
216 case UniversalMagicSwapped:
Chris Lattner24943d22010-06-08 16:52:24 +0000217 return UniversalMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic);
218
219 default:
220 break;
221 }
222 }
223 return false;
224}
225
226static FileSpec
227LocateDSYMMachFileInDSYMBundle
228(
229 const FileSpec& dsym_bundle_fspec,
230 const UUID *uuid,
231 const ArchSpec *arch)
232{
233 char path[PATH_MAX];
234
235 FileSpec dsym_fspec;
236
237 if (dsym_bundle_fspec.GetPath(path, sizeof(path)))
238 {
239 ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
240
Greg Clayton52fd9842011-02-02 02:24:04 +0000241 lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir);
Greg Claytonad400272011-02-01 05:15:02 +0000242 if (dirp.is_valid())
Chris Lattner24943d22010-06-08 16:52:24 +0000243 {
Greg Clayton537a7a82010-10-20 20:54:39 +0000244 dsym_fspec.GetDirectory().SetCString(path);
Chris Lattner24943d22010-06-08 16:52:24 +0000245 struct dirent* dp;
Greg Claytonad400272011-02-01 05:15:02 +0000246 while ((dp = readdir(dirp.get())) != NULL)
Chris Lattner24943d22010-06-08 16:52:24 +0000247 {
248 // Only search directories
249 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
250 {
251 if (dp->d_namlen == 1 && dp->d_name[0] == '.')
252 continue;
253
254 if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
255 continue;
256 }
257
258 if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN)
259 {
Greg Clayton537a7a82010-10-20 20:54:39 +0000260 dsym_fspec.GetFilename().SetCString(dp->d_name);
Chris Lattner24943d22010-06-08 16:52:24 +0000261 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 {
Greg Clayton537a7a82010-10-20 20:54:39 +0000336 out_dsym_fspec->SetFile(path, false);
Chris Lattner24943d22010-06-08 16:52:24 +0000337
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;
Greg Clayton537a7a82010-10-20 20:54:39 +0000360 out_dsym_fspec->SetFile(path, false);
Chris Lattner24943d22010-06-08 16:52:24 +0000361 }
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
Greg Clayton537a7a82010-10-20 20:54:39 +0000386 dsym_fspec.SetFile(path, false);
Chris Lattner24943d22010-06-08 16:52:24 +0000387
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));
Greg Clayton537a7a82010-10-20 20:54:39 +0000405 dsym_fspec.SetFile(path, false);
Chris Lattner24943d22010-06-08 16:52:24 +0000406 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}