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