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