]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/llvm/tools/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / llvm / tools / lldb / source / Plugins / ObjectContainer / BSD-Archive / ObjectContainerBSDArchive.cpp
1 //===-- ObjectContainerBSDArchive.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 "ObjectContainerBSDArchive.h"
11
12 #ifdef _WIN32
13 // Defines from ar, missing on Windows
14 #define ARMAG   "!<arch>\n"
15 #define SARMAG  8
16 #define ARFMAG  "`\n"
17
18 typedef struct ar_hdr
19 {
20     char ar_name[16];
21     char ar_date[12];
22     char ar_uid[6], ar_gid[6];
23     char ar_mode[8];
24     char ar_size[10];
25     char ar_fmag[2];
26 } ar_hdr;
27 #else
28 #include <ar.h>
29 #endif
30
31 #include "lldb/Core/ArchSpec.h"
32 #include "lldb/Core/DataBuffer.h"
33 #include "lldb/Core/Module.h"
34 #include "lldb/Core/ModuleSpec.h"
35 #include "lldb/Core/PluginManager.h"
36 #include "lldb/Core/Stream.h"
37 #include "lldb/Core/Timer.h"
38 #include "lldb/Host/Mutex.h"
39 #include "lldb/Symbol/ObjectFile.h"
40
41 using namespace lldb;
42 using namespace lldb_private;
43
44
45
46 ObjectContainerBSDArchive::Object::Object() :
47     ar_name(),
48     ar_date(0),
49     ar_uid(0),
50     ar_gid(0),
51     ar_mode(0),
52     ar_size(0),
53     ar_file_offset(0),
54     ar_file_size(0)
55 {
56 }
57
58 void
59 ObjectContainerBSDArchive::Object::Clear()
60 {
61     ar_name.Clear();
62     ar_date = 0;
63     ar_uid  = 0;
64     ar_gid  = 0;
65     ar_mode = 0;
66     ar_size = 0;
67     ar_file_offset = 0;
68     ar_file_size = 0;
69 }
70
71 lldb::offset_t
72 ObjectContainerBSDArchive::Object::Extract (const DataExtractor& data, lldb::offset_t offset)
73 {
74     size_t ar_name_len = 0;
75     std::string str;
76     char *err;
77     str.assign ((const char *)data.GetData(&offset, 16),    16);
78     if (str.find("#1/") == 0)
79     {
80         // If the name is longer than 16 bytes, or contains an embedded space
81         // then it will use this format where the length of the name is
82         // here and the name characters are after this header.
83         ar_name_len = strtoul(str.c_str() + 3, &err, 10);
84     }
85     else
86     {
87         // Strip off any spaces (if the object file name contains spaces it
88         // will use the extended format above).
89         str.erase (str.find(' '));
90         ar_name.SetCString(str.c_str());
91     }
92
93     str.assign ((const char *)data.GetData(&offset, 12),    12);
94     ar_date = strtoul(str.c_str(), &err, 10);
95
96     str.assign ((const char *)data.GetData(&offset, 6), 6);
97     ar_uid  = strtoul(str.c_str(), &err, 10);
98
99     str.assign ((const char *)data.GetData(&offset, 6), 6);
100     ar_gid  = strtoul(str.c_str(), &err, 10);
101
102     str.assign ((const char *)data.GetData(&offset, 8), 8);
103     ar_mode = strtoul(str.c_str(), &err, 8);
104
105     str.assign ((const char *)data.GetData(&offset, 10),    10);
106     ar_size = strtoul(str.c_str(), &err, 10);
107
108     str.assign ((const char *)data.GetData(&offset, 2), 2);
109     if (str == ARFMAG)
110     {
111         if (ar_name_len > 0)
112         {
113             str.assign ((const char *)data.GetData(&offset, ar_name_len), ar_name_len);
114             ar_name.SetCString (str.c_str());
115         }
116         ar_file_offset = offset;
117         ar_file_size = ar_size - ar_name_len;
118         return offset;
119     }
120     return LLDB_INVALID_OFFSET;
121 }
122
123 ObjectContainerBSDArchive::Archive::Archive
124 (
125     const lldb_private::ArchSpec &arch,
126     const lldb_private::TimeValue &time,
127     lldb::offset_t file_offset,
128     lldb_private::DataExtractor &data
129 ) :
130     m_arch (arch),
131     m_time (time),
132     m_file_offset (file_offset),
133     m_objects(),
134     m_data (data)
135 {
136 }
137
138 ObjectContainerBSDArchive::Archive::~Archive ()
139 {
140 }
141
142 size_t
143 ObjectContainerBSDArchive::Archive::ParseObjects ()
144 {
145     DataExtractor &data = m_data;
146     std::string str;
147     lldb::offset_t offset = 0;
148     str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG);
149     if (str == ARMAG)
150     {
151         Object obj;
152         do
153         {
154             offset = obj.Extract (data, offset);
155             if (offset == LLDB_INVALID_OFFSET)
156                 break;
157             size_t obj_idx = m_objects.size();
158             m_objects.push_back(obj);
159             // Insert all of the C strings out of order for now...
160             m_object_name_to_index_map.Append (obj.ar_name.GetCString(), obj_idx);
161             offset += obj.ar_file_size;
162             obj.Clear();
163         } while (data.ValidOffset(offset));
164
165         // Now sort all of the object name pointers
166         m_object_name_to_index_map.Sort ();
167     }
168     return m_objects.size();
169 }
170
171 ObjectContainerBSDArchive::Object *
172 ObjectContainerBSDArchive::Archive::FindObject (const ConstString &object_name, const TimeValue &object_mod_time)
173 {
174     const ObjectNameToIndexMap::Entry *match = m_object_name_to_index_map.FindFirstValueForName (object_name.GetCString());
175     if (match)
176     {
177         if (object_mod_time.IsValid())
178         {
179             const uint64_t object_date = object_mod_time.GetAsSecondsSinceJan1_1970();
180             if (m_objects[match->value].ar_date == object_date)
181                 return &m_objects[match->value];
182             const ObjectNameToIndexMap::Entry *next_match = m_object_name_to_index_map.FindNextValueForName (match);
183             while (next_match)
184             {
185                 if (m_objects[next_match->value].ar_date == object_date)
186                     return &m_objects[next_match->value];
187                 next_match = m_object_name_to_index_map.FindNextValueForName (next_match);
188             }
189         }
190         else
191         {
192             return &m_objects[match->value];
193         }
194     }
195     return NULL;
196 }
197
198
199 ObjectContainerBSDArchive::Archive::shared_ptr
200 ObjectContainerBSDArchive::Archive::FindCachedArchive (const FileSpec &file, const ArchSpec &arch, const TimeValue &time, lldb::offset_t file_offset)
201 {
202     Mutex::Locker locker(Archive::GetArchiveCacheMutex ());
203     shared_ptr archive_sp;
204     Archive::Map &archive_map = Archive::GetArchiveCache ();
205     Archive::Map::iterator pos = archive_map.find (file);
206     // Don't cache a value for "archive_map.end()" below since we might
207     // delete an archive entry...
208     while (pos != archive_map.end() && pos->first == file)
209     {
210         bool match = true;
211         if (arch.IsValid() && pos->second->GetArchitecture().IsCompatibleMatch(arch) == false)
212             match = false;
213         else if (file_offset != LLDB_INVALID_OFFSET && pos->second->GetFileOffset() != file_offset)
214             match = false;
215         if (match)
216         {
217             if (pos->second->GetModificationTime() == time)
218             {
219                 return pos->second;
220             }
221             else
222             {
223                 // We have a file at the same path with the same architecture
224                 // whose modification time doesn't match. It doesn't make sense
225                 // for us to continue to use this BSD archive since we cache only
226                 // the object info which consists of file time info and also the
227                 // file offset and file size of any contianed objects. Since
228                 // this information is now out of date, we won't get the correct
229                 // information if we go and extract the file data, so we should 
230                 // remove the old and outdated entry.
231                 archive_map.erase (pos);
232                 pos = archive_map.find (file);
233                 continue; // Continue to next iteration so we don't increment pos below...
234             }
235         }
236         ++pos;
237     }
238     return archive_sp;
239 }
240
241 ObjectContainerBSDArchive::Archive::shared_ptr
242 ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile
243 (
244     const FileSpec &file,
245     const ArchSpec &arch,
246     const TimeValue &time,
247     lldb::offset_t file_offset,
248     DataExtractor &data
249 )
250 {
251     shared_ptr archive_sp(new Archive (arch, time, file_offset, data));
252     if (archive_sp)
253     {
254         const size_t num_objects = archive_sp->ParseObjects ();
255         if (num_objects > 0)
256         {
257             Mutex::Locker locker(Archive::GetArchiveCacheMutex ());
258             Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp));
259         }
260         else
261         {
262             archive_sp.reset();
263         }
264     }
265     return archive_sp;
266 }
267
268 ObjectContainerBSDArchive::Archive::Map &
269 ObjectContainerBSDArchive::Archive::GetArchiveCache ()
270 {
271     static Archive::Map g_archive_map;
272     return g_archive_map;
273 }
274
275 Mutex &
276 ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex ()
277 {
278     static Mutex g_archive_map_mutex (Mutex::eMutexTypeRecursive);
279     return g_archive_map_mutex;
280 }
281
282
283 void
284 ObjectContainerBSDArchive::Initialize()
285 {
286     PluginManager::RegisterPlugin (GetPluginNameStatic(),
287                                    GetPluginDescriptionStatic(),
288                                    CreateInstance,
289                                    GetModuleSpecifications);
290 }
291
292 void
293 ObjectContainerBSDArchive::Terminate()
294 {
295     PluginManager::UnregisterPlugin (CreateInstance);
296 }
297
298
299 lldb_private::ConstString
300 ObjectContainerBSDArchive::GetPluginNameStatic()
301 {
302     static ConstString g_name("bsd-archive");
303     return g_name;
304 }
305
306 const char *
307 ObjectContainerBSDArchive::GetPluginDescriptionStatic()
308 {
309     return "BSD Archive object container reader.";
310 }
311
312
313 ObjectContainer *
314 ObjectContainerBSDArchive::CreateInstance
315 (
316     const lldb::ModuleSP &module_sp,
317     DataBufferSP& data_sp,
318     lldb::offset_t data_offset,
319     const FileSpec *file,
320     lldb::offset_t file_offset,
321     lldb::offset_t length)
322 {
323     ConstString object_name (module_sp->GetObjectName());
324     if (object_name)
325     {
326         if (data_sp)
327         {
328             // We have data, which means this is the first 512 bytes of the file
329             // Check to see if the magic bytes match and if they do, read the entire
330             // table of contents for the archive and cache it
331             DataExtractor data;
332             data.SetData (data_sp, data_offset, length);
333             if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data))
334             {
335                 Timer scoped_timer (__PRETTY_FUNCTION__,
336                                     "ObjectContainerBSDArchive::CreateInstance (module = %s, file = %p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")",
337                                     module_sp->GetFileSpec().GetPath().c_str(),
338                                     file, (uint64_t) file_offset, (uint64_t) length);
339
340                 // Map the entire .a file to be sure that we don't lose any data if the file
341                 // gets updated by a new build while this .a file is being used for debugging
342                 DataBufferSP archive_data_sp (file->MemoryMapFileContents(file_offset, length));
343                 lldb::offset_t archive_data_offset = 0;
344
345                 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file,
346                                                                             module_sp->GetArchitecture(),
347                                                                             module_sp->GetModificationTime(),
348                                                                             file_offset));
349                 std::unique_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp,
350                                                                                                        archive_data_sp,
351                                                                                                        archive_data_offset,
352                                                                                                        file,
353                                                                                                        file_offset,
354                                                                                                        length));
355
356                 if (container_ap.get())
357                 {
358                     if (archive_sp)
359                     {
360                         // We already have this archive in our cache, use it
361                         container_ap->SetArchive (archive_sp);
362                         return container_ap.release();
363                     }
364                     else if (container_ap->ParseHeader())
365                         return container_ap.release();
366                 }
367             }
368         }
369         else
370         {
371             // No data, just check for a cached archive
372             Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file,
373                                                                         module_sp->GetArchitecture(),
374                                                                         module_sp->GetModificationTime(),
375                                                                         file_offset));
376             if (archive_sp)
377             {
378                 std::unique_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, data_sp, data_offset, file, file_offset, length));
379                 
380                 if (container_ap.get())
381                 {
382                     // We already have this archive in our cache, use it
383                     container_ap->SetArchive (archive_sp);
384                     return container_ap.release();
385                 }
386             }
387         }
388     }
389     return NULL;
390 }
391
392
393
394 bool
395 ObjectContainerBSDArchive::MagicBytesMatch (const DataExtractor &data)
396 {
397     uint32_t offset = 0;
398     const char* armag = (const char* )data.PeekData (offset, sizeof(ar_hdr));
399     if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0)
400     {
401         armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG;
402         if (strncmp(armag, ARFMAG, 2) == 0)
403             return true;
404     }
405     return false;
406 }
407
408 ObjectContainerBSDArchive::ObjectContainerBSDArchive
409 (
410     const lldb::ModuleSP &module_sp,
411     DataBufferSP& data_sp,
412     lldb::offset_t data_offset,
413     const lldb_private::FileSpec *file,
414     lldb::offset_t file_offset,
415     lldb::offset_t size
416 ) :
417     ObjectContainer (module_sp, file, file_offset, size, data_sp, data_offset),
418     m_archive_sp ()
419 {
420 }
421 void
422 ObjectContainerBSDArchive::SetArchive (Archive::shared_ptr &archive_sp)
423 {
424     m_archive_sp = archive_sp;
425 }
426
427
428
429 ObjectContainerBSDArchive::~ObjectContainerBSDArchive()
430 {
431 }
432
433 bool
434 ObjectContainerBSDArchive::ParseHeader ()
435 {
436     if (m_archive_sp.get() == NULL)
437     {
438         if (m_data.GetByteSize() > 0)
439         {
440             ModuleSP module_sp (GetModule());
441             if (module_sp)
442             {
443                 m_archive_sp = Archive::ParseAndCacheArchiveForFile (m_file,
444                                                                      module_sp->GetArchitecture(),
445                                                                      module_sp->GetModificationTime(),
446                                                                      m_offset,
447                                                                      m_data);
448             }
449             // Clear the m_data that contains the entire archive
450             // data and let our m_archive_sp hold onto the data.
451             m_data.Clear();
452         }
453     }
454     return m_archive_sp.get() != NULL;
455 }
456
457 void
458 ObjectContainerBSDArchive::Dump (Stream *s) const
459 {
460     s->Printf("%p: ", this);
461     s->Indent();
462     const size_t num_archs = GetNumArchitectures();
463     const size_t num_objects = GetNumObjects();
464     s->Printf("ObjectContainerBSDArchive, num_archs = %zu, num_objects = %zu", num_archs, num_objects);
465     uint32_t i;
466     ArchSpec arch;
467     s->IndentMore();
468     for (i=0; i<num_archs; i++)
469     {
470         s->Indent();
471         GetArchitectureAtIndex(i, arch);
472         s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName());
473     }
474     for (i=0; i<num_objects; i++)
475     {
476         s->Indent();
477         s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex (i));
478     }
479     s->IndentLess();
480     s->EOL();
481 }
482
483 ObjectFileSP
484 ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file)
485 {
486     ModuleSP module_sp (GetModule());
487     if (module_sp)
488     {
489         if (module_sp->GetObjectName() && m_archive_sp)
490         {
491             Object *object = m_archive_sp->FindObject (module_sp->GetObjectName(),
492                                                        module_sp->GetObjectModificationTime());
493             if (object)
494             {
495                 lldb::offset_t data_offset = object->ar_file_offset;
496                 return ObjectFile::FindPlugin (module_sp,
497                                                file, 
498                                                m_offset + object->ar_file_offset,
499                                                object->ar_file_size,
500                                                m_archive_sp->GetData().GetSharedDataBuffer(),
501                                                data_offset);
502             }
503         }
504     }
505     return ObjectFileSP();
506 }
507
508
509 //------------------------------------------------------------------
510 // PluginInterface protocol
511 //------------------------------------------------------------------
512 lldb_private::ConstString
513 ObjectContainerBSDArchive::GetPluginName()
514 {
515     return GetPluginNameStatic();
516 }
517
518 uint32_t
519 ObjectContainerBSDArchive::GetPluginVersion()
520 {
521     return 1;
522 }
523
524
525 size_t
526 ObjectContainerBSDArchive::GetModuleSpecifications (const lldb_private::FileSpec& file,
527                                                     lldb::DataBufferSP& data_sp,
528                                                     lldb::offset_t data_offset,
529                                                     lldb::offset_t file_offset,
530                                                     lldb::offset_t file_size,
531                                                     lldb_private::ModuleSpecList &specs)
532 {
533
534     // We have data, which means this is the first 512 bytes of the file
535     // Check to see if the magic bytes match and if they do, read the entire
536     // table of contents for the archive and cache it
537     DataExtractor data;
538     data.SetData (data_sp, data_offset, data_sp->GetByteSize());
539     if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data))
540     {
541         const size_t initial_count = specs.GetSize();
542         TimeValue file_mod_time = file.GetModificationTime();
543         Archive::shared_ptr archive_sp (Archive::FindCachedArchive (file, ArchSpec(), file_mod_time, file_offset));
544         bool set_archive_arch = false;
545         if (!archive_sp)
546         {
547             set_archive_arch = true;
548             DataBufferSP data_sp (file.MemoryMapFileContents(file_offset, file_size));
549             data.SetData (data_sp, 0, data_sp->GetByteSize());
550             archive_sp = Archive::ParseAndCacheArchiveForFile(file, ArchSpec(), file_mod_time, file_offset, data);
551         }
552         
553         if (archive_sp)
554         {
555             const size_t num_objects = archive_sp->GetNumObjects();
556             for (size_t idx = 0; idx < num_objects; ++idx)
557             {
558                 const Object *object = archive_sp->GetObjectAtIndex (idx);
559                 if (object)
560                 {
561                     const lldb::offset_t object_file_offset = file_offset + object->ar_file_offset;
562                     if (object->ar_file_offset < file_size && file_size > object_file_offset)
563                     {
564                         if (ObjectFile::GetModuleSpecifications(file,
565                                                                 object_file_offset,
566                                                                 file_size - object_file_offset,
567                                                                 specs))
568                         {
569                             ModuleSpec &spec = specs.GetModuleSpecRefAtIndex (specs.GetSize() - 1);
570                             TimeValue object_mod_time;
571                             object_mod_time.OffsetWithSeconds(object->ar_date);
572                             spec.GetObjectName () = object->ar_name;
573                             spec.SetObjectOffset(object_file_offset);
574                             spec.GetObjectModificationTime () = object_mod_time;
575                         }
576                     }
577                 }
578             }
579         }
580         const size_t end_count = specs.GetSize();
581         size_t num_specs_added = end_count - initial_count;
582         if (set_archive_arch && num_specs_added > 0)
583         {
584             // The archive was created but we didn't have an architecture
585             // so we need to set it
586             for (size_t i=initial_count; i<end_count; ++ i)
587             {
588                 ModuleSpec module_spec;
589                 if (specs.GetModuleSpecAtIndex(i, module_spec))
590                 {
591                     if (module_spec.GetArchitecture().IsValid())
592                     {
593                         archive_sp->SetArchitecture (module_spec.GetArchitecture());
594                         break;
595                     }
596                 }
597             }
598         }
599         return num_specs_added;
600     }
601     return 0;
602 }