Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -578,10 +578,9 @@ std::string Temp = S.str(); TemporaryFiles.push_back(Temp); - std::pair Ret = - llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU, - /*Deterministics=*/true, - /*Thin=*/false); + std::pair Ret = llvm::writeArchive( + Temp, New, /*WriteSymtab=*/true, /*WriteObjPaths=*/true, + Archive::Kind::K_GNU, /*Deterministics=*/true, /*Thin=*/false); if (Ret.second) error("failed to create a new archive " + S.str() + ": " + Ret.first); return Temp; Index: llvm/include/llvm/Object/ArchiveWriter.h =================================================================== --- llvm/include/llvm/Object/ArchiveWriter.h +++ llvm/include/llvm/Object/ArchiveWriter.h @@ -38,8 +38,9 @@ std::pair writeArchive(StringRef ArcName, std::vector &NewMembers, - bool WriteSymtab, object::Archive::Kind Kind, bool Deterministic, - bool Thin, std::unique_ptr OldArchiveBuf = nullptr); + bool WriteSymtab, bool WriteObjPaths, object::Archive::Kind Kind, + bool Deterministic, bool Thin, + std::unique_ptr OldArchiveBuf = nullptr); } #endif Index: llvm/lib/Object/ArchiveWriter.cpp =================================================================== --- llvm/lib/Object/ArchiveWriter.cpp +++ llvm/lib/Object/ArchiveWriter.cpp @@ -183,22 +183,22 @@ Out.write(uint8_t(0)); } -static bool useStringTable(bool Thin, StringRef Name) { - return Thin || Name.size() >= 16; +/// A member needs to use the string table if it is long or if it contains a +/// slash. +static bool useStringTable(StringRef Name) { + return Name.size() >= 16 || Name.find('/') != StringRef::npos; } static void -printMemberHeader(raw_fd_ostream &Out, object::Archive::Kind Kind, bool Thin, +printMemberHeader(raw_fd_ostream &Out, object::Archive::Kind Kind, StringRef Name, - std::vector::iterator &StringMapIndexIter, const sys::TimePoint &ModTime, unsigned UID, unsigned GID, unsigned Perms, unsigned Size) { if (isBSDLike(Kind)) return printBSDMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size); - if (!useStringTable(Thin, Name)) + if (!Name.startswith("/")) return printGNUSmallMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size); - Out << '/'; - printWithSpacePadding(Out, *StringMapIndexIter++, 15); + printWithSpacePadding(Out, Name, 16); printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); } @@ -231,32 +231,63 @@ return Relative.str(); } +/// There are three kinds of archive member names: +/// 1. Plain filename: foo.o +/// 2. Plain path: lib/bar/foo.o +/// 3. Relative path from archive for thin archives: ../lib/bar/foo.o +static std::string computeMemberName(StringRef ArchivePath, StringRef ObjPath, + bool WriteObjPaths, bool Thin) { + // The non-thin cases are easy: either return the path, or the filename. + if (!Thin) { + if (WriteObjPaths) + return ObjPath; + return sys::path::filename(ObjPath); + } + // In the thin case, compute the relative paths and return that. + return computeRelativePath(ArchivePath, ObjPath); +} + +std::vector +computeMemberNames(ArrayRef NewMembers, StringRef ArcName, + bool WriteObjPaths, bool Thin) { + std::vector Names; + for (const NewArchiveMember &M : NewMembers) { + if (M.IsNew) { + Names.push_back(computeMemberName(ArcName, M.Buf->getBufferIdentifier(), + WriteObjPaths, Thin)); + } else { + Names.push_back(M.Buf->getBufferIdentifier()); + } + } + return Names; +} + +/// Write a string table. Modify the names in place to contain the names that +/// will appear in the fixed 16 byte archive member header. static void writeStringTable(raw_fd_ostream &Out, StringRef ArcName, - ArrayRef Members, - std::vector &StringMapIndexes, - bool Thin) { + std::vector &MemberNames) { unsigned StartOffset = 0; - for (const NewArchiveMember &M : Members) { - StringRef Path = M.Buf->getBufferIdentifier(); - StringRef Name = sys::path::filename(Path); - if (!useStringTable(Thin, Name)) + for (std::string &Name : MemberNames) { + if (!useStringTable(Name)) continue; + if (StartOffset == 0) { printWithSpacePadding(Out, "//", 58); Out << "`\n"; StartOffset = Out.tell(); } - StringMapIndexes.push_back(Out.tell() - StartOffset); - if (Thin) { - if (M.IsNew) - Out << computeRelativePath(ArcName, Path); - else - Out << M.Buf->getBufferIdentifier(); - } else - Out << Name; + // Compute the short raw name that should be used in the 16 byte member + // header. + unsigned StringMapIndex = Out.tell() - StartOffset; + std::string RawName; + RawName = (Twine('/') + Twine(StringMapIndex)).str(); - Out << "/\n"; + // Print the long name into the string table. + Out << Name << "/\n"; + + // Replace Name with the short raw name. + Name = std::move(RawName); } if (StartOffset == 0) return; @@ -372,11 +403,12 @@ } std::pair -llvm::writeArchive(StringRef ArcName, - std::vector &NewMembers, - bool WriteSymtab, object::Archive::Kind Kind, - bool Deterministic, bool Thin, +llvm::writeArchive(StringRef ArcName, std::vector &NewMembers, + bool WriteSymtab, bool WriteObjPaths, + object::Archive::Kind Kind, bool Deterministic, bool Thin, std::unique_ptr OldArchiveBuf) { + if (Thin) + assert(WriteObjPaths && "thin archives require paths for archive members"); assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode"); SmallString<128> TmpArchive; int TmpArchiveFD; @@ -402,13 +434,15 @@ MemberReferenceOffset = MemberReferenceOffsetOrErr.get(); } - std::vector StringMapIndexes; + std::vector MemberNames = + computeMemberNames(NewMembers, ArcName, WriteObjPaths, Thin); if (!isBSDLike(Kind)) - writeStringTable(Out, ArcName, NewMembers, StringMapIndexes, Thin); + writeStringTable(Out, ArcName, MemberNames); - std::vector::iterator StringMapIndexIter = StringMapIndexes.begin(); std::vector MemberOffset; - for (const NewArchiveMember &M : NewMembers) { + for (const auto &P : zip(NewMembers, MemberNames)) { + const NewArchiveMember &M = std::get<0>(P); + const std::string &Name = std::get<1>(P); MemoryBufferRef File = M.Buf->getMemBufferRef(); unsigned Padding = 0; @@ -422,9 +456,7 @@ if (Kind == object::Archive::K_DARWIN) Padding = OffsetToAlignment(M.Buf->getBufferSize(), 8); - printMemberHeader(Out, Kind, Thin, - sys::path::filename(M.Buf->getBufferIdentifier()), - StringMapIndexIter, M.ModTime, M.UID, M.GID, M.Perms, + printMemberHeader(Out, Kind, Name, M.ModTime, M.UID, M.GID, M.Perms, M.Buf->getBufferSize() + Padding); if (!Thin) Index: llvm/lib/Object/COFFImportFile.cpp =================================================================== --- llvm/lib/Object/COFFImportFile.cpp +++ llvm/lib/Object/COFFImportFile.cpp @@ -516,10 +516,9 @@ OF.createShortImport(*Name, E.Ordinal, ImportType, NameType)); } - std::pair Result = - writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU, - /*Deterministic*/ true, /*Thin*/ false); - + std::pair Result = writeArchive( + Path, Members, /*WriteSymtab=*/true, /*WriteObjPaths=*/false, + object::Archive::K_GNU, /*Deterministic=*/true, /*Thin=*/false); return Result.second; } Index: llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp =================================================================== --- llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp +++ llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp @@ -155,10 +155,10 @@ Members.emplace_back(std::move(*MOrErr)); } - std::pair Result = - llvm::writeArchive(getOutputPath(&Args, Members[0]), Members, - /*WriteSymtab=*/true, object::Archive::K_GNU, - /*Deterministic*/ true, Args.hasArg(OPT_llvmlibthin)); + std::pair Result = llvm::writeArchive( + getOutputPath(&Args, Members[0]), Members, /*WriteSymtab=*/true, + /*WriteObjPaths=*/true, object::Archive::K_GNU, /*Deterministic=*/true, + Args.hasArg(OPT_llvmlibthin)); if (Result.second) { if (Result.first.empty()) Index: llvm/test/LibDriver/use-paths.test =================================================================== --- /dev/null +++ llvm/test/LibDriver/use-paths.test @@ -0,0 +1,24 @@ +llvm-lib should behave like "link.exe /lib" and use relative paths to describe +archive members. + +First, get in a clean working directory. +RUN: rm -rf %t && mkdir -p %t && cd %t + +Make foo/a.obj and foo/b.obj. +RUN: mkdir foo +RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o foo/a.obj %S/Inputs/a.s +RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o foo/b.obj %S/Inputs/b.s + +RUN: llvm-lib -out:foo.lib foo/a.obj foo/b.obj +RUN: llvm-ar t foo.lib | FileCheck %s + +FIXME: We should probably use backslashes on Windows to better match MSVC tools. +CHECK: foo/a.obj +CHECK: foo/b.obj + +Do it again with absolute paths and see that we get something. +RUN: llvm-lib -out:foo.lib %t/foo/a.obj %t/foo/b.obj +RUN: llvm-ar t foo.lib | FileCheck %s --check-prefix=ABS + +ABS: {{.*}}/foo/a.obj +ABS: {{.*}}/foo/b.obj Index: llvm/test/Object/archive-format.test =================================================================== --- llvm/test/Object/archive-format.test +++ llvm/test/Object/archive-format.test @@ -54,12 +54,11 @@ RUN: llvm-ar --format=gnu rcT test.a 0123456789abcde 0123456789abcdef RUN: cat test.a | FileCheck -strict-whitespace --check-prefix=THIN %s THIN: ! -THIN-NEXT: // 36 ` -THIN-NEXT: 0123456789abcde/ +THIN-NEXT: // 18 ` THIN-NEXT: 0123456789abcdef/{{$}} THIN: {{^$}} -THIN: /0 0 0 0 644 4 ` -THIN-NEXT: /17 0 0 0 644 4 ` +THIN: 0123456789abcde/0 0 0 644 4 ` +THIN-NEXT: /0 0 0 0 644 4 ` RUN: mkdir -p bar RUN: rm -f bar/test.a Index: llvm/tools/llvm-ar/llvm-ar.cpp =================================================================== --- llvm/tools/llvm-ar/llvm-ar.cpp +++ llvm/tools/llvm-ar/llvm-ar.cpp @@ -685,7 +685,8 @@ std::pair Result = writeArchive(ArchiveName, NewMembersP ? *NewMembersP : NewMembers, Symtab, - Kind, Deterministic, Thin, std::move(OldArchiveBuf)); + /*WriteObjPaths=*/Thin, Kind, Deterministic, Thin, + std::move(OldArchiveBuf)); failIfError(Result.second, Result.first); }