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