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