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