1 //===- ArchiveWriter.cpp - ar File Format implementation --------*- 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 // This file defines the writeArchive function. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Object/ArchiveWriter.h" 15 #include "llvm/ADT/ArrayRef.h" 16 #include "llvm/ADT/StringRef.h" 17 #include "llvm/IR/LLVMContext.h" 18 #include "llvm/Object/Archive.h" 19 #include "llvm/Object/ObjectFile.h" 20 #include "llvm/Object/SymbolicFile.h" 21 #include "llvm/Support/EndianStream.h" 22 #include "llvm/Support/Errc.h" 23 #include "llvm/Support/ErrorHandling.h" 24 #include "llvm/Support/Format.h" 25 #include "llvm/Support/Path.h" 26 #include "llvm/Support/ToolOutputFile.h" 27 #include "llvm/Support/raw_ostream.h" 28 29 #if !defined(_MSC_VER) && !defined(__MINGW32__) 30 #include <unistd.h> 31 #else 32 #include <io.h> 33 #endif 34 35 using namespace llvm; 36 37 NewArchiveIterator::NewArchiveIterator() {} 38 39 NewArchiveIterator::NewArchiveIterator(object::Archive::child_iterator I, 40 StringRef Name) 41 : IsNewMember(false), Name(Name), OldI(I) {} 42 43 NewArchiveIterator::NewArchiveIterator(StringRef NewFilename, StringRef Name) 44 : IsNewMember(true), Name(Name), NewFilename(NewFilename) {} 45 46 StringRef NewArchiveIterator::getName() const { return Name; } 47 48 bool NewArchiveIterator::isNewMember() const { return IsNewMember; } 49 50 object::Archive::child_iterator NewArchiveIterator::getOld() const { 51 assert(!IsNewMember); 52 return OldI; 53 } 54 55 StringRef NewArchiveIterator::getNew() const { 56 assert(IsNewMember); 57 return NewFilename; 58 } 59 60 llvm::ErrorOr<int> 61 NewArchiveIterator::getFD(sys::fs::file_status &NewStatus) const { 62 assert(IsNewMember); 63 int NewFD; 64 if (auto EC = sys::fs::openFileForRead(NewFilename, NewFD)) 65 return EC; 66 assert(NewFD != -1); 67 68 if (auto EC = sys::fs::status(NewFD, NewStatus)) 69 return EC; 70 71 // Opening a directory doesn't make sense. Let it fail. 72 // Linux cannot open directories with open(2), although 73 // cygwin and *bsd can. 74 if (NewStatus.type() == sys::fs::file_type::directory_file) 75 return make_error_code(errc::is_a_directory); 76 77 return NewFD; 78 } 79 80 template <typename T> 81 static void printWithSpacePadding(raw_fd_ostream &OS, T Data, unsigned Size, 82 bool MayTruncate = false) { 83 uint64_t OldPos = OS.tell(); 84 OS << Data; 85 unsigned SizeSoFar = OS.tell() - OldPos; 86 if (Size > SizeSoFar) { 87 OS.indent(Size - SizeSoFar); 88 } else if (Size < SizeSoFar) { 89 assert(MayTruncate && "Data doesn't fit in Size"); 90 // Some of the data this is used for (like UID) can be larger than the 91 // space available in the archive format. Truncate in that case. 92 OS.seek(OldPos + Size); 93 } 94 } 95 96 static void print32BE(raw_ostream &Out, uint32_t Val) { 97 support::endian::Writer<support::big>(Out).write(Val); 98 } 99 100 static void printRestOfMemberHeader(raw_fd_ostream &Out, 101 const sys::TimeValue &ModTime, unsigned UID, 102 unsigned GID, unsigned Perms, 103 unsigned Size) { 104 printWithSpacePadding(Out, ModTime.toEpochTime(), 12); 105 printWithSpacePadding(Out, UID, 6, true); 106 printWithSpacePadding(Out, GID, 6, true); 107 printWithSpacePadding(Out, format("%o", Perms), 8); 108 printWithSpacePadding(Out, Size, 10); 109 Out << "`\n"; 110 } 111 112 static void printMemberHeader(raw_fd_ostream &Out, StringRef Name, 113 const sys::TimeValue &ModTime, unsigned UID, 114 unsigned GID, unsigned Perms, unsigned Size) { 115 printWithSpacePadding(Out, Twine(Name) + "/", 16); 116 printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); 117 } 118 119 static void printMemberHeader(raw_fd_ostream &Out, unsigned NameOffset, 120 const sys::TimeValue &ModTime, unsigned UID, 121 unsigned GID, unsigned Perms, unsigned Size) { 122 Out << '/'; 123 printWithSpacePadding(Out, NameOffset, 15); 124 printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); 125 } 126 127 static void writeStringTable(raw_fd_ostream &Out, 128 ArrayRef<NewArchiveIterator> Members, 129 std::vector<unsigned> &StringMapIndexes) { 130 unsigned StartOffset = 0; 131 for (ArrayRef<NewArchiveIterator>::iterator I = Members.begin(), 132 E = Members.end(); 133 I != E; ++I) { 134 StringRef Name = I->getName(); 135 if (Name.size() < 16) 136 continue; 137 if (StartOffset == 0) { 138 printWithSpacePadding(Out, "//", 58); 139 Out << "`\n"; 140 StartOffset = Out.tell(); 141 } 142 StringMapIndexes.push_back(Out.tell() - StartOffset); 143 Out << Name << "/\n"; 144 } 145 if (StartOffset == 0) 146 return; 147 if (Out.tell() % 2) 148 Out << '\n'; 149 int Pos = Out.tell(); 150 Out.seek(StartOffset - 12); 151 printWithSpacePadding(Out, Pos - StartOffset, 10); 152 Out.seek(Pos); 153 } 154 155 // Returns the offset of the first reference to a member offset. 156 static ErrorOr<unsigned> 157 writeSymbolTable(raw_fd_ostream &Out, ArrayRef<NewArchiveIterator> Members, 158 ArrayRef<MemoryBufferRef> Buffers, 159 std::vector<unsigned> &MemberOffsetRefs) { 160 unsigned StartOffset = 0; 161 unsigned MemberNum = 0; 162 std::string NameBuf; 163 raw_string_ostream NameOS(NameBuf); 164 unsigned NumSyms = 0; 165 LLVMContext Context; 166 for (ArrayRef<NewArchiveIterator>::iterator I = Members.begin(), 167 E = Members.end(); 168 I != E; ++I, ++MemberNum) { 169 MemoryBufferRef MemberBuffer = Buffers[MemberNum]; 170 ErrorOr<std::unique_ptr<object::SymbolicFile>> ObjOrErr = 171 object::SymbolicFile::createSymbolicFile( 172 MemberBuffer, sys::fs::file_magic::unknown, &Context); 173 if (!ObjOrErr) 174 continue; // FIXME: check only for "not an object file" errors. 175 object::SymbolicFile &Obj = *ObjOrErr.get(); 176 177 if (!StartOffset) { 178 printMemberHeader(Out, "", sys::TimeValue::now(), 0, 0, 0, 0); 179 StartOffset = Out.tell(); 180 print32BE(Out, 0); 181 } 182 183 for (const object::BasicSymbolRef &S : Obj.symbols()) { 184 uint32_t Symflags = S.getFlags(); 185 if (Symflags & object::SymbolRef::SF_FormatSpecific) 186 continue; 187 if (!(Symflags & object::SymbolRef::SF_Global)) 188 continue; 189 if (Symflags & object::SymbolRef::SF_Undefined) 190 continue; 191 if (auto EC = S.printName(NameOS)) 192 return EC; 193 NameOS << '\0'; 194 ++NumSyms; 195 MemberOffsetRefs.push_back(MemberNum); 196 print32BE(Out, 0); 197 } 198 } 199 Out << NameOS.str(); 200 201 if (StartOffset == 0) 202 return 0; 203 204 if (Out.tell() % 2) 205 Out << '\0'; 206 207 unsigned Pos = Out.tell(); 208 Out.seek(StartOffset - 12); 209 printWithSpacePadding(Out, Pos - StartOffset, 10); 210 Out.seek(StartOffset); 211 print32BE(Out, NumSyms); 212 Out.seek(Pos); 213 return StartOffset + 4; 214 } 215 216 std::pair<StringRef, std::error_code> 217 llvm::writeArchive(StringRef ArcName, 218 std::vector<NewArchiveIterator> &NewMembers, 219 bool WriteSymtab) { 220 SmallString<128> TmpArchive; 221 int TmpArchiveFD; 222 if (auto EC = sys::fs::createUniqueFile(ArcName + ".temp-archive-%%%%%%%.a", 223 TmpArchiveFD, TmpArchive)) 224 return std::make_pair(ArcName, EC); 225 226 tool_output_file Output(TmpArchive, TmpArchiveFD); 227 raw_fd_ostream &Out = Output.os(); 228 Out << "!<arch>\n"; 229 230 std::vector<unsigned> MemberOffsetRefs; 231 232 std::vector<std::unique_ptr<MemoryBuffer>> Buffers; 233 std::vector<MemoryBufferRef> Members; 234 std::vector<sys::fs::file_status> NewMemberStatus; 235 236 for (unsigned I = 0, N = NewMembers.size(); I < N; ++I) { 237 NewArchiveIterator &Member = NewMembers[I]; 238 MemoryBufferRef MemberRef; 239 240 if (Member.isNewMember()) { 241 StringRef Filename = Member.getNew(); 242 NewMemberStatus.resize(NewMemberStatus.size() + 1); 243 sys::fs::file_status &Status = NewMemberStatus.back(); 244 ErrorOr<int> FD = Member.getFD(Status); 245 if (auto EC = FD.getError()) 246 return std::make_pair(Filename, EC); 247 ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr = 248 MemoryBuffer::getOpenFile(FD.get(), Filename, Status.getSize(), 249 false); 250 if (auto EC = MemberBufferOrErr.getError()) 251 return std::make_pair(Filename, EC); 252 if (close(FD.get()) != 0) 253 return std::make_pair(Filename, 254 std::error_code(errno, std::generic_category())); 255 Buffers.push_back(std::move(MemberBufferOrErr.get())); 256 MemberRef = Buffers.back()->getMemBufferRef(); 257 } else { 258 object::Archive::child_iterator OldMember = Member.getOld(); 259 ErrorOr<MemoryBufferRef> MemberBufferOrErr = 260 OldMember->getMemoryBufferRef(); 261 if (auto EC = MemberBufferOrErr.getError()) 262 return std::make_pair("", EC); 263 MemberRef = MemberBufferOrErr.get(); 264 } 265 Members.push_back(MemberRef); 266 } 267 268 unsigned MemberReferenceOffset = 0; 269 if (WriteSymtab) { 270 ErrorOr<unsigned> MemberReferenceOffsetOrErr = 271 writeSymbolTable(Out, NewMembers, Members, MemberOffsetRefs); 272 if (auto EC = MemberReferenceOffsetOrErr.getError()) 273 return std::make_pair(ArcName, EC); 274 MemberReferenceOffset = MemberReferenceOffsetOrErr.get(); 275 } 276 277 std::vector<unsigned> StringMapIndexes; 278 writeStringTable(Out, NewMembers, StringMapIndexes); 279 280 unsigned MemberNum = 0; 281 unsigned LongNameMemberNum = 0; 282 unsigned NewMemberNum = 0; 283 std::vector<unsigned> MemberOffset; 284 for (std::vector<NewArchiveIterator>::iterator I = NewMembers.begin(), 285 E = NewMembers.end(); 286 I != E; ++I, ++MemberNum) { 287 288 unsigned Pos = Out.tell(); 289 MemberOffset.push_back(Pos); 290 291 MemoryBufferRef File = Members[MemberNum]; 292 if (I->isNewMember()) { 293 StringRef FileName = I->getNew(); 294 const sys::fs::file_status &Status = NewMemberStatus[NewMemberNum]; 295 NewMemberNum++; 296 297 StringRef Name = sys::path::filename(FileName); 298 if (Name.size() < 16) 299 printMemberHeader(Out, Name, Status.getLastModificationTime(), 300 Status.getUser(), Status.getGroup(), 301 Status.permissions(), Status.getSize()); 302 else 303 printMemberHeader(Out, StringMapIndexes[LongNameMemberNum++], 304 Status.getLastModificationTime(), Status.getUser(), 305 Status.getGroup(), Status.permissions(), 306 Status.getSize()); 307 } else { 308 object::Archive::child_iterator OldMember = I->getOld(); 309 StringRef Name = I->getName(); 310 311 if (Name.size() < 16) 312 printMemberHeader(Out, Name, OldMember->getLastModified(), 313 OldMember->getUID(), OldMember->getGID(), 314 OldMember->getAccessMode(), OldMember->getSize()); 315 else 316 printMemberHeader(Out, StringMapIndexes[LongNameMemberNum++], 317 OldMember->getLastModified(), OldMember->getUID(), 318 OldMember->getGID(), OldMember->getAccessMode(), 319 OldMember->getSize()); 320 } 321 322 Out << File.getBuffer(); 323 324 if (Out.tell() % 2) 325 Out << '\n'; 326 } 327 328 if (MemberReferenceOffset) { 329 Out.seek(MemberReferenceOffset); 330 for (unsigned MemberNum : MemberOffsetRefs) 331 print32BE(Out, MemberOffset[MemberNum]); 332 } 333 334 Output.keep(); 335 Out.close(); 336 sys::fs::rename(TmpArchive, ArcName); 337 return std::make_pair("", std::error_code()); 338 } 339