Home | History | Annotate | Download | only in LD
      1 //===- GNUArchiveReader.cpp -----------------------------------------------===//
      2 //
      3 //                     The MCLinker Project
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 #include "mcld/MC/MCLDInfo.h"
     10 #include "mcld/MC/MCLDInput.h"
     11 #include "mcld/MC/MCLDInputTree.h"
     12 #include "mcld/LD/GNUArchiveReader.h"
     13 
     14 #include <llvm/Support/MemoryBuffer.h>
     15 #include <llvm/Support/system_error.h>
     16 
     17 #include <sstream>
     18 #include <string>
     19 #include <vector>
     20 #include <cstdlib>
     21 
     22 using namespace std;
     23 using namespace mcld;
     24 
     25 typedef size_t sectionSizeTy;
     26 typedef uint32_t elfWord;
     27 
     28 /// Archive Header, Magic number, etc..
     29 
     30 const unsigned archiveMagicSize = 8;
     31 const char archiveMagic[archiveMagicSize] = { '!', '<', 'a', 'r', 'c', 'h', '>', '\n' };
     32 const char thinArchiveMagic[archiveMagicSize] = { '!', '<', 't', 'h', 'i', 'n', '>', '\n' };
     33 const char archiveFinalMagic[2] = { '`', '\n' };
     34 
     35 struct GNUArchiveReader::ArchiveMemberHeader
     36 {
     37   char name[16];
     38   char date[12];
     39   char uid[6];
     40   char gid[6];
     41   char mode[8];
     42   char size[10];
     43   char finalMagic[2];
     44 };
     45 
     46 struct GNUArchiveReader::SymbolTableEntry
     47 {
     48   off_t fileOffset;
     49   std::string name;
     50 };
     51 
     52 inline void endian_swap(unsigned int& x)
     53 {
     54   x = (x>>24) |
     55       ((x<<8) & 0x00FF0000) |
     56       ((x>>8) & 0x0000FF00) |
     57       (x<<24);
     58 }
     59 
     60 
     61 /// convert string to size_t
     62 template<class Type>
     63 Type stringToType(const std::string &str)
     64 {
     65   Type n;
     66   std::stringstream ss(str);
     67   ss >> n;
     68   return n;
     69 }
     70 
     71 
     72 /// GNUArchiveReader Operations
     73 /// Public API
     74 bool GNUArchiveReader::isMyFormat(Input &pInput) const
     75 {
     76   llvm::OwningPtr<llvm::MemoryBuffer> mapFile;
     77   llvm::MemoryBuffer::getFile(pInput.path().c_str(), mapFile);
     78   const char* pFile = mapFile->getBufferStart();
     79 
     80   /// check archive format.
     81   if(mapFile->getBufferSize() <= archiveMagicSize)
     82     return false;
     83   bool isThinArchive = memcmp(pFile, thinArchiveMagic, archiveMagicSize) == 0;
     84   if(!isThinArchive && memcmp(pFile, archiveMagic, archiveMagicSize) != 0)
     85     return false;
     86   return true;
     87 }
     88 
     89 LDReader::Endian GNUArchiveReader::endian(Input& pFile) const
     90 {
     91   return m_endian;
     92 }
     93 
     94 InputTree *GNUArchiveReader::readArchive(Input &pInput)
     95 {
     96   return setupNewArchive(pInput, 0);
     97 }
     98 
     99 /// Read Input as archive. First create a null InputTree.
    100 /// Then Construct Input object for corresponding member of this archive
    101 /// and insert the Input object into the InputTree.
    102 /// Finally, return the InputTree.
    103 InputTree *GNUArchiveReader::setupNewArchive(Input &pInput,
    104                                             size_t off)
    105 {
    106   llvm::OwningPtr<llvm::MemoryBuffer> mapFile;
    107   if(llvm::MemoryBuffer::getFile(pInput.path().c_str(), mapFile))
    108   {
    109     assert(0 && "GNUArchiveReader:can't map a file to MemoryBuffer\n");
    110     return NULL;
    111   }
    112 
    113   InputTree *resultTree = new InputTree(m_pLDInfo.inputFactory());
    114   std::vector<SymbolTableEntry> symbolTable;
    115   std::string archiveMemberName;
    116   std::string extendedName;
    117   bool isThinArchive;
    118   const char *pFile = mapFile->getBufferStart();
    119 
    120   /// check archive format.
    121   if(mapFile->getBufferSize() <= archiveMagicSize)
    122     return NULL;
    123   else
    124   {
    125     isThinArchive = memcmp(pFile, thinArchiveMagic, archiveMagicSize) == 0;
    126     if(!isThinArchive && memcmp(pFile, archiveMagic, archiveMagicSize) != 0)
    127       return NULL;
    128   }
    129 
    130   off += archiveMagicSize ;
    131   size_t symbolTableSize = parseMemberHeader(mapFile, off, &archiveMemberName,
    132                                             NULL, extendedName);
    133   /// read archive symbol table
    134   if(archiveMemberName.empty())
    135   {
    136     readSymbolTable(mapFile, symbolTable,
    137                     off+sizeof(GNUArchiveReader::ArchiveMemberHeader), symbolTableSize);
    138     off = off + sizeof(GNUArchiveReader::ArchiveMemberHeader) + symbolTableSize;
    139   }
    140   else
    141   {
    142     assert(0 && "fatal error : need symbol table\n");
    143     return NULL;
    144   }
    145 
    146   if((off&1) != 0)
    147     ++off;
    148 
    149   size_t extendedSize = parseMemberHeader(mapFile, off, &archiveMemberName,
    150                                           NULL, extendedName);
    151   /// read long Name table if exist
    152   if(archiveMemberName == "/")
    153   {
    154     off += sizeof(GNUArchiveReader::ArchiveMemberHeader);
    155     pFile += off;
    156     extendedName.assign(pFile,extendedSize);
    157   }
    158 
    159   /// traverse all the archive members
    160   InputTree::iterator node = resultTree->root();
    161   set<string> haveSeen;
    162   for(unsigned i=0 ; i<symbolTable.size() ; ++i)
    163   {
    164     /// We shall get each member at this archive.
    165     /// Construct a corresponding mcld::Input, and insert it into
    166     /// the original InputTree, resultTree.
    167     off_t nestedOff = 0;
    168 
    169     parseMemberHeader(mapFile, symbolTable[i].fileOffset, &archiveMemberName,
    170                       &nestedOff, extendedName);
    171 
    172     if(haveSeen.find(archiveMemberName)==haveSeen.end())
    173       haveSeen.insert(archiveMemberName);
    174     else
    175       continue;
    176 
    177     if(!isThinArchive)
    178     {
    179       /// New a Input object and assign fileOffset in MCLDFile.
    180       /// Insert the object to resultTree and move ahead.
    181       off_t fileOffset = symbolTable[i].fileOffset + sizeof(ArchiveMemberHeader);
    182       Input *insertObjectFile = m_pLDInfo.inputFactory().produce(archiveMemberName,
    183                                                                  pInput.path(),
    184                                                                  MCLDFile::Object,
    185                                                                  fileOffset);
    186       resultTree->insert<InputTree::Positional>(node, *insertObjectFile);
    187       if(i==0)
    188         node.move<InputTree::Inclusive>();
    189       else
    190         node.move<InputTree::Positional>();
    191 
    192       continue;
    193     }
    194 
    195     /// create the real path
    196     sys::fs::RealPath realPath(archiveMemberName);
    197     if(nestedOff > 0)
    198     {
    199       /// This is a member of a nested archive.
    200       /// Create an Input for this archive ,and recursive call setupNewArchive
    201       /// Finally, merge the new InputTree with the old one
    202       Input *newArchive = m_pLDInfo.inputFactory().produce(archiveMemberName,
    203                                                            realPath,
    204                                                            MCLDFile::Archive,
    205                                                            0);
    206 
    207       resultTree->insert<InputTree::Positional>(node, *newArchive);
    208       if(i==0)
    209         node.move<InputTree::Inclusive>();
    210       else
    211         node.move<InputTree::Positional>();
    212       InputTree *newArchiveTree = setupNewArchive(*newArchive, 0);
    213       resultTree->merge<InputTree::Inclusive>(node, *newArchiveTree);
    214       continue;
    215     }
    216     /// External member , open it as normal object file
    217     /// add new Input to InputTree
    218     Input *insertObjectFile = m_pLDInfo.inputFactory().produce(archiveMemberName,
    219                                                                realPath,
    220                                                                MCLDFile::Object,
    221                                                                0);
    222     resultTree->insert<InputTree::Positional>(node, *insertObjectFile);
    223     if(i==0)
    224       node.move<InputTree::Inclusive>();
    225     else
    226       node.move<InputTree::Positional>();
    227   }
    228   return resultTree;
    229 }
    230 
    231 
    232 /// Parse the member header and return the size of member
    233 /// Archive member names in System 5 style :
    234 ///
    235 /// "/                  " - symbol table, must be the first member
    236 /// "//                 " - long name table
    237 /// "filename.o/        " - regular file with short name
    238 /// "/5566              " - name at offset 5566 at long name table
    239 
    240 size_t GNUArchiveReader::parseMemberHeader(llvm::OwningPtr<llvm::MemoryBuffer> &mapFile,
    241                                            off_t off,
    242                                            std::string *p_Name,
    243                                            off_t *p_NestedOff,
    244                                            std::string &p_ExtendedName)
    245 {
    246   const char *pFile = mapFile->getBufferStart();
    247   pFile += off;
    248   const ArchiveMemberHeader *header = reinterpret_cast<const ArchiveMemberHeader *>(pFile);
    249 
    250   /// check magic number of member header
    251   if(memcmp(header->finalMagic, archiveFinalMagic, sizeof archiveFinalMagic))
    252   {
    253     assert(0 && "archive member header magic number false");
    254     return 0;
    255   }
    256 
    257   /// evaluate member size
    258   std::string sizeString(header->size, sizeof(header->size)+1);
    259   size_t memberSize = stringToType<size_t>(sizeString);
    260   if(memberSize == 0)
    261   {
    262     assert(0 && "member Size Error");
    263     return 0;
    264   }
    265 
    266   if(header->name[0] != '/')
    267   {
    268     /// This is a regular file with short name
    269     const char* nameEnd = strchr(header->name, '/');
    270     size_t nameLen = ((nameEnd == NULL) ? 0 : (nameEnd - header->name));
    271     if((nameLen <= 0) || (nameLen >= sizeof(header->name)))
    272     {
    273       assert(0 && "header name format error\n");
    274       return 0;
    275     }
    276     p_Name->assign(header->name, nameLen);
    277 
    278     if(!p_NestedOff)
    279       p_NestedOff = 0;
    280   }
    281   else if(header->name[1] == ' ')
    282   {
    283     /// This is symbol table
    284     if(!p_Name->empty())
    285       p_Name->clear();
    286   }
    287   else if(header->name[1] == '/')
    288   {
    289     /// This is long name table
    290     p_Name->assign(1,'/');
    291   }
    292   else
    293   {
    294     /// This is regular file with long name
    295     char *end;
    296     long extendedNameOff = strtol(header->name+1, &end, 10);
    297     long nestedOff = 0;
    298     if(*end == ':')
    299       nestedOff = strtol(end+1, &end, 10);
    300 
    301     if(*end != ' '
    302        || extendedNameOff < 0
    303        || static_cast<size_t>(extendedNameOff) >= p_ExtendedName.size())
    304     {
    305       assert(0 && "extended name");
    306       return 0;
    307     }
    308 
    309     const char *name = p_ExtendedName.data() + extendedNameOff;
    310     const char *nameEnd = strchr(name, '\n');
    311     if(nameEnd[-1] != '/'
    312        || static_cast<size_t>(nameEnd-name) > p_ExtendedName.size())
    313     {
    314       assert(0 && "p_ExtendedName substring is not end with / \n");
    315       return 0;
    316     }
    317     p_Name->assign(name, nameEnd-name-1);
    318     if(p_NestedOff)
    319      *p_NestedOff = nestedOff;
    320   }
    321 
    322   return memberSize;
    323 }
    324 
    325 void GNUArchiveReader::readSymbolTable(llvm::OwningPtr<llvm::MemoryBuffer> &mapFile,
    326                                        std::vector<SymbolTableEntry> &pSymbolTable,
    327                                        off_t start,
    328                                        size_t size)
    329 {
    330   const char *startPtr = mapFile->getBufferStart() + start;
    331   const elfWord *p_Word = reinterpret_cast<const elfWord *>(startPtr);
    332   unsigned int symbolNum = *p_Word;
    333 
    334   /// Portable Issue on Sparc platform
    335   /// Intel, ARM and Mips are littel-endian , Sparc is little-endian after verion 9
    336   /// symbolNum in symbol table is always big-endian
    337   if(m_endian == LDReader::LittleEndian)
    338     endian_swap(symbolNum);
    339   ++p_Word;
    340 
    341   const char *p_Name = reinterpret_cast<const char *>(p_Word + symbolNum);
    342 
    343   pSymbolTable.resize(symbolNum);
    344   for(unsigned int i=0 ; i<symbolNum ; ++i)
    345   {
    346     /// assign member offset
    347     unsigned int memberOffset = *p_Word;
    348     endian_swap(memberOffset);
    349     pSymbolTable[i].fileOffset = static_cast<off_t>(memberOffset);
    350     ++p_Word;
    351     /// assign member name
    352     off_t nameEnd = strlen(p_Name) + 1;
    353     pSymbolTable[i].name.assign(p_Name, nameEnd);
    354     p_Name += nameEnd;
    355   }
    356 }
    357