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