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