Index: llvm/trunk/include/llvm/Object/MachO.h =================================================================== --- llvm/trunk/include/llvm/Object/MachO.h +++ llvm/trunk/include/llvm/Object/MachO.h @@ -353,6 +353,10 @@ 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 @@ 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 @@ LibraryList Libraries; LoadCommandList LoadCommands; typedef SmallVector LibraryShortName; + using BuildToolList = SmallVector; + BuildToolList BuildTools; mutable LibraryShortName LibrariesShortNames; const char *SymtabLoadCmd; const char *DysymtabLoadCmd; Index: llvm/trunk/include/llvm/ObjectYAML/MachOYAML.h =================================================================== --- llvm/trunk/include/llvm/ObjectYAML/MachOYAML.h +++ llvm/trunk/include/llvm/ObjectYAML/MachOYAML.h @@ -53,6 +53,7 @@ virtual ~LoadCommand(); llvm::MachO::macho_load_command Data; std::vector
Sections; + std::vector Tools; std::vector PayloadBytes; std::string PayloadString; uint64_t ZeroPadBytes; @@ -146,6 +147,7 @@ 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 @@ static void mapping(IO &IO, MachOYAML::NListEntry &NListEntry); }; +template <> struct MappingTraits { + static void mapping(IO &IO, MachO::build_tool_version &tool); +}; + #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ io.enumCase(value, #LCName, MachO::LCName); Index: llvm/trunk/include/llvm/Support/MachO.h =================================================================== --- llvm/trunk/include/llvm/Support/MachO.h +++ llvm/trunk/include/llvm/Support/MachO.h @@ -487,6 +487,22 @@ 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 struct mach_header { @@ -827,6 +843,21 @@ 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 @@ 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: llvm/trunk/include/llvm/Support/MachO.def =================================================================== --- llvm/trunk/include/llvm/Support/MachO.def +++ llvm/trunk/include/llvm/Support/MachO.def @@ -74,6 +74,7 @@ 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(uuid_command) LOAD_COMMAND_STRUCT(version_min_command) LOAD_COMMAND_STRUCT(note_command) +LOAD_COMMAND_STRUCT(build_version_command) #endif Index: llvm/trunk/lib/Object/MachOObjectFile.cpp =================================================================== --- llvm/trunk/lib/Object/MachOObjectFile.cpp +++ llvm/trunk/lib/Object/MachOObjectFile.cpp @@ -809,6 +809,27 @@ return Error::success(); } +static Error +parseBuildVersionCommand(const MachOObjectFile &Obj, + const MachOObjectFile::LoadCommandInfo &Load, + SmallVectorImpl &BuildTools, + uint32_t LoadCommandIndex) { + MachO::build_version_command BVC = + getStruct(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 @@ } 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 @@ return getStruct(*this, L.Ptr); } +MachO::build_version_command +MachOObjectFile::getBuildVersionLoadCommand(const LoadCommandInfo &L) const { + return getStruct(*this, L.Ptr); +} + +MachO::build_tool_version +MachOObjectFile::getBuildToolVersion(unsigned index) const { + return getStruct(*this, BuildTools[index]); +} + MachO::dylib_command MachOObjectFile::getDylibIDLoadCommand(const LoadCommandInfo &L) const { return getStruct(*this, L.Ptr); Index: llvm/trunk/lib/ObjectYAML/MachOYAML.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/MachOYAML.cpp +++ llvm/trunk/lib/ObjectYAML/MachOYAML.cpp @@ -230,6 +230,12 @@ IO.mapOptional("PayloadString", LoadCommand.PayloadString); } +template <> +void mapLoadCommandData( + IO &IO, MachOYAML::LoadCommand &LoadCommand) { + IO.mapOptional("Tools", LoadCommand.Tools); +} + void MappingTraits::mapping( IO &IO, MachOYAML::LoadCommand &LoadCommand) { MachO::LoadCommandType TempCmd = static_cast( @@ -282,6 +288,12 @@ IO.mapOptional("reserved3", Section.reserved3); } +void MappingTraits::mapping( + IO &IO, MachO::build_tool_version &tool) { + IO.mapRequired("tool", tool.tool); + IO.mapRequired("version", tool.version); +} + void MappingTraits::mapping(IO &IO, MachO::dylib &DylibStruct) { IO.mapRequired("name", DylibStruct.name); IO.mapRequired("timestamp", DylibStruct.timestamp); @@ -566,6 +578,15 @@ IO.mapRequired("size", LoadCommand.size); } +void MappingTraits::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: llvm/trunk/test/MC/MachO/darwin-version-min-load-command.s =================================================================== --- llvm/trunk/test/MC/MachO/darwin-version-min-load-command.s +++ llvm/trunk/test/MC/MachO/darwin-version-min-load-command.s @@ -26,3 +26,10 @@ // 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 Index: llvm/trunk/test/ObjectYAML/MachO/build_version_command.yaml =================================================================== --- llvm/trunk/test/ObjectYAML/MachO/build_version_command.yaml +++ llvm/trunk/test/ObjectYAML/MachO/build_version_command.yaml @@ -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 Index: llvm/trunk/test/tools/llvm-objdump/X86/invalid-macho-build-version.yaml =================================================================== --- llvm/trunk/test/tools/llvm-objdump/X86/invalid-macho-build-version.yaml +++ llvm/trunk/test/tools/llvm-objdump/X86/invalid-macho-build-version.yaml @@ -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 +... Index: llvm/trunk/test/tools/llvm-objdump/X86/macho-build-version.yaml =================================================================== --- llvm/trunk/test/tools/llvm-objdump/X86/macho-build-version.yaml +++ llvm/trunk/test/tools/llvm-objdump/X86/macho-build-version.yaml @@ -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: llvm/trunk/tools/llvm-objdump/MachODump.cpp =================================================================== --- llvm/trunk/tools/llvm-objdump/MachODump.cpp +++ llvm/trunk/tools/llvm-objdump/MachODump.cpp @@ -8182,6 +8182,38 @@ 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 @@ } 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: llvm/trunk/tools/llvm-readobj/MachODumper.cpp =================================================================== --- llvm/trunk/tools/llvm-readobj/MachODumper.cpp +++ llvm/trunk/tools/llvm-readobj/MachODumper.cpp @@ -713,12 +713,30 @@ 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: llvm/trunk/tools/obj2yaml/macho2yaml.cpp =================================================================== --- llvm/trunk/tools/obj2yaml/macho2yaml.cpp +++ llvm/trunk/tools/obj2yaml/macho2yaml.cpp @@ -163,6 +163,23 @@ return readString(LC, LoadCmd); } +template <> +const char *MachODumper::processLoadCommandData( + 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> MachODumper::dump() { auto Y = make_unique(); Y->IsLittleEndian = Obj.isLittleEndian(); Index: llvm/trunk/tools/yaml2obj/yaml2macho.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2macho.cpp +++ llvm/trunk/tools/yaml2obj/yaml2macho.cpp @@ -180,6 +180,21 @@ return writePayloadString(LC, OS); } +template <> +size_t writeLoadCommandData( + 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(&tool), + sizeof(MachO::build_tool_version)); + BytesWritten += sizeof(MachO::build_tool_version); + } + return BytesWritten; +} + void ZeroFillBytes(raw_ostream &OS, size_t Size) { std::vector FillData; FillData.insert(FillData.begin(), Size, 0);