Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enums #62

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ Style/AccessorGrouping:

Style/FormatStringToken:
Enabled: false

Style/TrivialAccessors:
AllowDSLWriters: true
70 changes: 70 additions & 0 deletions lib/elftools/enums.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

# copyright https://stackoverflow.com/questions/75759/how-to-implement-enums-in-ruby
#
class Enum
def self.enum_attr(name, num)
name = name.to_s

@values ||= {}
@values[num] = name

define_method("#{name}?") do
@value == num
end

instance = new(num)

@instances ||= {}
@instances[num] = instance

define_singleton_method(name.upcase.to_sym) { instance }
const_set(name.upcase.to_sym, instance) if name.match?(/^[[:alpha:]]/)
end

class << self
zygzagZ marked this conversation as resolved.
Show resolved Hide resolved
attr_reader :values
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add document of its type


def val(value)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add document

key = if value.is_a? self.class
value.to_i
elsif values.keys.include?(value)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keys.include? can be replaced with "key?"

value
else
values.key(value.to_s)
end
raise ArgumentError, "Unknown enum #{value}" unless key

key
end

def [](value)
@instances[val(value)]
end
end

# Initialize enum with value or name
# @param [Integer, Symbol] n Value or name.
# @return [Enum]
# Throws ArgumentError if enum name or value is invalid.
def initialize(value = 0)
@value = self.class.val(value)
end

def to_i
@value
end

def to_s
self.class.values[@value] || @value.to_s
end

def inspect
v = self.class.values[@value]
v ? "#{self.class.name}.#{v.upcase}" : @value
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsure whether it matters - shouldn't "@value" be "@value.inspect"?

end

def ==(other)
@value == self.class.val(other)
end
end
74 changes: 74 additions & 0 deletions lib/elftools/sections/relocation_section.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'elftools/constants'
require 'elftools/enums'
require 'elftools/sections/section'
require 'elftools/structs'

Expand Down Expand Up @@ -78,6 +79,56 @@ class Relocation
attr_reader :header # @return [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela] Rel(a) header.
attr_reader :stream # @return [#pos=, #read] Streaming object.

class Relocation32 < Enum
enum_attr :none, 0
enum_attr :"32", 1
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like these integer names but.. sigh

enum_attr :pc32, 2
enum_attr :got32, 3
enum_attr :plt32, 4
enum_attr :copy, 5
enum_attr :glob_dat, 6
enum_attr :jmp_slot, 7
enum_attr :relative, 8
enum_attr :gotoff, 9
enum_attr :gotpc, 10
enum_attr :"32plt", 11
enum_attr :"16", 20
enum_attr :pc16, 21
enum_attr :"8", 22
enum_attr :pc8, 23
enum_attr :size32, 38
end

class Relocation64 < Enum
enum_attr :none, 0
enum_attr :"64", 1
enum_attr :pc32, 2
enum_attr :got32, 3
enum_attr :plt32, 4
enum_attr :copy, 5
enum_attr :glob_dat, 6
enum_attr :jump_slot, 7
enum_attr :relative, 8
enum_attr :gotpcrel, 9
enum_attr :"32", 10
enum_attr :"32s", 11
enum_attr :"16", 12
enum_attr :pc16, 13
enum_attr :"8", 14
enum_attr :pc8, 15
enum_attr :pc64, 24
enum_attr :gotoff64, 25
enum_attr :gotpc32, 26
enum_attr :size32, 32
enum_attr :size64, 33
end

# Hash containing x86 relocation types class depending on elf_class
RELOCATION_ARCH = {
32 => Relocation32,
64 => Relocation64
}.freeze

# Instantiate a {Relocation} object.
def initialize(header, stream)
@header = header
Expand All @@ -100,6 +151,29 @@ def r_info_type
end
alias type r_info_type

# Convenience method returning relocation type wrapped in an Relocation Enum type.
# @param [Integer] bits Use {bits} x86 arch instead of elf_class to parse type enum.
# @return [Relocation32, Relocation64] relocation type enum
def type_enum(bits = header.elf_class)
RELOCATION_ARCH[bits].new(type)
end

# Update relocation type.
# @param [String, Relocation64, Relocation32, Integer] type Relocation type
def r_info_type=(type)
type = RELOCATION_ARCH[header.elf_class].new(type) if type.is_a? String
mask = (1 << mask_bit) - 1
header.r_info = (header.r_info & (~mask)) | (type.to_i & mask)
end
alias type= r_info_type=

# Update relocation symbol index.
# @param [Integer] ind symbol index.
def symbol_index=(ind)
mask = (1 << mask_bit) - 1
header.r_info = (ind << mask_bit) | (header.r_info & mask)
end

private
zygzagZ marked this conversation as resolved.
Show resolved Hide resolved

def mask_bit
Expand Down
73 changes: 73 additions & 0 deletions lib/elftools/sections/sym_tab_section.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require 'elftools/enums'
zygzagZ marked this conversation as resolved.
Show resolved Hide resolved
require 'elftools/sections/section'

module ELFTools
Expand Down Expand Up @@ -103,6 +104,42 @@ class Symbol
attr_reader :header # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] Section header.
attr_reader :stream # @return [#pos=, #read] Streaming object.

# based on https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-79797.html
class Bind < Enum
enum_attr :local, 0
enum_attr :global, 1
enum_attr :weak, 2
enum_attr :loos, 10
enum_attr :hios, 12
enum_attr :loproc, 13
enum_attr :hiproc, 15
end

class Type < Enum
enum_attr :notype, 0
enum_attr :object, 1
enum_attr :func, 2
enum_attr :section, 3
enum_attr :file, 4
enum_attr :common, 5
enum_attr :tls, 6
enum_attr :loos, 10
enum_attr :hios, 12
enum_attr :sparc_register, 13
enum_attr :loproc, 13
enum_attr :hiproc, 15
end

class Visibility < Enum
enum_attr :default, 0
enum_attr :internal, 1
enum_attr :hidden, 2
enum_attr :protected, 3
enum_attr :exported, 4
enum_attr :singleton, 5
enum_attr :eliminate, 6
end

# Instantiate a {ELFTools::Sections::Symbol} object.
# @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] header
# The symbol header.
Expand All @@ -122,6 +159,42 @@ def initialize(header, stream, symstr: nil)
def name
@name ||= @symstr.call.name_at(header.st_name)
end

# Return the symbol bind property.
# @return [Symbol::Bind] Bind property.
def st_bind
Bind.new(header.st_info >> 4)
end

# Updates the symbol bind property. Stored in header's st_info high bits.
# @param [Symbol::Bind, Integer] bind Bind property.
def st_bind=(bind)
header.st_info = (header.st_info & 0xf) | (bind.to_i << 4)
end

# Return the symbol type property.
# @return [Symbol::Type] type Type property.
def st_type
Type.new(header.st_info & 0xf)
end

# Updates the symbol type property. Stored in header's st_info low bits.
# @param [Symbol::Type, Integer] type Type property.
def st_type=(type)
header.st_info = (header.st_info & (~0xf)) | (type.to_i & 0xf)
end

# Return the symbol visibility property.
# @return [Symbol::Visibility] vis Visibility property.
def st_vis
Visibility.new(header.st_other & 0x7)
end

# Updates the symbol visibility property. Stored in header's st_other.
# @param [Symbol::Visibility, Integer] vis Visibility property.
def st_vis=(vis)
header.st_other = vis.to_i & 0x7
end
end
end
end
32 changes: 32 additions & 0 deletions spec/enums_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require 'elftools/enums'

class Numbers < Enum
enum_attr :first, 1
enum_attr :second, 2
enum_attr :third, 3
end

describe Enum do
it 'constructs' do
expect(Numbers::FIRST).to eq 1
expect(Numbers[1]).to eq 1
expect(Numbers.SECOND).to eq 2
expect(Numbers[:third]).to eq 3
expect(Numbers.new('third')).to eq 3
expect { Numbers[:fourth] }.to raise_error(ArgumentError)
expect { Numbers.new('fifth') }.to raise_error(ArgumentError)
expect { Numbers.new(6) }.to raise_error(ArgumentError)
expect { Numbers[7] }.to raise_error(ArgumentError)
end

it 'compares' do
expect(Numbers::FIRST).to eq Numbers::FIRST
expect(Numbers::FIRST).not_to eq 2
expect(Numbers.SECOND).to eq 2
expect(Numbers[:third]).to eq 'third'
expect(Numbers[:third]).not_to eq 'first'
expect(Numbers.new('third')).to eq :third
end
end