// Write out the LLVM symbol table as an archive member to the file. void Archive::writeSymbolTable(std::ofstream& ARFile) { // Construct the symbol table's header ArchiveMemberHeader Hdr; Hdr.init(); memcpy(Hdr.name,ARFILE_LLVM_SYMTAB_NAME,16); uint64_t secondsSinceEpoch = sys::TimeValue::now().toEpochTime(); char buffer[32]; sprintf(buffer, "%-8o", 0644); memcpy(Hdr.mode,buffer,8); sprintf(buffer, "%-6u", sys::Process::GetCurrentUserId()); memcpy(Hdr.uid,buffer,6); sprintf(buffer, "%-6u", sys::Process::GetCurrentGroupId()); memcpy(Hdr.gid,buffer,6); sprintf(buffer,"%-12u", unsigned(secondsSinceEpoch)); memcpy(Hdr.date,buffer,12); sprintf(buffer,"%-10u",symTabSize); memcpy(Hdr.size,buffer,10); // Write the header ARFile.write((char*)&Hdr, sizeof(Hdr)); #ifndef NDEBUG // Save the starting position of the symbol tables data content. unsigned startpos = ARFile.tellp(); #endif // Write out the symbols sequentially for ( Archive::SymTabType::iterator I = symTab.begin(), E = symTab.end(); I != E; ++I) { // Write out the file index writeInteger(I->second, ARFile); // Write out the length of the symbol writeInteger(I->first.length(), ARFile); // Write out the symbol ARFile.write(I->first.data(), I->first.length()); } #ifndef NDEBUG // Now that we're done with the symbol table, get the ending file position unsigned endpos = ARFile.tellp(); #endif // Make sure that the amount we wrote is what we pre-computed. This is // critical for file integrity purposes. assert(endpos - startpos == symTabSize && "Invalid symTabSize computation"); // Make sure the symbol table is even sized if (symTabSize % 2 != 0 ) ARFile << ARFILE_PAD; }
// Fill the ArchiveMemberHeader with the information from a member. If // TruncateNames is true, names are flattened to 15 chars or less. The sz field // is provided here instead of coming from the mbr because the member might be // stored compressed and the compressed size is not the ArchiveMember's size. // Furthermore compressed files have negative size fields to identify them as // compressed. bool Archive::fillHeader(const ArchiveMember &mbr, ArchiveMemberHeader& hdr, int sz) const { // Set the permissions mode, uid and gid hdr.init(); char buffer[32]; sprintf(buffer, "%-8o", mbr.getMode()); memcpy(hdr.mode,buffer,8); sprintf(buffer, "%-6u", mbr.getUser()); memcpy(hdr.uid,buffer,6); sprintf(buffer, "%-6u", mbr.getGroup()); memcpy(hdr.gid,buffer,6); // Set the last modification date uint64_t secondsSinceEpoch = mbr.getModTime().toEpochTime(); sprintf(buffer,"%-12u", unsigned(secondsSinceEpoch)); memcpy(hdr.date,buffer,12); std::string mbrPath = sys::path::filename(mbr.getPath()); // Set the name field in one of its various flavors. bool writeLongName = false; if (mbr.isStringTable()) { memcpy(hdr.name,ARFILE_STRTAB_NAME,16); } else if (mbr.isSVR4SymbolTable()) { memcpy(hdr.name,ARFILE_SVR4_SYMTAB_NAME,16); } else if (mbr.isBSD4SymbolTable()) { memcpy(hdr.name,ARFILE_BSD4_SYMTAB_NAME,16); } else if (mbrPath.length() < 16 && mbrPath.find('/') == std::string::npos) { memcpy(hdr.name,mbrPath.c_str(),mbrPath.length()); hdr.name[mbrPath.length()] = '/'; } else { std::string nm = "#1/"; nm += utostr(mbrPath.length()); memcpy(hdr.name,nm.data(),nm.length()); if (sz < 0) sz -= mbrPath.length(); else sz += mbrPath.length(); writeLongName = true; } // Set the size field if (sz < 0) { buffer[0] = '-'; sprintf(&buffer[1],"%-9u",(unsigned)-sz); } else { sprintf(buffer, "%-10u", (unsigned)sz); } memcpy(hdr.size,buffer,10); return writeLongName; }
LibInfo read_lib(const fs::path& path) { std::fstream fs(path, std::ios::in | std::ios::binary | std::ios::ate); Checks::check_exit(VCPKG_LINE_INFO, fs.is_open(), "Could not open file %s for reading", path.generic_string()); read_and_verify_archive_file_signature(fs); Marker marker; marker.set_to_current_pos(fs); // First Linker Member const ArchiveMemberHeader first_linker_member_header = ArchiveMemberHeader::read(fs); Checks::check_exit(VCPKG_LINE_INFO, first_linker_member_header.name().substr(0, 2) == "/ ", "Could not find proper first linker member"); marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + first_linker_member_header.member_size()); marker.seek_to_marker(fs); const ArchiveMemberHeader second_linker_member_header = ArchiveMemberHeader::read(fs); Checks::check_exit(VCPKG_LINE_INFO, second_linker_member_header.name().substr(0, 2) == "/ ", "Could not find proper second linker member"); // The first 4 bytes contains the number of archive members const auto archive_member_count = read_value_from_stream<uint32_t>(fs); const OffsetsArray offsets = OffsetsArray::read(fs, archive_member_count); marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + second_linker_member_header.member_size()); marker.seek_to_marker(fs); const bool has_longname_member_header = peek_value_from_stream<uint16_t>(fs) == 0x2F2F; if (has_longname_member_header) { const ArchiveMemberHeader longnames_member_header = ArchiveMemberHeader::read(fs); marker.advance_by(ArchiveMemberHeader::HEADER_SIZE + longnames_member_header.member_size()); marker.seek_to_marker(fs); } std::set<MachineType> machine_types; // Next we have the obj and pseudo-object files for (const uint32_t offset : offsets.data) { marker.set_to_offset(offset + ArchiveMemberHeader::HEADER_SIZE); // Skip the header, no need to read it. marker.seek_to_marker(fs); const auto first_two_bytes = peek_value_from_stream<uint16_t>(fs); const bool is_import_header = to_machine_type(first_two_bytes) == MachineType::UNKNOWN; const MachineType machine = is_import_header ? ImportHeader::read(fs).machine_type() : CoffFileHeader::read(fs).machine_type(); machine_types.insert(machine); } return {std::vector<MachineType>(machine_types.cbegin(), machine_types.cend())}; }
static bool isInternalMember(const ArchiveMemberHeader &amh) { static const char *const internals[] = { "/", "//" }; StringRef name = amh.getName(); for (std::size_t i = 0; i < sizeof(internals) / sizeof(*internals); ++i) { if (name == internals[i]) return true; } return false; }
// Fill the ArchiveMemberHeader with the information from a member. If // TruncateNames is true, names are flattened to 15 chars or less. The sz field // is provided here instead of coming from the mbr because the member might be // stored compressed and the compressed size is not the ArchiveMember's size. // Furthermore compressed files have negative size fields to identify them as // compressed. bool Archive::fillHeader(const ArchiveMember &mbr, ArchiveMemberHeader& hdr, int sz, bool TruncateNames) const { // Set the permissions mode, uid and gid hdr.init(); char buffer[32]; sprintf(buffer, "%-8o", mbr.getMode()); memcpy(hdr.mode,buffer,8); sprintf(buffer, "%-6u", mbr.getUser()); memcpy(hdr.uid,buffer,6); sprintf(buffer, "%-6u", mbr.getGroup()); memcpy(hdr.gid,buffer,6); // Set the last modification date uint64_t secondsSinceEpoch = mbr.getModTime().toEpochTime(); sprintf(buffer,"%-12u", unsigned(secondsSinceEpoch)); memcpy(hdr.date,buffer,12); // Get rid of trailing blanks in the name std::string mbrPath = mbr.getPath().str(); size_t mbrLen = mbrPath.length(); while (mbrLen > 0 && mbrPath[mbrLen-1] == ' ') { mbrPath.erase(mbrLen-1,1); mbrLen--; } // Set the name field in one of its various flavors. bool writeLongName = false; if (mbr.isStringTable()) { memcpy(hdr.name,ARFILE_STRTAB_NAME,16); } else if (mbr.isSVR4SymbolTable()) { memcpy(hdr.name,ARFILE_SVR4_SYMTAB_NAME,16); } else if (mbr.isBSD4SymbolTable()) { memcpy(hdr.name,ARFILE_BSD4_SYMTAB_NAME,16); } else if (mbr.isLLVMSymbolTable()) { memcpy(hdr.name,ARFILE_LLVM_SYMTAB_NAME,16); } else if (TruncateNames) { const char* nm = mbrPath.c_str(); unsigned len = mbrPath.length(); size_t slashpos = mbrPath.rfind('/'); if (slashpos != std::string::npos) { nm += slashpos + 1; len -= slashpos +1; } if (len > 15) len = 15; memcpy(hdr.name,nm,len); hdr.name[len] = '/'; } else if (mbrPath.length() < 16 && mbrPath.find('/') == std::string::npos) { memcpy(hdr.name,mbrPath.c_str(),mbrPath.length()); hdr.name[mbrPath.length()] = '/'; } else { std::string nm = "#1/"; nm += utostr(mbrPath.length()); memcpy(hdr.name,nm.data(),nm.length()); if (sz < 0) sz -= mbrPath.length(); else sz += mbrPath.length(); writeLongName = true; } // Set the size field if (sz < 0) { buffer[0] = '-'; sprintf(&buffer[1],"%-9u",(unsigned)-sz); } else { sprintf(buffer, "%-10u", (unsigned)sz); } memcpy(hdr.size,buffer,10); return writeLongName; }
// 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]); const char *nulp = (const char *)memchr(At, '\0', len); pathname.assign(At, nulp != 0 ? (uintptr_t)(nulp - 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; }