+/// Read a variable-bit-rate encoded unsigned integer
+static inline unsigned readInteger(const char*&At, const char*End) {
+ unsigned Shift = 0;
+ unsigned Result = 0;
+
+ do {
+ if (At == End)
+ return Result;
+ Result |= (unsigned)((*At++) & 0x7F) << Shift;
+ Shift += 7;
+ } while (At[-1] & 0x80);
+ return Result;
+}
+
+// Completely parse the Archive's symbol table and populate symTab member var.
+bool
+Archive::parseSymbolTable(const void* data, unsigned size, std::string* error) {
+ const char* At = (const char*) data;
+ const char* End = At + size;
+ while (At < End) {
+ unsigned offset = readInteger(At, End);
+ if (At == End) {
+ if (error)
+ *error = "Ran out of data reading vbr_uint for symtab offset!";
+ return false;
+ }
+ unsigned length = readInteger(At, End);
+ if (At == End) {
+ if (error)
+ *error = "Ran out of data reading vbr_uint for symtab length!";
+ return false;
+ }
+ if (At + length > End) {
+ if (error)
+ *error = "Malformed symbol table: length not consistent with size";
+ return false;
+ }
+ // we don't care if it can't be inserted (duplicate entry)
+ symTab.insert(std::make_pair(std::string(At, length), offset));
+ At += length;
+ }
+ symTabSize = size;
+ return true;
+}
+
+// This member parses an ArchiveMemberHeader that is presumed to be pointed to
+// by At. The At pointer is updated to the byte just after the header, which
+// can be variable in size.
+ArchiveMember*
+Archive::parseMemberHeader(const char*& At, const char* End, std::string* error)
+{
+ if (At + sizeof(ArchiveMemberHeader) >= End) {
+ if (error)
+ *error = "Unexpected end of file";
+ return 0;
+ }
+
+ // Cast archive member header
+ ArchiveMemberHeader* Hdr = (ArchiveMemberHeader*)At;
+ At += sizeof(ArchiveMemberHeader);
+
+ // Extract the size and determine if the file is
+ // compressed or not (negative length).
+ int flags = 0;
+ int MemberSize = atoi(Hdr->size);
+ if (MemberSize < 0) {
+ flags |= ArchiveMember::CompressedFlag;
+ MemberSize = -MemberSize;
+ }
+
+ // Check the size of the member for sanity
+ if (At + MemberSize > End) {
+ if (error)
+ *error = "invalid member length in archive file";
+ return 0;
+ }
+
+ // Check the member signature
+ if (!Hdr->checkSignature()) {
+ if (error)
+ *error = "invalid file member signature";
+ return 0;
+ }
+
+ // Convert and check the member name
+ // The empty name ( '/' and 15 blanks) is for a foreign (non-LLVM) symbol
+ // table. The special name "//" and 14 blanks is for a string table, used
+ // for long file names. This library doesn't generate either of those but
+ // it will accept them. If the name starts with #1/ and the remainder is
+ // digits, then those digits specify the length of the name that is
+ // stored immediately following the header. The special name
+ // __LLVM_SYM_TAB__ identifies the symbol table for LLVM bitcode.
+ // Anything else is a regular, short filename that is terminated with
+ // a '/' and blanks.
+
+ std::string pathname;
+ switch (Hdr->name[0]) {
+ case '#':
+ if (Hdr->name[1] == '1' && Hdr->name[2] == '/') {
+ if (isdigit(Hdr->name[3])) {
+ unsigned len = atoi(&Hdr->name[3]);
+ pathname.assign(At, len);
+ At += len;
+ MemberSize -= len;
+ flags |= ArchiveMember::HasLongFilenameFlag;
+ } else {
+ if (error)
+ *error = "invalid long filename";
+ return 0;
+ }
+ } else if (Hdr->name[1] == '_' &&
+ (0 == memcmp(Hdr->name, ARFILE_LLVM_SYMTAB_NAME, 16))) {
+ // The member is using a long file name (>15 chars) format.
+ // This format is standard for 4.4BSD and Mac OSX operating
+ // systems. LLVM uses it similarly. In this format, the
+ // remainder of the name field (after #1/) specifies the
+ // length of the file name which occupy the first bytes of
+ // the member's data. The pathname already has the #1/ stripped.
+ pathname.assign(ARFILE_LLVM_SYMTAB_NAME);
+ flags |= ArchiveMember::LLVMSymbolTableFlag;
+ }
+ break;
+ case '/':
+ if (Hdr->name[1]== '/') {
+ if (0 == memcmp(Hdr->name, ARFILE_STRTAB_NAME, 16)) {
+ pathname.assign(ARFILE_STRTAB_NAME);
+ flags |= ArchiveMember::StringTableFlag;
+ } else {
+ if (error)
+ *error = "invalid string table name";
+ return 0;
+ }
+ } else if (Hdr->name[1] == ' ') {
+ if (0 == memcmp(Hdr->name, ARFILE_SVR4_SYMTAB_NAME, 16)) {
+ pathname.assign(ARFILE_SVR4_SYMTAB_NAME);
+ flags |= ArchiveMember::SVR4SymbolTableFlag;
+ } else {
+ if (error)
+ *error = "invalid SVR4 symbol table name";
+ return 0;
+ }
+ } else if (isdigit(Hdr->name[1])) {
+ unsigned index = atoi(&Hdr->name[1]);
+ if (index < strtab.length()) {
+ const char* namep = strtab.c_str() + index;
+ const char* endp = strtab.c_str() + strtab.length();
+ const char* p = namep;
+ const char* last_p = p;
+ while (p < endp) {
+ if (*p == '\n' && *last_p == '/') {
+ pathname.assign(namep, last_p - namep);
+ flags |= ArchiveMember::HasLongFilenameFlag;
+ break;
+ }
+ last_p = p;
+ p++;
+ }
+ if (p >= endp) {
+ if (error)
+ *error = "missing name termiantor in string table";
+ return 0;
+ }
+ } else {
+ if (error)
+ *error = "name index beyond string table";
+ return 0;
+ }
+ }
+ break;
+ case '_':
+ if (Hdr->name[1] == '_' &&
+ (0 == memcmp(Hdr->name, ARFILE_BSD4_SYMTAB_NAME, 16))) {
+ pathname.assign(ARFILE_BSD4_SYMTAB_NAME);
+ flags |= ArchiveMember::BSD4SymbolTableFlag;
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ char* slash = (char*) memchr(Hdr->name, '/', 16);
+ if (slash == 0)
+ slash = Hdr->name + 16;
+ pathname.assign(Hdr->name, slash - Hdr->name);
+ break;
+ }
+
+ // Determine if this is a bitcode file
+ switch (sys::IdentifyFileType(At, 4)) {
+ case sys::Bitcode_FileType:
+ flags |= ArchiveMember::BitcodeFlag;
+ break;
+ default:
+ flags &= ~ArchiveMember::BitcodeFlag;
+ break;
+ }
+
+ // Instantiate the ArchiveMember to be filled
+ ArchiveMember* member = new ArchiveMember(this);
+
+ // Fill in fields of the ArchiveMember
+ member->parent = this;
+ member->path.set(pathname);
+ member->info.fileSize = MemberSize;
+ member->info.modTime.fromEpochTime(atoi(Hdr->date));
+ unsigned int mode;
+ sscanf(Hdr->mode, "%o", &mode);
+ member->info.mode = mode;
+ member->info.user = atoi(Hdr->uid);
+ member->info.group = atoi(Hdr->gid);
+ member->flags = flags;
+ member->data = At;
+
+ return member;