module MachO # A generic Mach-O error in execution. class MachOError < RuntimeError end # Raised when a Mach-O file modification fails. class ModificationError < MachOError end # Raised when a Mach-O file modification fails but can be recovered when # operating on multiple Mach-O slices of a fat binary in non-strict mode. class RecoverableModificationError < ModificationError # @return [Fixnum, nil] The index of the Mach-O slice of a fat binary for # which modification failed or `nil` if not a fat binary. This is used to # make the error message more useful. attr_accessor :macho_slice # @return [String] The exception message. def to_s s = super.to_s s = "While modifying Mach-O slice #{@macho_slice}: #{s}" if @macho_slice s end end # Raised when a file is not a Mach-O. class NotAMachOError < MachOError # @param error [String] the error in question def initialize(error) super error end end # Raised when a file is too short to be a valid Mach-O file. class TruncatedFileError < NotAMachOError def initialize super "File is too short to be a valid Mach-O" end end # Raised when a file's magic bytes are not valid Mach-O magic. class MagicError < NotAMachOError # @param num [Fixnum] the unknown number def initialize(num) super "Unrecognized Mach-O magic: 0x#{"%02x" % num}" end end # Raised when a file is a Java classfile instead of a fat Mach-O. class JavaClassFileError < NotAMachOError def initialize super "File is a Java class file" end end # Raised when a fat binary is loaded with MachOFile. class FatBinaryError < MachOError def initialize super "Fat binaries must be loaded with MachO::FatFile" end end # Raised when a Mach-O is loaded with FatFile. class MachOBinaryError < MachOError def initialize super "Normal binaries must be loaded with MachO::MachOFile" end end # Raised when the CPU type is unknown. class CPUTypeError < MachOError # @param cputype [Fixnum] the unknown CPU type def initialize(cputype) super "Unrecognized CPU type: 0x#{"%08x" % cputype}" end end # Raised when the CPU type/sub-type pair is unknown. class CPUSubtypeError < MachOError # @param cputype [Fixnum] the CPU type of the unknown pair # @param cpusubtype [Fixnum] the CPU sub-type of the unknown pair def initialize(cputype, cpusubtype) super "Unrecognized CPU sub-type: 0x#{"%08x" % cpusubtype}" \ " (for CPU type: 0x#{"%08x" % cputype})" end end # Raised when a mach-o file's filetype field is unknown. class FiletypeError < MachOError # @param num [Fixnum] the unknown number def initialize(num) super "Unrecognized Mach-O filetype code: 0x#{"%02x" % num}" end end # Raised when an unknown load command is encountered. class LoadCommandError < MachOError # @param num [Fixnum] the unknown number def initialize(num) super "Unrecognized Mach-O load command: 0x#{"%02x" % num}" end end # Raised when a load command can't be created manually. class LoadCommandNotCreatableError < MachOError # @param cmd_sym [Symbol] the uncreatable load command's symbol def initialize(cmd_sym) super "Load commands of type #{cmd_sym} cannot be created manually" end end # Raised when the number of arguments used to create a load command manually # is wrong. class LoadCommandCreationArityError < MachOError # @param cmd_sym [Symbol] the load command's symbol # @param expected_arity [Fixnum] the number of arguments expected # @param actual_arity [Fixnum] the number of arguments received def initialize(cmd_sym, expected_arity, actual_arity) super "Expected #{expected_arity} arguments for #{cmd_sym} creation," \ " got #{actual_arity}" end end # Raised when a load command can't be serialized. class LoadCommandNotSerializableError < MachOError # @param cmd_sym [Symbol] the load command's symbol def initialize(cmd_sym) super "Load commands of type #{cmd_sym} cannot be serialized" end end # Raised when a load command string is malformed in some way. class LCStrMalformedError < MachOError # @param lc [MachO::LoadCommand] the load command containing the string def initialize(lc) super "Load command #{lc.type} at offset #{lc.view.offset} contains a" \ " malformed string" end end # Raised when a change at an offset is not valid. class OffsetInsertionError < ModificationError # @param offset [Fixnum] the invalid offset def initialize(offset) super "Insertion at offset #{offset} is not valid" end end # Raised when load commands are too large to fit in the current file. class HeaderPadError < ModificationError # @param filename [String] the filename def initialize(filename) super "Updated load commands do not fit in the header of " \ "#{filename}. #{filename} needs to be relinked, possibly with " \ "-headerpad or -headerpad_max_install_names" end end # Raised when attempting to change a dylib name that doesn't exist. class DylibUnknownError < RecoverableModificationError # @param dylib [String] the unknown shared library name def initialize(dylib) super "No such dylib name: #{dylib}" end end # Raised when a dylib is missing an ID class DylibIdMissingError < RecoverableModificationError def initialize super "Dylib is missing a dylib ID" end end # Raised when attempting to change an rpath that doesn't exist. class RpathUnknownError < RecoverableModificationError # @param path [String] the unknown runtime path def initialize(path) super "No such runtime path: #{path}" end end # Raised when attempting to add an rpath that already exists. class RpathExistsError < RecoverableModificationError # @param path [String] the extant path def initialize(path) super "#{path} already exists" end end # Raised whenever unfinished code is called. class UnimplementedError < MachOError # @param thing [String] the thing that is unimplemented def initialize(thing) super "Unimplemented: #{thing}" end end end