diff --git a/export.go b/export.go index 2bbd028..ac3d55f 100644 --- a/export.go +++ b/export.go @@ -283,11 +283,32 @@ func (f *File) CodeSign(config *codesign.Config) error { if config.SpecialSlots == nil { config.SpecialSlots = cs.CodeDirectories[0].SpecialSlots } + if config.RuntimeVersion == 0 { + if cs.CodeDirectories[0].Header.Runtime != 0 { + config.RuntimeVersion = cs.CodeDirectories[0].Header.Runtime + } else if bv := f.BuildVersion(); bv != nil { + config.RuntimeVersion = bv.Sdk + } else if vm := f.VersionMin(); vm != nil { + config.RuntimeVersion = vm.Sdk + } + } } } else { // create NEW code signature if config.ID == "" { return fmt.Errorf("you must supply an ID") } + // infer runtime version from build or min version load commands if necessary + if config.Flags & ctypes.RUNTIME != 0 { + if config.RuntimeVersion == 0 { + if bv := f.BuildVersion(); bv != nil { + config.RuntimeVersion = bv.Sdk + } else if vm := f.VersionMin(); vm != nil { + config.RuntimeVersion = vm.Sdk + } + } + } else { + config.RuntimeVersion = 0 + } cs = &CodeSignature{ CodeSignatureCmd: types.CodeSignatureCmd{ LoadCmd: types.LC_CODE_SIGNATURE, diff --git a/file.go b/file.go index 97de9ee..57ed489 100644 --- a/file.go +++ b/file.go @@ -1735,6 +1735,23 @@ func (f *File) BuildVersion() *BuildVersion { return nil } +// VersionMin returns the minimum-version load command, or nil if no minimum-version exists. +func (f *File) VersionMin() *VersionMin { + for _, l := range f.Loads { + switch s := l.(type) { + case *VersionMinMacOSX: + return &s.VersionMin + case *VersionMinTvOS: + return &s.VersionMin + case *VersionMinWatchOS: + return &s.VersionMin + case *VersionMiniPhoneOS: + return &s.VersionMin + } + } + return nil +} + // FileSets returns an array of Fileset entries. func (f *File) FileSets() []*FilesetEntry { var fsets []*FilesetEntry diff --git a/pkg/codesign/codesign.go b/pkg/codesign/codesign.go index 05bdfed..7e8cdba 100644 --- a/pkg/codesign/codesign.go +++ b/pkg/codesign/codesign.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/blacktop/go-macho/pkg/codesign/types" + mtypes "github.com/blacktop/go-macho/types" ) // CodeSignature object @@ -434,6 +435,7 @@ type Config struct { EntitlementsDER []byte ResourceDirSlotHash []byte SlotHashes slotHashes + RuntimeVersion mtypes.Version CertChain []*x509.Certificate SignerFunction func([]byte) ([]byte, error) } @@ -628,7 +630,7 @@ func createCodeDirectory(r io.Reader, config *Config) (*bytes.Buffer, error) { cdHeader := types.CodeDirectoryType{ CdEarliest: types.CdEarliest{ - Version: types.SUPPORTS_EXECSEG, // TODO: support other versions (e.g.SUPPORTS_RUNTIME) + Version: types.SUPPORTS_RUNTIME, // TODO: support other versions (e.g.SUPPORTS_LINKAGE) Flags: config.Flags, HashOffset: hashOffset, IdentOffset: identOffset, @@ -646,6 +648,9 @@ func createCodeDirectory(r io.Reader, config *Config) (*bytes.Buffer, error) { ExecSegBase: uint64(config.TextOffset), ExecSegLimit: uint64(config.TextSize), }, + CdRuntime: types.CdRuntime{ + Runtime: config.RuntimeVersion, + }, } // CodeDirectoryType is a variable length struct based on the Version field