[llvm] r273719 - [obj2yaml] [yaml2obj] Support for MachO Universal binaries

Chris Bieneman via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 24 13:42:29 PDT 2016


Author: cbieneman
Date: Fri Jun 24 15:42:28 2016
New Revision: 273719

URL: http://llvm.org/viewvc/llvm-project?rev=273719&view=rev
Log:
[obj2yaml] [yaml2obj] Support for MachO Universal binaries

This patch adds round-trip support for MachO Universal binaries to obj2yaml and yaml2obj. Universal binaries have a header and list of architecture structures, followed by a the individual object files at specified offsets.

Added:
    llvm/trunk/test/ObjectYAML/MachO/fat_macho_i386_x86_64.yaml
Modified:
    llvm/trunk/include/llvm/Object/MachOUniversal.h
    llvm/trunk/include/llvm/ObjectYAML/MachOYAML.h
    llvm/trunk/lib/ObjectYAML/MachOYAML.cpp
    llvm/trunk/tools/obj2yaml/macho2yaml.cpp
    llvm/trunk/tools/obj2yaml/obj2yaml.cpp
    llvm/trunk/tools/obj2yaml/obj2yaml.h
    llvm/trunk/tools/yaml2obj/yaml2macho.cpp

Modified: llvm/trunk/include/llvm/Object/MachOUniversal.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Object/MachOUniversal.h?rev=273719&r1=273718&r2=273719&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Object/MachOUniversal.h (original)
+++ llvm/trunk/include/llvm/Object/MachOUniversal.h Fri Jun 24 15:42:28 2016
@@ -84,6 +84,12 @@ public:
       else // Parent->getMagic() == MachO::FAT_MAGIC_64
         return Header64.align;
     }
+    uint32_t getReserved() const {
+      if (Parent->getMagic() == MachO::FAT_MAGIC)
+        return 0;
+      else // Parent->getMagic() == MachO::FAT_MAGIC_64
+        return Header64.align;
+    }
     std::string getArchTypeName() const {
       if (Parent->getMagic() == MachO::FAT_MAGIC) {
         Triple T =

Modified: llvm/trunk/include/llvm/ObjectYAML/MachOYAML.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ObjectYAML/MachOYAML.h?rev=273719&r1=273718&r2=273719&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ObjectYAML/MachOYAML.h (original)
+++ llvm/trunk/include/llvm/ObjectYAML/MachOYAML.h Fri Jun 24 15:42:28 2016
@@ -109,6 +109,32 @@ struct Object {
   LinkEditData LinkEdit;
 };
 
+struct FatHeader {
+  llvm::yaml::Hex32 magic;
+  uint32_t nfat_arch;
+};
+
+struct FatArch {
+  llvm::yaml::Hex32 cputype;
+  llvm::yaml::Hex32 cpusubtype;
+  llvm::yaml::Hex64 offset;
+  uint64_t size;
+  uint32_t align;
+  llvm::yaml::Hex32 reserved;
+};
+
+struct UniversalBinary {
+  FatHeader Header;
+  std::vector<FatArch> FatArchs;
+  std::vector<Object> Slices;
+};
+
+struct MachFile {
+  bool isFat;
+  UniversalBinary FatFile;
+  Object ThinFile;
+};
+
 } // namespace llvm::MachOYAML
 } // namespace llvm
 
@@ -122,6 +148,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachO
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachOYAML::ExportEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachOYAML::NListEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::StringRef)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachOYAML::Object)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachOYAML::FatArch)
 
 namespace llvm {
 namespace yaml {
@@ -134,6 +162,22 @@ template <> struct MappingTraits<MachOYA
   static void mapping(IO &IO, MachOYAML::Object &Object);
 };
 
+template <> struct MappingTraits<MachOYAML::FatHeader> {
+  static void mapping(IO &IO, MachOYAML::FatHeader &FatHeader);
+};
+
+template <> struct MappingTraits<MachOYAML::FatArch> {
+  static void mapping(IO &IO, MachOYAML::FatArch &FatArch);
+};
+
+template <> struct MappingTraits<MachOYAML::UniversalBinary> {
+  static void mapping(IO &IO, MachOYAML::UniversalBinary &UniversalBinary);
+};
+
+template <> struct MappingTraits<MachOYAML::MachFile> {
+  static void mapping(IO &IO, MachOYAML::MachFile &MachFile);
+};
+
 template <> struct MappingTraits<MachOYAML::LoadCommand> {
   static void mapping(IO &IO, MachOYAML::LoadCommand &LoadCommand);
 };

Modified: llvm/trunk/lib/ObjectYAML/MachOYAML.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ObjectYAML/MachOYAML.cpp?rev=273719&r1=273718&r2=273719&view=diff
==============================================================================
--- llvm/trunk/lib/ObjectYAML/MachOYAML.cpp (original)
+++ llvm/trunk/lib/ObjectYAML/MachOYAML.cpp Fri Jun 24 15:42:28 2016
@@ -96,7 +96,68 @@ void MappingTraits<MachOYAML::Object>::m
   IO.mapRequired("FileHeader", Object.Header);
   IO.mapOptional("LoadCommands", Object.LoadCommands);
   IO.mapOptional("LinkEditData", Object.LinkEdit);
-  IO.setContext(nullptr);
+
+  if (IO.getContext() == &Object)
+    IO.setContext(nullptr);
+}
+
+void MappingTraits<MachOYAML::FatHeader>::mapping(
+    IO &IO, MachOYAML::FatHeader &FatHeader) {
+  IO.mapRequired("magic", FatHeader.magic);
+  IO.mapRequired("nfat_arch", FatHeader.nfat_arch);
+}
+
+void MappingTraits<MachOYAML::FatArch>::mapping(IO &IO,
+                                                MachOYAML::FatArch &FatArch) {
+  IO.mapRequired("cputype", FatArch.cputype);
+  IO.mapRequired("cpusubtype", FatArch.cpusubtype);
+  IO.mapRequired("offset", FatArch.offset);
+  IO.mapRequired("size", FatArch.size);
+  IO.mapRequired("align", FatArch.align);
+  IO.mapOptional("reserved", FatArch.reserved,
+                 static_cast<llvm::yaml::Hex32>(0));
+}
+
+void MappingTraits<MachOYAML::UniversalBinary>::mapping(
+    IO &IO, MachOYAML::UniversalBinary &UniversalBinary) {
+  if (!IO.getContext()) {
+    IO.setContext(&UniversalBinary);
+    IO.mapTag("!fat-mach-o", true);
+  }
+  IO.mapRequired("FatHeader", UniversalBinary.Header);
+  IO.mapRequired("FatArchs", UniversalBinary.FatArchs);
+  IO.mapRequired("Slices", UniversalBinary.Slices);
+
+  if (IO.getContext() == &UniversalBinary)
+    IO.setContext(nullptr);
+}
+
+void MappingTraits<MachOYAML::MachFile>::mapping(
+    IO &IO, MachOYAML::MachFile &MachFile) {
+  if (!IO.getContext()) {
+    IO.setContext(&MachFile);
+  }
+  if (IO.outputting()) {
+    if (MachFile.isFat) {
+      IO.mapTag("!fat-mach-o", true);
+      MappingTraits<MachOYAML::UniversalBinary>::mapping(IO, MachFile.FatFile);
+    } else {
+      IO.mapTag("!mach-o", true);
+      MappingTraits<MachOYAML::Object>::mapping(IO, MachFile.ThinFile);
+    }
+  } else {
+    if (IO.mapTag("!fat-mach-o")) {
+      MachFile.isFat = true;
+      MappingTraits<MachOYAML::UniversalBinary>::mapping(IO, MachFile.FatFile);
+    } else if (IO.mapTag("!mach-o")) {
+      MachFile.isFat = false;
+      MappingTraits<MachOYAML::Object>::mapping(IO, MachFile.ThinFile);
+    } else {
+      assert(false && "No tag found in YAML, cannot identify file type!");
+    }
+  }
+  if (IO.getContext() == &MachFile)
+    IO.setContext(nullptr);
 }
 
 void MappingTraits<MachOYAML::LinkEditData>::mapping(

Added: llvm/trunk/test/ObjectYAML/MachO/fat_macho_i386_x86_64.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/ObjectYAML/MachO/fat_macho_i386_x86_64.yaml?rev=273719&view=auto
==============================================================================
--- llvm/trunk/test/ObjectYAML/MachO/fat_macho_i386_x86_64.yaml (added)
+++ llvm/trunk/test/ObjectYAML/MachO/fat_macho_i386_x86_64.yaml Fri Jun 24 15:42:28 2016
@@ -0,0 +1,72 @@
+# RUN: yaml2obj -format=macho %s | obj2yaml | FileCheck %s
+
+--- !fat-mach-o
+FatHeader:       
+  magic:           0xCAFEBABE
+  nfat_arch:       2
+FatArchs:        
+  - cputype:         0x00000007
+    cpusubtype:      0x00000003
+    offset:          0x0000000000001000
+    size:            15244
+    align:           12
+  - cputype:         0x01000007
+    cpusubtype:      0x80000003
+    offset:          0x0000000000005000
+    size:            15380
+    align:           12
+Slices:          
+  - FileHeader:      
+      magic:           0xFEEDFACE
+      cputype:         0x00000007
+      cpusubtype:      0x00000003
+      filetype:        0x00000002
+      ncmds:           0
+      sizeofcmds:      0
+      flags:           0x01218085
+  - FileHeader:      
+      magic:           0xFEEDFACF
+      cputype:         0x01000007
+      cpusubtype:      0x80000003
+      filetype:        0x00000002
+      ncmds:           0
+      sizeofcmds:      0
+      flags:           0x00218085
+      reserved:        0x00000000
+...
+
+
+#CHECK: --- !fat-mach-o
+#CHECK: FatHeader:       
+#CHECK:   magic:           0xCAFEBABE
+#CHECK:   nfat_arch:       2
+#CHECK: FatArchs:        
+#CHECK:   - cputype:         0x00000007
+#CHECK:     cpusubtype:      0x00000003
+#CHECK:     offset:          0x0000000000001000
+#CHECK:     size:            15244
+#CHECK:     align:           12
+#CHECK:   - cputype:         0x01000007
+#CHECK:     cpusubtype:      0x80000003
+#CHECK:     offset:          0x0000000000005000
+#CHECK:     size:            15380
+#CHECK:     align:           12
+#CHECK: Slices:          
+#CHECK:   - FileHeader:      
+#CHECK:       magic:           0xFEEDFACE
+#CHECK:       cputype:         0x00000007
+#CHECK:       cpusubtype:      0x00000003
+#CHECK:       filetype:        0x00000002
+#CHECK:       ncmds:           0
+#CHECK:       sizeofcmds:      0
+#CHECK:       flags:           0x01218085
+#CHECK:   - FileHeader:      
+#CHECK:       magic:           0xFEEDFACF
+#CHECK:       cputype:         0x01000007
+#CHECK:       cpusubtype:      0x80000003
+#CHECK:       filetype:        0x00000002
+#CHECK:       ncmds:           0
+#CHECK:       sizeofcmds:      0
+#CHECK:       flags:           0x00218085
+#CHECK:       reserved:        0x00000000
+#CHECK: ...

Modified: llvm/trunk/tools/obj2yaml/macho2yaml.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/obj2yaml/macho2yaml.cpp?rev=273719&r1=273718&r2=273719&view=diff
==============================================================================
--- llvm/trunk/tools/obj2yaml/macho2yaml.cpp (original)
+++ llvm/trunk/tools/obj2yaml/macho2yaml.cpp Fri Jun 24 15:42:28 2016
@@ -473,23 +473,52 @@ Error macho2yaml(raw_ostream &Out, const
 }
 
 Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj) {
-  return make_error<Obj2YamlError>(obj2yaml_error::not_implemented);
+  MachOYAML::MachFile YAMLFile;
+  YAMLFile.isFat = true;
+  MachOYAML::UniversalBinary &YAML = YAMLFile.FatFile;
+  YAML.Header.magic = Obj.getMagic();
+  YAML.Header.nfat_arch = Obj.getNumberOfObjects();
+
+  for (auto Slice : Obj.objects()) {
+    MachOYAML::FatArch arch;
+    arch.cputype = Slice.getCPUType();
+    arch.cpusubtype = Slice.getCPUSubType();
+    arch.offset = Slice.getOffset();
+    arch.size = Slice.getSize();
+    arch.align = Slice.getAlign();
+    arch.reserved = Slice.getReserved();
+    YAML.FatArchs.push_back(arch);
+
+    auto SliceObj = Slice.getAsObjectFile();
+    if (!SliceObj)
+      return SliceObj.takeError();
+
+    MachODumper Dumper(*SliceObj.get());
+    Expected<std::unique_ptr<MachOYAML::Object>> YAMLObj = Dumper.dump();
+    if (!YAMLObj)
+      return YAMLObj.takeError();
+    YAML.Slices.push_back(*YAMLObj.get());
+  }
+
+  yaml::Output Yout(Out);
+  Yout << YAML;
+  return Error::success();
 }
 
-std::error_code macho2yaml(raw_ostream &Out, const object::ObjectFile &Obj) {
-  if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Obj)) {
+std::error_code macho2yaml(raw_ostream &Out, const object::Binary &Binary) {
+  if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Binary)) {
     if (auto Err = macho2yaml(Out, *MachOObj)) {
       return errorToErrorCode(std::move(Err));
     }
     return obj2yaml_error::success;
   }
 
-  if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj)) {
+  if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Binary)) {
     if (auto Err = macho2yaml(Out, *MachOObj)) {
       return errorToErrorCode(std::move(Err));
     }
     return obj2yaml_error::success;
   }
-
+  
   return obj2yaml_error::unsupported_obj_file_format;
 }

Modified: llvm/trunk/tools/obj2yaml/obj2yaml.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/obj2yaml/obj2yaml.cpp?rev=273719&r1=273718&r2=273719&view=diff
==============================================================================
--- llvm/trunk/tools/obj2yaml/obj2yaml.cpp (original)
+++ llvm/trunk/tools/obj2yaml/obj2yaml.cpp Fri Jun 24 15:42:28 2016
@@ -24,8 +24,6 @@ static std::error_code dumpObject(const
     return coff2yaml(outs(), cast<COFFObjectFile>(Obj));
   if (Obj.isELF())
     return elf2yaml(outs(), Obj);
-  if (Obj.isMachO() || Obj.isMachOUniversalBinary())
-    return macho2yaml(outs(), Obj);
 
   return obj2yaml_error::unsupported_obj_file_format;
 }
@@ -36,6 +34,10 @@ static std::error_code dumpInput(StringR
     return errorToErrorCode(BinaryOrErr.takeError());
 
   Binary &Binary = *BinaryOrErr.get().getBinary();
+  // 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);
   // TODO: If this is an archive, then burst it and dump each entry
   if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary))
     return dumpObject(*Obj);

Modified: llvm/trunk/tools/obj2yaml/obj2yaml.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/obj2yaml/obj2yaml.h?rev=273719&r1=273718&r2=273719&view=diff
==============================================================================
--- llvm/trunk/tools/obj2yaml/obj2yaml.h (original)
+++ llvm/trunk/tools/obj2yaml/obj2yaml.h Fri Jun 24 15:42:28 2016
@@ -22,6 +22,6 @@ std::error_code coff2yaml(llvm::raw_ostr
 std::error_code elf2yaml(llvm::raw_ostream &Out,
                          const llvm::object::ObjectFile &Obj);
 std::error_code macho2yaml(llvm::raw_ostream &Out,
-                         const llvm::object::ObjectFile &Obj);
+                           const llvm::object::Binary &Obj);
 
 #endif

Modified: llvm/trunk/tools/yaml2obj/yaml2macho.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/yaml2obj/yaml2macho.cpp?rev=273719&r1=273718&r2=273719&view=diff
==============================================================================
--- llvm/trunk/tools/yaml2obj/yaml2macho.cpp (original)
+++ llvm/trunk/tools/yaml2obj/yaml2macho.cpp Fri Jun 24 15:42:28 2016
@@ -424,17 +424,120 @@ Error MachOWriter::writeStringTable(raw_
   return Error::success();
 }
 
+class UniversalWriter {
+public:
+  UniversalWriter(MachOYAML::MachFile &MachFile)
+      : MachFile(MachFile), fileStart(0) {}
+
+  Error writeMachO(raw_ostream &OS);
+
+private:
+  Error writeFatHeader(raw_ostream &OS);
+  Error writeFatArchs(raw_ostream &OS);
+
+  void ZeroToOffset(raw_ostream &OS, size_t offset);
+
+  MachOYAML::MachFile &MachFile;
+  uint64_t fileStart;
+};
+
+Error UniversalWriter::writeMachO(raw_ostream &OS) {
+  fileStart = OS.tell();
+  if (!MachFile.isFat) {
+    MachOWriter Writer(MachFile.ThinFile);
+    return Writer.writeMachO(OS);
+  }
+  if (auto Err = writeFatHeader(OS))
+    return Err;
+  if (auto Err = writeFatArchs(OS))
+    return Err;
+  auto &FatFile = MachFile.FatFile;
+  assert(FatFile.FatArchs.size() == FatFile.Slices.size());
+  for (size_t i = 0; i < FatFile.Slices.size(); i++) {
+    ZeroToOffset(OS, FatFile.FatArchs[i].offset);
+    MachOWriter Writer(FatFile.Slices[i]);
+    if (auto Err = Writer.writeMachO(OS))
+      return Err;
+    auto SliceEnd = FatFile.FatArchs[i].offset + FatFile.FatArchs[i].size;
+    ZeroToOffset(OS, SliceEnd);
+  }
+  return Error::success();
+}
+
+Error UniversalWriter::writeFatHeader(raw_ostream &OS) {
+  auto &FatFile = MachFile.FatFile;
+  MachO::fat_header header;
+  header.magic = FatFile.Header.magic;
+  header.nfat_arch = FatFile.Header.nfat_arch;
+  if (sys::IsLittleEndianHost)
+    swapStruct(header);
+  OS.write(reinterpret_cast<const char *>(&header), sizeof(MachO::fat_header));
+  return Error::success();
+}
+
+template <typename FatArchType>
+FatArchType constructFatArch(MachOYAML::FatArch &Arch) {
+  FatArchType FatArch;
+  FatArch.cputype = Arch.cputype;
+  FatArch.cpusubtype = Arch.cpusubtype;
+  FatArch.offset = Arch.offset;
+  FatArch.size = Arch.size;
+  FatArch.align = Arch.align;
+  return FatArch;
+}
+
+template <typename StructType>
+void writeFatArch(MachOYAML::FatArch &LC, raw_ostream &OS) {}
+
+template <>
+void writeFatArch<MachO::fat_arch>(MachOYAML::FatArch &Arch, raw_ostream &OS) {
+  auto FatArch = constructFatArch<MachO::fat_arch>(Arch);
+  if (sys::IsLittleEndianHost)
+    swapStruct(FatArch);
+  OS.write(reinterpret_cast<const char *>(&FatArch), sizeof(MachO::fat_arch));
+}
+
+template <>
+void writeFatArch<MachO::fat_arch_64>(MachOYAML::FatArch &Arch,
+                                      raw_ostream &OS) {
+  auto FatArch = constructFatArch<MachO::fat_arch_64>(Arch);
+  FatArch.reserved = Arch.reserved;
+  if (sys::IsLittleEndianHost)
+    swapStruct(FatArch);
+  OS.write(reinterpret_cast<const char *>(&FatArch),
+           sizeof(MachO::fat_arch_64));
+}
+
+Error UniversalWriter::writeFatArchs(raw_ostream &OS) {
+  auto &FatFile = MachFile.FatFile;
+  bool is64Bit = FatFile.Header.magic == MachO::FAT_MAGIC_64;
+  for (auto Arch : FatFile.FatArchs) {
+    if (is64Bit)
+      writeFatArch<MachO::fat_arch_64>(Arch, OS);
+    else
+      writeFatArch<MachO::fat_arch>(Arch, OS);
+  }
+
+  return Error::success();
+}
+
+void UniversalWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) {
+  auto currOffset = OS.tell() - fileStart;
+  if (currOffset < Offset)
+    ZeroFillBytes(OS, Offset - currOffset);
+}
+
 } // end anonymous namespace
 
 int yaml2macho(yaml::Input &YIn, raw_ostream &Out) {
-  MachOYAML::Object Doc;
+  MachOYAML::MachFile Doc;
   YIn >> Doc;
   if (YIn.error()) {
     errs() << "yaml2obj: Failed to parse YAML file!\n";
     return 1;
   }
 
-  MachOWriter Writer(Doc);
+  UniversalWriter Writer(Doc);
   if (auto Err = Writer.writeMachO(Out)) {
     errs() << toString(std::move(Err));
     return 1;




More information about the llvm-commits mailing list