OSDN Git Service

Add LC_BUILD_VERSION load command
authorSteven Wu <stevenwu@apple.com>
Mon, 23 Jan 2017 20:07:55 +0000 (20:07 +0000)
committerSteven Wu <stevenwu@apple.com>
Mon, 23 Jan 2017 20:07:55 +0000 (20:07 +0000)
Summary:
Add a new load command LC_BUILD_VERSION. It is a generic version of
LC_*_VERSION_MIN load_command used on Apple platforms. Instead of having
a seperate load command for each platform, LC_BUILD_VERSION is recording
platform info as an enum. It also records SDK version, min_os, and tools
that used to build the binary.

rdar://problem/29781291

Reviewers: enderby

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D29044

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@292824 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
include/llvm/Object/MachO.h
include/llvm/ObjectYAML/MachOYAML.h
include/llvm/Support/MachO.def
include/llvm/Support/MachO.h
lib/Object/MachOObjectFile.cpp
lib/ObjectYAML/MachOYAML.cpp
test/MC/MachO/darwin-version-min-load-command.s
test/ObjectYAML/MachO/build_version_command.yaml [new file with mode: 0644]
test/tools/llvm-objdump/X86/invalid-macho-build-version.yaml [new file with mode: 0644]
test/tools/llvm-objdump/X86/macho-build-version.yaml [new file with mode: 0644]
tools/llvm-objdump/MachODump.cpp
tools/llvm-readobj/MachODumper.cpp
tools/obj2yaml/macho2yaml.cpp
tools/yaml2obj/yaml2macho.cpp

index b9ff2e2..b91e8c3 100644 (file)
@@ -353,6 +353,10 @@ public:
   getVersionMinLoadCommand(const LoadCommandInfo &L) const;
   MachO::note_command
   getNoteLoadCommand(const LoadCommandInfo &L) const;
+  MachO::build_version_command
+  getBuildVersionLoadCommand(const LoadCommandInfo &L) const;
+  MachO::build_tool_version
+  getBuildToolVersion(unsigned index) const;
   MachO::dylib_command
   getDylibIDLoadCommand(const LoadCommandInfo &L) const;
   MachO::dyld_info_command
@@ -446,6 +450,46 @@ public:
     return VersionOrSDK & 0xff;
   }
 
+  static std::string getBuildPlatform(uint32_t platform) {
+    switch (platform) {
+    case MachO::PLATFORM_MACOS: return "macos";
+    case MachO::PLATFORM_IOS: return "ios";
+    case MachO::PLATFORM_TVOS: return "tvos";
+    case MachO::PLATFORM_WATCHOS: return "watchos";
+    case MachO::PLATFORM_BRIDGEOS: return "bridgeos";
+    default:
+      std::string ret;
+      llvm::raw_string_ostream ss(ret);
+      ss << format_hex(platform, 8, true);
+      return ss.str();
+    }
+  }
+
+  static std::string getBuildTool(uint32_t tools) {
+    switch (tools) {
+    case MachO::TOOL_CLANG: return "clang";
+    case MachO::TOOL_SWIFT: return "swift";
+    case MachO::TOOL_LD: return "ld";
+    default:
+      std::string ret;
+      llvm::raw_string_ostream ss(ret);
+      ss << format_hex(tools, 8, true);
+      return ss.str();
+    }
+  }
+
+  static std::string getVersionString(uint32_t version) {
+    uint32_t major = (version >> 16) & 0xffff;
+    uint32_t minor = (version >> 8) & 0xff;
+    uint32_t update = version & 0xff;
+
+    SmallString<32> Version;
+    Version = utostr(major) + "." + utostr(minor);
+    if (update != 0)
+      Version += "." + utostr(update);
+    return Version.str();
+  }
+
 private:
 
   MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, bool Is64Bits,
@@ -464,6 +508,8 @@ private:
   LibraryList Libraries;
   LoadCommandList LoadCommands;
   typedef SmallVector<StringRef, 1> LibraryShortName;
+  using BuildToolList = SmallVector<const char*, 1>;
+  BuildToolList BuildTools;
   mutable LibraryShortName LibrariesShortNames;
   const char *SymtabLoadCmd;
   const char *DysymtabLoadCmd;
index 9ec32d2..f69aa15 100644 (file)
@@ -53,6 +53,7 @@ struct LoadCommand {
   virtual ~LoadCommand();
   llvm::MachO::macho_load_command Data;
   std::vector<Section> Sections;
+  std::vector<MachO::build_tool_version> Tools;
   std::vector<llvm::yaml::Hex8> PayloadBytes;
   std::string PayloadString;
   uint64_t ZeroPadBytes;
@@ -146,6 +147,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachOYAML::ExportEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachOYAML::NListEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachOYAML::Object)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachOYAML::FatArch)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::MachO::build_tool_version)
 
 namespace llvm {
 namespace yaml {
@@ -198,6 +200,10 @@ template <> struct MappingTraits<MachOYAML::NListEntry> {
   static void mapping(IO &IO, MachOYAML::NListEntry &NListEntry);
 };
 
+template <> struct MappingTraits<MachO::build_tool_version> {
+  static void mapping(IO &IO, MachO::build_tool_version &tool);
+};
+
 #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct)                         \
   io.enumCase(value, #LCName, MachO::LCName);
 
index f395d82..95de48d 100644 (file)
@@ -74,6 +74,7 @@ HANDLE_LOAD_COMMAND(LC_LINKER_OPTIMIZATION_HINT, 0x0000002Eu, linkedit_data_comm
 HANDLE_LOAD_COMMAND(LC_VERSION_MIN_TVOS, 0x0000002Fu, version_min_command)
 HANDLE_LOAD_COMMAND(LC_VERSION_MIN_WATCHOS, 0x00000030u, version_min_command)
 HANDLE_LOAD_COMMAND(LC_NOTE, 0x00000031u, note_command)
+HANDLE_LOAD_COMMAND(LC_BUILD_VERSION, 0x00000032u, build_version_command)
 
 #endif
 
@@ -111,6 +112,7 @@ LOAD_COMMAND_STRUCT(twolevel_hints_command)
 LOAD_COMMAND_STRUCT(uuid_command)
 LOAD_COMMAND_STRUCT(version_min_command)
 LOAD_COMMAND_STRUCT(note_command)
+LOAD_COMMAND_STRUCT(build_version_command)
 
 #endif
 
index ab037c8..88483aa 100644 (file)
@@ -487,6 +487,22 @@ namespace llvm {
       VM_PROT_EXECUTE = 0x4
     };
 
+    // Values for platform field in build_version_command.
+    enum {
+      PLATFORM_MACOS    = 1,
+      PLATFORM_IOS      = 2,
+      PLATFORM_TVOS     = 3,
+      PLATFORM_WATCHOS  = 4,
+      PLATFORM_BRIDGEOS = 5
+    };
+
+    // Values for tools enum in build_tool_version.
+    enum {
+      TOOL_CLANG  = 1,
+      TOOL_SWIFT  = 2,
+      TOOL_LD     = 3
+    };
+
     // Structs from <mach-o/loader.h>
 
     struct mach_header {
@@ -827,6 +843,21 @@ namespace llvm {
       uint64_t size;       // length of data region
     };
 
+    struct build_tool_version {
+      uint32_t tool;      // enum for the tool
+      uint32_t version;   // version of the tool
+    };
+
+    struct build_version_command {
+      uint32_t cmd;       // LC_BUILD_VERSION
+      uint32_t cmdsize;   // sizeof(struct build_version_command) +
+                          // ntools * sizeof(struct build_tool_version)
+      uint32_t platform;  // platform
+      uint32_t minos;     // X.Y.Z is encoded in nibbles xxxx.yy.zz
+      uint32_t sdk;       // X.Y.Z is encoded in nibbles xxxx.yy.zz
+      uint32_t ntools;    // number of tool entries following this
+    };
+
     struct dyld_info_command {
       uint32_t cmd;
       uint32_t cmdsize;
@@ -1281,6 +1312,20 @@ namespace llvm {
       sys::swapByteOrder(C.size);
     }
 
+    inline void swapStruct(build_version_command&C) {
+      sys::swapByteOrder(C.cmd);
+      sys::swapByteOrder(C.cmdsize);
+      sys::swapByteOrder(C.platform);
+      sys::swapByteOrder(C.minos);
+      sys::swapByteOrder(C.sdk);
+      sys::swapByteOrder(C.ntools);
+    }
+
+    inline void swapStruct(build_tool_version&C) {
+      sys::swapByteOrder(C.tool);
+      sys::swapByteOrder(C.version);
+    }
+
     inline void swapStruct(data_in_code_entry &C) {
       sys::swapByteOrder(C.offset);
       sys::swapByteOrder(C.length);
index 9a1e368..f35a3e3 100644 (file)
@@ -809,6 +809,27 @@ static Error checkNoteCommand(const MachOObjectFile &Obj,
   return Error::success();
 }
 
+static Error
+parseBuildVersionCommand(const MachOObjectFile &Obj,
+                         const MachOObjectFile::LoadCommandInfo &Load,
+                         SmallVectorImpl<const char*> &BuildTools,
+                         uint32_t LoadCommandIndex) {
+  MachO::build_version_command BVC =
+      getStruct<MachO::build_version_command>(Obj, Load.Ptr);
+  if (Load.C.cmdsize !=
+      sizeof(MachO::build_version_command) +
+          BVC.ntools * sizeof(MachO::build_tool_version))
+    return malformedError("load command " + Twine(LoadCommandIndex) +
+                          " LC_BUILD_VERSION_COMMAND has incorrect cmdsize");
+
+  auto Start = Load.Ptr + sizeof(MachO::build_version_command);
+  BuildTools.resize(BVC.ntools);
+  for (unsigned i = 0; i < BVC.ntools; ++i)
+    BuildTools[i] = Start + i * sizeof(MachO::build_tool_version);
+
+  return Error::success();
+}
+
 static Error checkRpathCommand(const MachOObjectFile &Obj,
                                const MachOObjectFile::LoadCommandInfo &Load,
                                uint32_t LoadCommandIndex) {
@@ -1308,6 +1329,9 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
     } else if (Load.C.cmd == MachO::LC_NOTE) {
       if ((Err = checkNoteCommand(*this, Load, I, Elements)))
         return;
+    } else if (Load.C.cmd == MachO::LC_BUILD_VERSION) {
+      if ((Err = parseBuildVersionCommand(*this, Load, BuildTools, I)))
+        return;
     } else if (Load.C.cmd == MachO::LC_RPATH) {
       if ((Err = checkRpathCommand(*this, Load, I)))
         return;
@@ -3322,6 +3346,16 @@ MachOObjectFile::getNoteLoadCommand(const LoadCommandInfo &L) const {
   return getStruct<MachO::note_command>(*this, L.Ptr);
 }
 
+MachO::build_version_command
+MachOObjectFile::getBuildVersionLoadCommand(const LoadCommandInfo &L) const {
+  return getStruct<MachO::build_version_command>(*this, L.Ptr);
+}
+
+MachO::build_tool_version
+MachOObjectFile::getBuildToolVersion(unsigned index) const {
+  return getStruct<MachO::build_tool_version>(*this, BuildTools[index]);
+}
+
 MachO::dylib_command
 MachOObjectFile::getDylibIDLoadCommand(const LoadCommandInfo &L) const {
   return getStruct<MachO::dylib_command>(*this, L.Ptr);
index f049563..6b0e4e3 100644 (file)
@@ -230,6 +230,12 @@ void mapLoadCommandData<MachO::dylinker_command>(
   IO.mapOptional("PayloadString", LoadCommand.PayloadString);
 }
 
+template <>
+void mapLoadCommandData<MachO::build_version_command>(
+    IO &IO, MachOYAML::LoadCommand &LoadCommand) {
+  IO.mapOptional("Tools", LoadCommand.Tools);
+}
+
 void MappingTraits<MachOYAML::LoadCommand>::mapping(
     IO &IO, MachOYAML::LoadCommand &LoadCommand) {
   MachO::LoadCommandType TempCmd = static_cast<MachO::LoadCommandType>(
@@ -282,6 +288,12 @@ void MappingTraits<MachOYAML::Section>::mapping(IO &IO,
   IO.mapOptional("reserved3", Section.reserved3);
 }
 
+void MappingTraits<MachO::build_tool_version>::mapping(
+    IO &IO, MachO::build_tool_version &tool) {
+  IO.mapRequired("tool", tool.tool);
+  IO.mapRequired("version", tool.version);
+}
+
 void MappingTraits<MachO::dylib>::mapping(IO &IO, MachO::dylib &DylibStruct) {
   IO.mapRequired("name", DylibStruct.name);
   IO.mapRequired("timestamp", DylibStruct.timestamp);
@@ -566,6 +578,15 @@ void MappingTraits<MachO::note_command>::mapping(
   IO.mapRequired("size", LoadCommand.size);
 }
 
+void MappingTraits<MachO::build_version_command>::mapping(
+    IO &IO, MachO::build_version_command &LoadCommand) {
+
+  IO.mapRequired("platform", LoadCommand.platform);
+  IO.mapRequired("minos", LoadCommand.minos);
+  IO.mapRequired("sdk", LoadCommand.sdk);
+  IO.mapRequired("ntools", LoadCommand.ntools);
+}
+
 } // namespace llvm::yaml
 
 } // namespace llvm
index 17f3784..7fd4daa 100644 (file)
 // CHECK-TVOS:            cmd LC_VERSION_MIN_TVOS
 // CHECK-TVOS-NEXT:   cmdsize 16
 // CHECK-TVOS-NEXT:   version 8.0
+
+// CHECK-BRIDGEOS:            cmd LC_BUILD_VERSION
+// CHECK-BRIDGEOS-NEXT:   cmdsize 24
+// CHECK-BRIDGEOS-NEXT:  platform bridgeos
+// CHECK-BRIDGEOS-NEXT:       sdk n/a
+// CHECK-BRIDGEOS-NEXT:     minos 2.0
+// CHECK-BRIDGEOS-NEXT:    ntools 0
diff --git a/test/ObjectYAML/MachO/build_version_command.yaml b/test/ObjectYAML/MachO/build_version_command.yaml
new file mode 100644 (file)
index 0000000..4dc2df2
--- /dev/null
@@ -0,0 +1,58 @@
+# RUN: yaml2obj %s | obj2yaml | FileCheck %s
+
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACE
+  cputype:         0x00000007
+  cpusubtype:      0x00000003
+  filetype:        0x00000004
+  ncmds:           2
+  sizeofcmds:      192
+  flags:           0x00000000
+LoadCommands:
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         152
+    segname:         __TEXT
+    vmaddr:          4294967296
+    vmsize:          8192
+    fileoff:         0
+    filesize:        3099
+    maxprot:         7
+    initprot:        5
+    nsects:          1
+    flags:           0
+    Sections:
+      - sectname:        __text
+        segname:         __TEXT
+        addr:            0x0000000100001160
+        size:            3099
+        offset:          0x00001160
+        align:           4
+        reloff:          0x00000000
+        nreloc:          0
+        flags:           0x80000400
+        reserved1:       0x00000000
+        reserved2:       0x00000000
+        reserved3:       0x00000000
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         32
+    platform:        2
+    minos:           0x00080000
+    sdk:             0x00090000
+    ntools:          1
+    Tools:
+      - tool:        1
+        version:     0x00000000
+...
+
+
+CHECK: LoadCommands:
+CHECK:   - cmd:             LC_BUILD_VERSION
+CHECK-NEXT:    cmdsize:         32
+CHECK-NEXT:    platform:        2
+CHECK-NEXT:    minos:           524288
+CHECK-NEXT:    sdk:             589824
+CHECK-NEXT:    ntools:          1
+CHECK-NEXT:    Tools:
+CHECK-NEXT:      - tool:            1
+CHECK-NEXT:        version:         0
diff --git a/test/tools/llvm-objdump/X86/invalid-macho-build-version.yaml b/test/tools/llvm-objdump/X86/invalid-macho-build-version.yaml
new file mode 100644 (file)
index 0000000..a81bb2d
--- /dev/null
@@ -0,0 +1,44 @@
+# RUN: yaml2obj %s | not llvm-objdump -macho -private-headers -
+
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x01000007
+  cpusubtype:      0x00000003
+  filetype:        0x00000004
+  ncmds:           2
+  sizeofcmds:      192
+  flags:           0x00000000
+  reserved:        0
+LoadCommands:
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         152
+    segname:         __TEXT
+    vmaddr:          4294967296
+    vmsize:          8192
+    fileoff:         0
+    filesize:        3099
+    maxprot:         7
+    initprot:        5
+    nsects:          1
+    flags:           0
+    Sections:
+      - sectname:        __text
+        segname:         __TEXT
+        addr:            0x0000000100001160
+        size:            3099
+        offset:          0x00001160
+        align:           4
+        reloff:          0x00000000
+        nreloc:          0
+        flags:           0x80000400
+        reserved1:       0x00000000
+        reserved2:       0x00000000
+        reserved3:       0x00000000
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         80
+    platform:        2
+    minos:           0x00080000
+    sdk:             0x00090000
+    ntools:          0
+...
diff --git a/test/tools/llvm-objdump/X86/macho-build-version.yaml b/test/tools/llvm-objdump/X86/macho-build-version.yaml
new file mode 100644 (file)
index 0000000..acefb69
--- /dev/null
@@ -0,0 +1,57 @@
+# RUN: yaml2obj %s | llvm-objdump -macho -private-headers - | FileCheck %s
+
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x01000007
+  cpusubtype:      0x00000003
+  filetype:        0x00000004
+  ncmds:           2
+  sizeofcmds:      192
+  flags:           0x00000000
+  reserved:        0
+LoadCommands:
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         152
+    segname:         __TEXT
+    vmaddr:          4294967296
+    vmsize:          8192
+    fileoff:         0
+    filesize:        3099
+    maxprot:         7
+    initprot:        5
+    nsects:          1
+    flags:           0
+    Sections:
+      - sectname:        __text
+        segname:         __TEXT
+        addr:            0x0000000100001160
+        size:            3099
+        offset:          0x00001160
+        align:           4
+        reloff:          0x00000000
+        nreloc:          0
+        flags:           0x80000400
+        reserved1:       0x00000000
+        reserved2:       0x00000000
+        reserved3:       0x00000000
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         32
+    platform:        2
+    minos:           0x00080000
+    sdk:             0x00090000
+    ntools:          1
+    Tools:
+      - tool:        1
+        version:     0x00000000
+...
+
+CHECK: Load command 1
+CHECK-NEXT:       cmd LC_BUILD_VERSION
+CHECK-NEXT:   cmdsize 32
+CHECK-NEXT:  platform ios
+CHECK-NEXT:       sdk 9.0
+CHECK-NEXT:     minos 8.0
+CHECK-NEXT:    ntools 1
+CHECK-NEXT:      tool clang
+CHECK-NEXT:   version n/a
index 6f810f4..faace1d 100644 (file)
@@ -8182,6 +8182,38 @@ static void PrintNoteLoadCommand(MachO::note_command Nt) {
   outs() << "      size " << Nt.size << "\n";
 }
 
+static void PrintBuildToolVersion(MachO::build_tool_version bv) {
+  outs() << "      tool " << MachOObjectFile::getBuildTool(bv.tool) << "\n";
+  outs() << "   version " << MachOObjectFile::getVersionString(bv.version)
+         << "\n";
+}
+
+static void PrintBuildVersionLoadCommand(const MachOObjectFile *obj,
+                                         MachO::build_version_command bd) {
+  outs() << "       cmd LC_BUILD_VERSION\n";
+  outs() << "   cmdsize " << bd.cmdsize;
+  if (bd.cmdsize !=
+      sizeof(struct MachO::build_version_command) +
+          bd.ntools * sizeof(struct MachO::build_tool_version))
+    outs() << " Incorrect size\n";
+  else
+    outs() << "\n";
+  outs() << "  platform " << MachOObjectFile::getBuildPlatform(bd.platform)
+         << "\n";
+  if (bd.sdk)
+    outs() << "       sdk " << MachOObjectFile::getVersionString(bd.sdk)
+           << "\n";
+  else
+    outs() << "       sdk n/a\n";
+  outs() << "     minos " << MachOObjectFile::getVersionString(bd.minos)
+         << "\n";
+  outs() << "    ntools " << bd.ntools << "\n";
+  for (unsigned i = 0; i < bd.ntools; ++i) {
+    MachO::build_tool_version bv = obj->getBuildToolVersion(i);
+    PrintBuildToolVersion(bv);
+  }
+}
+
 static void PrintSourceVersionCommand(MachO::source_version_command sd) {
   outs() << "      cmd LC_SOURCE_VERSION\n";
   outs() << "  cmdsize " << sd.cmdsize;
@@ -9030,6 +9062,10 @@ static void PrintLoadCommands(const MachOObjectFile *Obj, uint32_t filetype,
     } else if (Command.C.cmd == MachO::LC_NOTE) {
       MachO::note_command Nt = Obj->getNoteLoadCommand(Command);
       PrintNoteLoadCommand(Nt);
+    } else if (Command.C.cmd == MachO::LC_BUILD_VERSION) {
+      MachO::build_version_command Bv =
+          Obj->getBuildVersionLoadCommand(Command);
+      PrintBuildVersionLoadCommand(Obj, Bv);
     } else if (Command.C.cmd == MachO::LC_SOURCE_VERSION) {
       MachO::source_version_command Sd = Obj->getSourceVersionCommand(Command);
       PrintSourceVersionCommand(Sd);
index 01b0741..39e9092 100644 (file)
@@ -713,12 +713,30 @@ void MachODumper::printMachOVersionMin() {
     case MachO::LC_VERSION_MIN_WATCHOS:
       Cmd = "LC_VERSION_MIN_WATCHOS";
       break;
+    case MachO::LC_BUILD_VERSION:
+      Cmd = "LC_BUILD_VERSION";
+      break;
     default:
       continue;
     }
 
-    MachO::version_min_command VMC = Obj->getVersionMinLoadCommand(Load);
     DictScope Group(W, "MinVersion");
+    // Handle LC_BUILD_VERSION.
+    if (Load.C.cmd == MachO::LC_BUILD_VERSION) {
+      MachO::build_version_command BVC = Obj->getBuildVersionLoadCommand(Load);
+      W.printString("Cmd", Cmd);
+      W.printNumber("Size", BVC.cmdsize);
+      W.printString("Platform",
+                    MachOObjectFile::getBuildPlatform(BVC.platform));
+      W.printString("Version", MachOObjectFile::getVersionString(BVC.minos));
+      if (BVC.sdk)
+        W.printString("SDK", MachOObjectFile::getVersionString(BVC.sdk));
+      else
+        W.printString("SDK", StringRef("n/a"));
+      continue;
+    }
+
+    MachO::version_min_command VMC = Obj->getVersionMinLoadCommand(Load);
     W.printString("Cmd", Cmd);
     W.printNumber("Size", VMC.cmdsize);
     SmallString<32> Version;
index 0c30dc7..9ad2a6d 100644 (file)
@@ -163,6 +163,23 @@ const char *MachODumper::processLoadCommandData<MachO::rpath_command>(
   return readString<MachO::rpath_command>(LC, LoadCmd);
 }
 
+template <>
+const char *MachODumper::processLoadCommandData<MachO::build_version_command>(
+    MachOYAML::LoadCommand &LC,
+    const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+  auto Start = LoadCmd.Ptr + sizeof(MachO::build_version_command);
+  auto NTools = LC.Data.build_version_command_data.ntools;
+  for (unsigned i = 0; i < NTools; ++i) {
+    auto Curr = Start + i * sizeof(MachO::build_tool_version);
+    MachO::build_tool_version BV;
+    memcpy((void *)&BV, Curr, sizeof(MachO::build_tool_version));
+    if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+      MachO::swapStruct(BV);
+    LC.Tools.push_back(BV);
+  }
+  return Start + NTools * sizeof(MachO::build_tool_version);
+}
+
 Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() {
   auto Y = make_unique<MachOYAML::Object>();
   Y->IsLittleEndian = Obj.isLittleEndian();
index 6a27a7f..92b736e 100644 (file)
@@ -180,6 +180,21 @@ size_t writeLoadCommandData<MachO::rpath_command>(MachOYAML::LoadCommand &LC,
   return writePayloadString(LC, OS);
 }
 
+template <>
+size_t writeLoadCommandData<MachO::build_version_command>(
+    MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) {
+  size_t BytesWritten = 0;
+  for (const auto &T : LC.Tools) {
+    struct MachO::build_tool_version tool = T;
+    if (IsLittleEndian != sys::IsLittleEndianHost)
+      MachO::swapStruct(tool);
+    OS.write(reinterpret_cast<const char *>(&tool),
+             sizeof(MachO::build_tool_version));
+    BytesWritten += sizeof(MachO::build_tool_version);
+  }
+  return BytesWritten;
+}
+
 void ZeroFillBytes(raw_ostream &OS, size_t Size) {
   std::vector<uint8_t> FillData;
   FillData.insert(FillData.begin(), Size, 0);