diff --git a/lld/MachO/Driver.h b/lld/MachO/Driver.h --- a/lld/MachO/Driver.h +++ b/lld/MachO/Driver.h @@ -43,6 +43,8 @@ llvm::Optional makeDylibFromTAPI(llvm::MemoryBufferRef mbref, DylibFile *umbrella = nullptr); +uint32_t getModTime(llvm::StringRef path); + } // namespace macho } // namespace lld diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -212,21 +212,32 @@ {"/Library/Frameworks", "/System/Library/Frameworks"}); } +namespace { +struct ArchiveMember { + MemoryBufferRef mbref; + uint32_t modTime; +}; +} // namespace + // Returns slices of MB by parsing MB as an archive file. // Each slice consists of a member file in the archive. -static std::vector getArchiveMembers(MemoryBufferRef mb) { +static std::vector getArchiveMembers(MemoryBufferRef mb) { std::unique_ptr file = CHECK(Archive::create(mb), mb.getBufferIdentifier() + ": failed to parse archive"); - std::vector v; + std::vector v; Error err = Error::success(); for (const Archive::Child &c : file->children(err)) { MemoryBufferRef mbref = CHECK(c.getMemoryBufferRef(), mb.getBufferIdentifier() + ": could not get the buffer for a child of the archive"); - v.push_back(mbref); + uint32_t modTime = toTimeT( + CHECK(c.getLastModified(), mb.getBufferIdentifier() + + ": could not get the modification " + "time for a child of the archive")); + v.push_back({mbref, modTime}); } if (err) fatal(mb.getBufferIdentifier() + @@ -235,6 +246,16 @@ return v; } +static void forceLoadArchive(StringRef path) { + if (Optional buffer = readFile(path)) { + for (const ArchiveMember &member : getArchiveMembers(*buffer)) { + auto file = make(member.mbref, member.modTime); + file->archiveName = buffer->getBufferIdentifier(); + inputFiles.push_back(file); + } + } +} + static InputFile *addFile(StringRef path) { Optional buffer = readFile(path); if (!buffer) @@ -251,9 +272,7 @@ error(path + ": archive has no index; run ranlib to add one"); if (config->allLoad) { - if (Optional buffer = readFile(path)) - for (MemoryBufferRef member : getArchiveMembers(*buffer)) - inputFiles.push_back(make(member)); + forceLoadArchive(path); } else if (config->forceLoadObjC) { for (const object::Archive::Symbol &sym : file->symbols()) if (sym.getName().startswith(objc::klass)) @@ -264,16 +283,16 @@ // consider creating a LazyObjFile class in order to avoid double-loading // these files here and below (as part of the ArchiveFile). if (Optional buffer = readFile(path)) - for (MemoryBufferRef member : getArchiveMembers(*buffer)) - if (hasObjCSection(member)) - inputFiles.push_back(make(member)); + for (const ArchiveMember &member : getArchiveMembers(*buffer)) + if (hasObjCSection(member.mbref)) + inputFiles.push_back(make(member.mbref, member.modTime)); } newFile = make(std::move(file)); break; } case file_magic::macho_object: - newFile = make(mbref); + newFile = make(mbref, getModTime(path)); break; case file_magic::macho_dynamically_linked_shared_lib: case file_magic::macho_dynamically_linked_shared_lib_stub: @@ -304,12 +323,6 @@ addFile(path); } -static void forceLoadArchive(StringRef path) { - if (Optional buffer = readFile(path)) - for (MemoryBufferRef member : getArchiveMembers(*buffer)) - inputFiles.push_back(make(member)); -} - static std::array archNames{"arm", "arm64", "i386", "x86_64", "ppc", "ppc64"}; static bool isArchString(StringRef s) { diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp --- a/lld/MachO/DriverUtils.cpp +++ b/lld/MachO/DriverUtils.cpp @@ -175,3 +175,13 @@ } return make(**result, umbrella); } + +uint32_t macho::getModTime(StringRef path) { + fs::file_status stat; + if (!fs::status(path, stat)) + if (fs::exists(stat)) + return toTimeT(stat.getLastModificationTime()); + + warn("failed to get modification time of " + path); + return 0; +} diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -90,10 +90,12 @@ // .o file class ObjFile : public InputFile { public: - explicit ObjFile(MemoryBufferRef mb); + explicit ObjFile(MemoryBufferRef mb, uint32_t modTime); static bool classof(const InputFile *f) { return f->kind() == ObjKind; } llvm::DWARFUnit *compileUnit = nullptr; + StringRef archiveName = ""; + const uint32_t modTime; private: void parseDebugInfo(); diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -364,7 +364,8 @@ subsections.push_back({{0, isec}}); } -ObjFile::ObjFile(MemoryBufferRef mb) : InputFile(ObjKind, mb) { +ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime) + : InputFile(ObjKind, mb), modTime(modTime) { auto *buf = reinterpret_cast(mb.getBufferStart()); auto *hdr = reinterpret_cast(mb.getBufferStart()); @@ -592,7 +593,16 @@ toString(this) + ": could not get the buffer for the member defining symbol " + sym.getName()); - auto file = make(mb); + + uint32_t modTime = toTimeT( + CHECK(c.getLastModified(), toString(this) + + ": could not get the modification time " + "for the member defining symbol " + + sym.getName())); + + auto file = make(mb, modTime); + file->archiveName = getName(); + symbols.insert(symbols.end(), file->symbols.begin(), file->symbols.end()); subsections.insert(subsections.end(), file->subsections.begin(), file->subsections.end()); diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp --- a/lld/MachO/LTO.cpp +++ b/lld/MachO/LTO.cpp @@ -73,10 +73,12 @@ saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o"); } + // TODO: set modTime properly std::vector ret; for (unsigned i = 0; i != maxTasks; ++i) if (!buf[i].empty()) - ret.push_back(make(MemoryBufferRef(buf[i], "lto.tmp"))); + ret.push_back( + make(MemoryBufferRef(buf[i], "lto.tmp"), /*modTime=*/0)); return ret; } diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -602,13 +602,18 @@ void SymtabSection::emitObjectFileStab(ObjFile *file) { StabsEntry stab(MachO::N_OSO); stab.sect = target->cpuSubtype; - SmallString<261> path(file->getName()); + SmallString<261> path(!file->archiveName.empty() ? file->archiveName + : file->getName()); std::error_code ec = sys::fs::make_absolute(path); if (ec) - fatal("failed to get absolute path for " + file->getName()); + fatal("failed to get absolute path for " + path); + + if (!file->archiveName.empty()) + path.append({"(", file->getName(), ")"}); stab.strx = stringTableSection.addString(saver.save(path.str())); stab.desc = 1; + stab.value = file->modTime; stabs.emplace_back(std::move(stab)); } diff --git a/lld/test/MachO/stabs.s b/lld/test/MachO/stabs.s --- a/lld/test/MachO/stabs.s +++ b/lld/test/MachO/stabs.s @@ -3,22 +3,35 @@ # RUN: split-file %s %t # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o +## Set modtimes of the files for deterministic test output. +# RUN: env TZ=UTC touch -t "197001010000.16" %t/test.o +# RUN: env TZ=UTC touch -t "197001010000.32" %t/foo.o +# RUN: rm -f %t/foo.a +# RUN: llvm-ar rcsU %t/foo.a %t/foo.o # RUN: %lld -lSystem %t/test.o %t/foo.o -o %t/test -# RUN: llvm-nm -pa %t/test | FileCheck %s -DDIR=%t +# RUN: llvm-nm -pa %t/test | FileCheck %s -DDIR=%t -DFOO_PATH=%t/foo.o + +## Check that we emit the right modtime even when the object file is in an +## archive. +# RUN: %lld -lSystem %t/test.o %t/foo.a -o %t/test +# RUN: llvm-nm -pa %t/test | FileCheck %s -DDIR=%t -DFOO_PATH=%t/foo.a\(foo.o\) ## Check that we emit absolute paths to the object files in our OSO entries ## even if our inputs are relative paths. # RUN: cd %t && %lld -lSystem test.o foo.o -o test -# RUN: llvm-nm -pa %t/test | FileCheck %s -DDIR=%t +# RUN: llvm-nm -pa %t/test | FileCheck %s -DDIR=%t -DFOO_PATH=%t/foo.o + +# RUN: cd %t && %lld -lSystem %t/test.o %t/foo.a -o %t/test +# RUN: llvm-nm -pa %t/test | FileCheck %s -DDIR=%t -DFOO_PATH=%t/foo.a\(foo.o\) # CHECK: 0000000000000000 - 00 0000 SO /tmp/test.cpp -# CHECK-NEXT: 0000000000000000 - 03 0001 OSO [[DIR]]/test.o +# CHECK-NEXT: 0000000000000010 - 03 0001 OSO [[DIR]]/test.o # CHECK-NEXT: [[#%x, MAIN:]] - 01 0000 FUN _main -# CHECK-NEXT: 0000000000000001 - 00 0000 FUN +# CHECK-NEXT: 0000000000000006 - 00 0000 FUN # CHECK-NEXT: 0000000000000000 - 01 0000 SO # CHECK-NEXT: 0000000000000000 - 00 0000 SO /foo.cpp -# CHECK-NEXT: 0000000000000000 - 03 0001 OSO [[DIR]]/foo.o +# CHECK-NEXT: 0000000000000020 - 03 0001 OSO [[FOO_PATH]] # CHECK-NEXT: [[#%x, FOO:]] - 01 0000 FUN _foo # CHECK-NEXT: 0000000000000001 - 00 0000 FUN # CHECK-NEXT: 0000000000000000 - 01 0000 SO @@ -30,6 +43,7 @@ .globl _main _main: Lfunc_begin0: + callq _foo retq Lfunc_end0: