class MachO::MachOStructure

A general purpose pseudo-structure. Described in detail in docs/machostructure-dsl.md. @abstract

Attributes

min_args[R]

Public Class Methods

bytesize() click to toggle source
# File lib/macho/structure.rb, line 108
def bytesize
  @bytesize ||= @size_list.sum
end
format() click to toggle source
# File lib/macho/structure.rb, line 104
def format
  @format ||= @fmt_list.join
end
new(*args) click to toggle source

@param args [Array] list of field parameters

# File lib/macho/structure.rb, line 75
def initialize(*args)
  raise ArgumentError, "Invalid number of arguments" if args.size < self.class.min_args

  @values = args
end
new_from_bin(endianness, bin) click to toggle source

@param endianness [Symbol] either ‘:big` or `:little` @param bin [String] the string to be unpacked into the new structure @return [MachO::MachOStructure] the resulting structure @api private

# File lib/macho/structure.rb, line 98
def new_from_bin(endianness, bin)
  format = Utils.specialize_format(self.format, endianness)

  new(*bin.unpack(format))
end

Private Class Methods

def_class_reader(name, type, idx) click to toggle source

Generates a reader method for classes that need to be initialized. These classes are defined in the Fields::CLASSES_TO_INIT array. @param name [Symbol] name of internal field @param type [Symbol] type of field in terms of binary size @param idx [Integer] the index of the field value in the @values array @api private

# File lib/macho/structure.rb, line 196
def def_class_reader(name, type, idx)
  case type
  when :lcstr
    define_method(name) do
      instance_variable_defined?("@#{name}") ||
        instance_variable_set("@#{name}", LoadCommands::LoadCommand::LCStr.new(self, @values[idx]))

      instance_variable_get("@#{name}")
    end
  when :two_level_hints_table
    define_method(name) do
      instance_variable_defined?("@#{name}") ||
        instance_variable_set("@#{name}", LoadCommands::TwolevelHintsCommand::TwolevelHintsTable.new(view, htoffset, nhints))

      instance_variable_get("@#{name}")
    end
  when :tool_entries
    define_method(name) do
      instance_variable_defined?("@#{name}") ||
        instance_variable_set("@#{name}", LoadCommands::BuildVersionCommand::ToolEntries.new(view, @values[idx]))

      instance_variable_get("@#{name}")
    end
  end
end
def_default_reader(name, idx, default) click to toggle source

Generates a reader method for fields that have default values. @param name [Symbol] name of internal field @param idx [Integer] the index of the field value in the @values array @param default [Value] the default value @api private

# File lib/macho/structure.rb, line 255
def def_default_reader(name, idx, default)
  define_method(name) do
    instance_variable_defined?("@#{name}") ||
      instance_variable_set("@#{name}", @values.size > idx ? @values[idx] : default)

    instance_variable_get("@#{name}")
  end
end
def_mask_reader(name, idx, mask) click to toggle source

Generates a reader method for fields that need to be bitmasked. @param name [Symbol] name of internal field @param idx [Integer] the index of the field value in the @values array @param mask [Integer] the bitmask @api private

# File lib/macho/structure.rb, line 227
def def_mask_reader(name, idx, mask)
  define_method(name) do
    instance_variable_defined?("@#{name}") ||
      instance_variable_set("@#{name}", @values[idx] & ~mask)

    instance_variable_get("@#{name}")
  end
end
def_reader(name, idx) click to toggle source

Generates an attr_reader like method for a field. @param name [Symbol] name of internal field @param idx [Integer] the index of the field value in the @values array @api private

# File lib/macho/structure.rb, line 268
def def_reader(name, idx)
  define_method(name) do
    @values[idx]
  end
end
def_to_s(name) click to toggle source

Generates the to_s method based on the named field. @param name [Symbol] name of the field @api private

# File lib/macho/structure.rb, line 277
def def_to_s(name)
  define_method(:to_s) do
    send(name).to_s
  end
end
def_unpack_reader(name, idx, unpack) click to toggle source

Generates a reader method for fields that need further unpacking. @param name [Symbol] name of internal field @param idx [Integer] the index of the field value in the @values array @param unpack [String] the format code used for further binary unpacking @api private

# File lib/macho/structure.rb, line 241
def def_unpack_reader(name, idx, unpack)
  define_method(name) do
    instance_variable_defined?("@#{name}") ||
      instance_variable_set("@#{name}", @values[idx].unpack(unpack))

    instance_variable_get("@#{name}")
  end
end
field(name, type, **options) click to toggle source

@param name [Symbol] name of internal field @param type [Symbol] type of field in terms of binary size @param options [Hash] set of additional options Expected options

:size [Integer] size in bytes
:mask [Integer] bitmask
:unpack [String] string format
:default [Value] default value
:to_s [Boolean] flag for generating #to_s
:endian [Symbol] optionally specify :big or :little endian
:padding [Symbol] optionally specify :null padding

@api private

# File lib/macho/structure.rb, line 144
def field(name, type, **options)
  raise ArgumentError, "Invalid field type #{type}" unless Fields::FORMAT_CODE.key?(type)

  # Get field idx for size_list and fmt_list
  idx = if @field_idxs.key?(name)
    @field_idxs[name]
  else
    @min_args += 1 unless options.key?(:default) || Fields::NO_ARG_REQUIRED.include?(type)
    @field_idxs[name] = @field_idxs.size
    @size_list << nil
    @fmt_list << nil
    @field_idxs.size - 1
  end

  # Update string type if padding is specified
  type = :null_padded_string if type == :string && options[:padding] == :null

  # Add to size_list and fmt_list
  @size_list[idx] = Fields::BYTE_SIZE[type] || options[:size]
  @fmt_list[idx] = if options[:endian]
    Utils.specialize_format(Fields::FORMAT_CODE[type], options[:endian])
  else
    Fields::FORMAT_CODE[type]
  end
  @fmt_list[idx] += options[:size].to_s if options.key?(:size)

  # Generate methods
  if Fields::CLASSES_TO_INIT.include?(type)
    def_class_reader(name, type, idx)
  elsif options.key?(:mask)
    def_mask_reader(name, idx, options[:mask])
  elsif options.key?(:unpack)
    def_unpack_reader(name, idx, options[:unpack])
  elsif options.key?(:default)
    def_default_reader(name, idx, options[:default])
  else
    def_reader(name, idx)
  end

  def_to_s(name) if options[:to_s]
end
inherited(subclass) click to toggle source

@param subclass [Class] subclass type @api private

# File lib/macho/structure.rb, line 116
def inherited(subclass) # rubocop:disable Lint/MissingSuper
  # Clone all class instance variables
  field_idxs = @field_idxs.dup
  size_list = @size_list.dup
  fmt_list = @fmt_list.dup
  min_args = @min_args.dup

  # Add those values to the inheriting class
  subclass.class_eval do
    @field_idxs = field_idxs
    @size_list = size_list
    @fmt_list = fmt_list
    @min_args = min_args
  end
end

Public Instance Methods

to_h() click to toggle source

@return [Hash] a hash representation of this {MachOStructure}.

# File lib/macho/structure.rb, line 82
def to_h
  {
    "structure" => {
      "format" => self.class.format,
      "bytesize" => self.class.bytesize,
    },
  }
end