Index: llvm/include/llvm/Object/MachO.h =================================================================== --- llvm/include/llvm/Object/MachO.h +++ llvm/include/llvm/Object/MachO.h @@ -311,6 +311,9 @@ bool isSectionBitcode(DataRefImpl Sec) const override; bool isDebugSection(DataRefImpl Sec) const override; + /// Return the raw contents of an entire segment. + ArrayRef getSegmentContents(StringRef SegmentName) const; + /// When dsymutil generates the companion file, it strips all unnecessary /// sections (e.g. everything in the _TEXT segment) by omitting their body /// and setting the offset in their corresponding load command to zero. Index: llvm/include/llvm/ObjectYAML/MachOYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/MachOYAML.h +++ llvm/include/llvm/ObjectYAML/MachOYAML.h @@ -131,6 +131,7 @@ std::vector LoadCommands; std::vector
Sections; LinkEditData LinkEdit; + Optional RawLinkEditSegment; DWARFYAML::Data DWARF; }; Index: llvm/lib/Object/MachOObjectFile.cpp =================================================================== --- llvm/lib/Object/MachOObjectFile.cpp +++ llvm/lib/Object/MachOObjectFile.cpp @@ -2048,6 +2048,46 @@ SectionName == "__swift_ast"; } +namespace { +template +ArrayRef getSegmentContents(const MachOObjectFile &Obj, + MachOObjectFile::LoadCommandInfo LoadCmd, + StringRef SegmentName) { + auto SegmentOrErr = getStructOrErr(Obj, LoadCmd.Ptr); + if (!SegmentOrErr) { + consumeError(SegmentOrErr.takeError()); + return {}; + } + auto &Segment = SegmentOrErr.get(); + if (StringRef(Segment.segname, 16).startswith(SegmentName)) + return arrayRefFromStringRef(Obj.getData().slice( + Segment.fileoff, Segment.fileoff + Segment.filesize)); + return {}; +} +} // namespace + +ArrayRef +MachOObjectFile::getSegmentContents(StringRef SegmentName) const { + for (auto LoadCmd : load_commands()) { + ArrayRef Contents; + switch (LoadCmd.C.cmd) { + case MachO::LC_SEGMENT: + Contents = ::getSegmentContents(*this, LoadCmd, + SegmentName); + break; + case MachO::LC_SEGMENT_64: + Contents = ::getSegmentContents(*this, LoadCmd, + SegmentName); + break; + default: + continue; + } + if (!Contents.empty()) + return Contents; + } + return {}; +} + unsigned MachOObjectFile::getSectionID(SectionRef Sec) const { return Sec.getRawDataRefImpl().d.a; } Index: llvm/lib/ObjectYAML/MachOEmitter.cpp =================================================================== --- llvm/lib/ObjectYAML/MachOEmitter.cpp +++ llvm/lib/ObjectYAML/MachOEmitter.cpp @@ -288,6 +288,7 @@ } Error MachOWriter::writeSectionData(raw_ostream &OS) { + uint64_t LinkEditOff = 0; for (auto &LC : Obj.LoadCommands) { switch (LC.Data.load_command_data.cmd) { case MachO::LC_SEGMENT: @@ -297,6 +298,9 @@ if (0 == strncmp(&LC.Data.segment_command_data.segname[0], "__LINKEDIT", 16)) { FoundLinkEditSeg = true; + LinkEditOff = segOff; + if (Obj.RawLinkEditSegment) + continue; writeLinkEditData(OS); } for (auto &Sec : LC.Sections) { @@ -344,6 +348,13 @@ } } + if (Obj.RawLinkEditSegment) { + ZeroToOffset(OS, LinkEditOff); + if (OS.tell() - fileStart > LinkEditOff || !LinkEditOff) + return createStringError(errc::invalid_argument, + "section offsets don't line up"); + Obj.RawLinkEditSegment->writeAsBinary(OS); + } return Error::success(); } Index: llvm/lib/ObjectYAML/MachOYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/MachOYAML.cpp +++ llvm/lib/ObjectYAML/MachOYAML.cpp @@ -110,6 +110,9 @@ Object.DWARF.Is64BitAddrSize = Object.Header.magic == MachO::MH_MAGIC_64 || Object.Header.magic == MachO::MH_CIGAM_64; IO.mapOptional("LoadCommands", Object.LoadCommands); + + if (Object.RawLinkEditSegment || !IO.outputting()) + IO.mapOptional("__LINKEDIT", Object.RawLinkEditSegment); if(!Object.LinkEdit.isEmpty() || !IO.outputting()) IO.mapOptional("LinkEditData", Object.LinkEdit); Index: llvm/tools/obj2yaml/macho2yaml.cpp =================================================================== --- llvm/tools/obj2yaml/macho2yaml.cpp +++ llvm/tools/obj2yaml/macho2yaml.cpp @@ -29,6 +29,7 @@ const object::MachOObjectFile &Obj; std::unique_ptr DWARFCtx; + unsigned RawSegments; void dumpHeader(std::unique_ptr &Y); Error dumpLoadCommands(std::unique_ptr &Y); void dumpLinkEdit(std::unique_ptr &Y); @@ -52,8 +53,8 @@ public: MachODumper(const object::MachOObjectFile &O, - std::unique_ptr DCtx) - : Obj(O), DWARFCtx(std::move(DCtx)) {} + std::unique_ptr DCtx, unsigned RawSegments) + : Obj(O), DWARFCtx(std::move(DCtx)), RawSegments(RawSegments) {} Expected> dump(); }; @@ -176,6 +177,13 @@ if (Expected S = constructSection(Sec, Sections.size() + 1)) { StringRef SecName(S->sectname); + + // Copy data sections if requested. + if ((RawSegments & RawSegments::data) && + StringRef(S->segname).startswith("__DATA")) + S->content = + yaml::BinaryRef(Obj.getSectionContents(Sec.offset, Sec.size)); + if (SecName.startswith("__debug_")) { // If the DWARF section cannot be successfully parsed, emit raw content // instead of an entry in the DWARF section of the YAML. @@ -282,7 +290,11 @@ dumpHeader(Y); if (Error Err = dumpLoadCommands(Y)) return std::move(Err); - dumpLinkEdit(Y); + if (RawSegments & RawSegments::linkedit) + Y->RawLinkEditSegment = + yaml::BinaryRef(Obj.getSegmentContents("__LINKEDIT")); + else + dumpLinkEdit(Y); return std::move(Y); } @@ -587,9 +599,10 @@ } } -Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj) { +Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj, + unsigned RawSegments) { std::unique_ptr DCtx = DWARFContext::create(Obj); - MachODumper Dumper(Obj, std::move(DCtx)); + MachODumper Dumper(Obj, std::move(DCtx), RawSegments); Expected> YAML = Dumper.dump(); if (!YAML) return YAML.takeError(); @@ -602,7 +615,8 @@ return Error::success(); } -Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj) { +Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj, + unsigned RawSegments) { yaml::YamlObjectFile YAMLFile; YAMLFile.FatMachO.reset(new MachOYAML::UniversalBinary()); MachOYAML::UniversalBinary &YAML = *YAMLFile.FatMachO; @@ -624,7 +638,7 @@ return SliceObj.takeError(); std::unique_ptr DCtx = DWARFContext::create(*SliceObj.get()); - MachODumper Dumper(*SliceObj.get(), std::move(DCtx)); + MachODumper Dumper(*SliceObj.get(), std::move(DCtx), RawSegments); Expected> YAMLObj = Dumper.dump(); if (!YAMLObj) return YAMLObj.takeError(); @@ -636,12 +650,13 @@ return Error::success(); } -Error macho2yaml(raw_ostream &Out, const object::Binary &Binary) { +Error macho2yaml(raw_ostream &Out, const object::Binary &Binary, + unsigned RawSegments) { if (const auto *MachOObj = dyn_cast(&Binary)) - return macho2yaml(Out, *MachOObj); + return macho2yaml(Out, *MachOObj, RawSegments); if (const auto *MachOObj = dyn_cast(&Binary)) - return macho2yaml(Out, *MachOObj); + return macho2yaml(Out, *MachOObj, RawSegments); llvm_unreachable("unexpected Mach-O file format"); } Index: llvm/tools/obj2yaml/obj2yaml.h =================================================================== --- llvm/tools/obj2yaml/obj2yaml.h +++ llvm/tools/obj2yaml/obj2yaml.h @@ -20,12 +20,13 @@ #include "llvm/Support/MemoryBufferRef.h" #include +enum RawSegments : unsigned { none = 0, data = 1, linkedit = 1 << 1 }; std::error_code coff2yaml(llvm::raw_ostream &Out, const llvm::object::COFFObjectFile &Obj); llvm::Error elf2yaml(llvm::raw_ostream &Out, const llvm::object::ObjectFile &Obj); -llvm::Error macho2yaml(llvm::raw_ostream &Out, - const llvm::object::Binary &Obj); +llvm::Error macho2yaml(llvm::raw_ostream &Out, const llvm::object::Binary &Obj, + unsigned RawSegments); llvm::Error minidump2yaml(llvm::raw_ostream &Out, const llvm::object::MinidumpFile &Obj); llvm::Error xcoff2yaml(llvm::raw_ostream &Out, Index: llvm/tools/obj2yaml/obj2yaml.cpp =================================================================== --- llvm/tools/obj2yaml/obj2yaml.cpp +++ llvm/tools/obj2yaml/obj2yaml.cpp @@ -1,4 +1,4 @@ -//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===// +//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -18,6 +18,14 @@ using namespace llvm; using namespace llvm::object; +static cl::opt + InputFilename(cl::Positional, cl::desc(""), cl::init("-")); +static cl::bits RawSegment( + "raw-segment", + cl::desc("Mach-O: dump the raw contents of the listed segments instead of " + "parsing them:"), + cl::values(clEnumVal(data, "__DATA"), clEnumVal(linkedit, "__LINKEDIT"))); + static Error dumpObject(const ObjectFile &Obj) { if (Obj.isCOFF()) return errorCodeToError(coff2yaml(outs(), cast(Obj))); @@ -54,7 +62,7 @@ // Universal MachO is not a subclass of ObjectFile, so it needs to be handled // here with the other binary types. if (Binary.isMachO() || Binary.isMachOUniversalBinary()) - return macho2yaml(outs(), Binary); + return macho2yaml(outs(), Binary, RawSegment.getBits()); if (ObjectFile *Obj = dyn_cast(&Binary)) return dumpObject(*Obj); if (MinidumpFile *Minidump = dyn_cast(&Binary)) @@ -74,9 +82,6 @@ errs().flush(); } -cl::opt InputFilename(cl::Positional, cl::desc(""), - cl::init("-")); - int main(int argc, char *argv[]) { InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv);