diff --git a/lib/macho.rb b/lib/macho.rb index c913dff93..bfe5c62d4 100644 --- a/lib/macho.rb +++ b/lib/macho.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "English" require_relative "macho/structure" require_relative "macho/view" require_relative "macho/headers" @@ -39,4 +40,22 @@ def self.open(filename) file end + + # Signs the dylib using an ad-hoc identity. + # Necessary after making any changes to a dylib, since otherwise + # changing a signed file invalidates its signature. + # @param filename [String] the file being opened + # @return [void] + # @raise [ModificationError] if the operation fails + def self.codesign!(filename) + # codesign binary is not available on Linux + return if RUBY_PLATFORM !~ /darwin/ + raise ArgumentError, "#{filename}: no such file" unless File.file?(filename) + + system("codesign", "--sign", "-", "--force", + "--preserve-metadata=entitlements,requirements,flags,runtime", + filename) + + raise ModificationError, "#{filename}: signing failed!" unless $CHILD_STATUS.success? + end end diff --git a/lib/macho/tools.rb b/lib/macho/tools.rb index 2bb05fdd6..00c80ef1c 100644 --- a/lib/macho/tools.rb +++ b/lib/macho/tools.rb @@ -25,6 +25,8 @@ def self.change_dylib_id(filename, new_id, options = {}) file.change_dylib_id(new_id, options) file.write! + + MachO.codesign!(filename) end # Changes a shared library install name in a Mach-O or Fat binary, @@ -41,6 +43,8 @@ def self.change_install_name(filename, old_name, new_name, options = {}) file.change_install_name(old_name, new_name, options) file.write! + + MachO.codesign!(filename) end # Changes a runtime path in a Mach-O or Fat binary, overwriting the source @@ -57,6 +61,8 @@ def self.change_rpath(filename, old_path, new_path, options = {}) file.change_rpath(old_path, new_path, options) file.write! + + MachO.codesign!(filename) end # Add a runtime path to a Mach-O or Fat binary, overwriting the source file. @@ -71,6 +77,8 @@ def self.add_rpath(filename, new_path, options = {}) file.add_rpath(new_path, options) file.write! + + MachO.codesign!(filename) end # Delete a runtime path from a Mach-O or Fat binary, overwriting the source @@ -86,6 +94,8 @@ def self.delete_rpath(filename, old_path, options = {}) file.delete_rpath(old_path, options) file.write! + + MachO.codesign!(filename) end # Merge multiple Mach-Os into one universal (Fat) binary. @@ -106,6 +116,8 @@ def self.merge_machos(filename, *files, fat64: false) fat_macho = MachO::FatFile.new_from_machos(*machos, :fat64 => fat64) fat_macho.write(filename) + + MachO.codesign!(filename) end end end