elftools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,123 @@
1
+ require 'elftools/sections/section'
2
+
3
+ module ELFTools
4
+ module Sections
5
+ # Class of symbol table section.
6
+ # Usually for section .symtab and .dynsym,
7
+ # which will refer to symbols in ELF file.
8
+ class SymTabSection < Section
9
+ # Instantiate a {SymTabSection} object.
10
+ # There's a +section_at+ lambda for {SymTabSection}
11
+ # to easily fetch other sections.
12
+ # @param [ELFTools::ELF_Shdr] header
13
+ # See {Section#initialize} for more information.
14
+ # @param [File] stream
15
+ # See {Section#initialize} for more information.
16
+ # @param [Proc] section_at
17
+ # The method for fetching other sections by index.
18
+ # This lambda should be {ELFTools::ELFFile#section_at}.
19
+ def initialize(header, stream, section_at: nil, **_kwagrs)
20
+ @section_at = section_at
21
+ # For faster #symbol_by_name
22
+ @symbol_name_map = {}
23
+ super
24
+ end
25
+
26
+ # Number of symbols.
27
+ # @return [Integer] The number.
28
+ # @example
29
+ # symtab.num_symbols
30
+ # #=> 75
31
+ def num_symbols
32
+ header.sh_size / header.sh_entsize
33
+ end
34
+
35
+ # Acquire the +n+-th symbol, 0-based.
36
+ #
37
+ # Symbols are lazy loaded.
38
+ # @param [Integer] n The index.
39
+ # @return [ELFTools:Symbol, NilClass]
40
+ # The target symbol.
41
+ # If +n+ is out of bound, +nil+ is returned.
42
+ def symbol_at(n)
43
+ @symbols ||= LazyArray.new(num_symbols, &method(:create_symbol))
44
+ @symbols[n]
45
+ end
46
+
47
+ # Iterate all symbols.
48
+ #
49
+ # All symbols are lazy loading, the symbol
50
+ # only be created whenever accessing it.
51
+ # This method is useful for {#symbol_by_name}
52
+ # since not all symbols need to be created.
53
+ # @param [Block] block
54
+ # Just like +Array#each+, you can give a block.
55
+ # @return [Array<ELFTools::symbol>]
56
+ # The whole symbols will be returned.
57
+ def each_symbols
58
+ Array.new(num_symbols) do |i|
59
+ sym = symbol_at(i)
60
+ block_given? ? yield(sym) : sym
61
+ end
62
+ end
63
+
64
+ alias symbols each_symbols
65
+
66
+ # Get symbol by it's name.
67
+ # @param [String] name
68
+ # The name of symbol.
69
+ # @return [ELFTools::Symbol] Desired symbol.
70
+ def symbol_by_name(name)
71
+ return @symbol_name_map[name] if @symbol_name_map[name]
72
+ each_symbols do |symbol|
73
+ @symbol_name_map[symbol.name] = symbol
74
+ return symbol if symbol.name == name
75
+ end
76
+ nil
77
+ end
78
+
79
+ # Return the symbol string section.
80
+ # Lazy loaded.
81
+ # @return [ELFTools::Sections::StrTabSection] The string table section.
82
+ def symstr
83
+ @symstr ||= @section_at.call(header.sh_link)
84
+ end
85
+
86
+ private
87
+
88
+ def create_symbol(n)
89
+ stream.pos = header.sh_offset + n * header.sh_entsize
90
+ sym = ELF_sym[header.elf_class].new(endian: header.class.self_endian)
91
+ sym.read(stream)
92
+ Symbol.new(sym, stream, symstr: method(:symstr))
93
+ end
94
+ end
95
+
96
+ # Class of symbol.
97
+ # XXX: Should this class be defined in an independent file?
98
+ class Symbol
99
+ attr_reader :header # @return [ELFTools::ELF32_sym, ELFTools::ELF64_sym] Section header.
100
+ attr_reader :stream # @return [File] Streaming object.
101
+
102
+ # Instantiate a {ELFTools::Symbol} object.
103
+ # @param [ELFTools::ELF32_sym, ELFTools::ELF64_sym] header
104
+ # The symbol header.
105
+ # @param [File] stream The streaming object.
106
+ # @param [ELFTools::Sections::StrTabSection, Proc] symstr
107
+ # The symbol string section.
108
+ # If +Proc+ is given, it will be called at the first time
109
+ # access {Symbol#name}.
110
+ def initialize(header, stream, symstr: nil)
111
+ @header = header
112
+ @stream = stream
113
+ @symstr = symstr
114
+ end
115
+
116
+ # Return the symbol name.
117
+ # @return [String] The name.
118
+ def name
119
+ @name ||= @symstr.call.name_at(header.st_name)
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,18 @@
1
+ require 'elftools/segments/segment'
2
+ require 'elftools/dynamic'
3
+
4
+ module ELFTools
5
+ module Segments
6
+ # Class for dynamic table segment.
7
+ #
8
+ # This class knows how to get the list of dynamic tags.
9
+ class DynamicSegment < Segment
10
+ include Dynamic # rock!
11
+ # Get the start address of tags.
12
+ # @return [Integer] Start address of tags.
13
+ def tag_start
14
+ header.p_offset
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'elftools/segments/segment'
2
+
3
+ module ELFTools
4
+ module Segments
5
+ # For DT_INTERP segment, knows how to get path of
6
+ # ELF interpreter.
7
+ class InterpSegment < Segment
8
+ # Get the path of interpreter.
9
+ # @return [String] Path to the interpreter.
10
+ # @example
11
+ # interp_segment.interp_name
12
+ # #=> '/lib64/ld-linux-x86-64.so.2'
13
+ def interp_name
14
+ data[0..-2] # remove last null byte
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ require 'elftools/note'
2
+ require 'elftools/segments/segment'
3
+
4
+ module ELFTools
5
+ module Segments
6
+ # Class of note segment.
7
+ class NoteSegment < Segment
8
+ # Load note related methods.
9
+ include ELFTools::Note
10
+
11
+ # Address offset of notes start.
12
+ # @return [Integer] The offset.
13
+ def note_start
14
+ header.p_offset
15
+ end
16
+
17
+ # The total size of notes in this segment.
18
+ # @return [Integer] The size.
19
+ def note_total_size
20
+ header.p_filesz
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,44 @@
1
+ module ELFTools
2
+ module Segments
3
+ # Base class of segments.
4
+ class Segment
5
+ attr_reader :header # @return [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] Program header.
6
+ attr_reader :stream # @return [File] Streaming object.
7
+
8
+ # Instantiate a {Segment} object.
9
+ # @param [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] header
10
+ # Program header.
11
+ # @param [File] stream
12
+ # Streaming object.
13
+ def initialize(header, stream)
14
+ @header = header
15
+ @stream = stream
16
+ end
17
+
18
+ # The content in this segment.
19
+ # @return [String] The content.
20
+ def data
21
+ stream.pos = header.p_offset
22
+ stream.read(header.p_filesz)
23
+ end
24
+
25
+ # Is this segment readable?
26
+ # @return [Boolean] Ture or false.
27
+ def readable?
28
+ (header.p_flags & 4) == 4
29
+ end
30
+
31
+ # Is this segment writable?
32
+ # @return [Boolean] Ture or false.
33
+ def writable?
34
+ (header.p_flags & 2) == 2
35
+ end
36
+
37
+ # Is this segment executable?
38
+ # @return [Boolean] Ture or false.
39
+ def executable?
40
+ (header.p_flags & 1) == 1
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ # Require this file to load all segment classes.
2
+
3
+ require 'elftools/segments/segment'
4
+
5
+ require 'elftools/segments/dynamic_segment'
6
+ require 'elftools/segments/interp_segment'
7
+ require 'elftools/segments/note_segment'
8
+
9
+ module ELFTools
10
+ # Module for defining different types of segments.
11
+ module Segments
12
+ # Class methods of {Segments::Segment}.
13
+ class << Segment
14
+ # Use different class according to +header.p_type+.
15
+ # @param [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] header Program header of a segment.
16
+ # @param [File] stream Streaming object.
17
+ # @return [ELFTools::Segments::Segment]
18
+ # Return object dependes on +header.p_type+.
19
+ def create(header, stream, *args)
20
+ klass = case header.p_type
21
+ when Constants::PT_DYNAMIC then DynamicSegment
22
+ when Constants::PT_INTERP then InterpSegment
23
+ when Constants::PT_NOTE then NoteSegment
24
+ else Segment
25
+ end
26
+ klass.new(header, stream, *args)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,133 @@
1
+ require 'bindata'
2
+ module ELFTools
3
+ # The base structure to define common methods.
4
+ class ELFStruct < BinData::Record
5
+ CHOICE_SIZE_T = {
6
+ selection: :elf_class, choices: { 32 => :uint32, 64 => :uint64 }
7
+ }.freeze
8
+
9
+ attr_accessor :elf_class # @return [Integer] 32 or 64.
10
+
11
+ # Hacking to get endian of current class
12
+ # @return [Symbol, NilClass] +:little+ or +:big+.
13
+ def self.self_endian
14
+ bindata_name[-2..-1] == 'ge' ? :big : :little
15
+ end
16
+ end
17
+
18
+ # ELF header structure.
19
+ class ELF_Ehdr < ELFStruct
20
+ endian :big_and_little
21
+ struct :e_ident do
22
+ string :magic, read_length: 4
23
+ int8 :ei_class
24
+ int8 :ei_data
25
+ int8 :ei_version
26
+ int8 :ei_osabi
27
+ int8 :ei_abiversion
28
+ string :ei_padding, read_length: 7 # no use
29
+ end
30
+ uint16 :e_type
31
+ uint16 :e_machine
32
+ uint32 :e_version
33
+ # entry point
34
+ choice :e_entry, **CHOICE_SIZE_T
35
+ choice :e_phoff, **CHOICE_SIZE_T
36
+ choice :e_shoff, **CHOICE_SIZE_T
37
+ uint32 :e_flags
38
+ uint16 :e_ehsize # size of this header
39
+ uint16 :e_phentsize # size of each segment
40
+ uint16 :e_phnum # number of segments
41
+ uint16 :e_shentsize # size of each section
42
+ uint16 :e_shnum # number of sections
43
+ uint16 :e_shstrndx # index of string table section
44
+ end
45
+
46
+ # Section header structure.
47
+ class ELF_Shdr < ELFStruct
48
+ endian :big_and_little
49
+ uint32 :sh_name
50
+ uint32 :sh_type
51
+ choice :sh_flags, **CHOICE_SIZE_T
52
+ choice :sh_addr, **CHOICE_SIZE_T
53
+ choice :sh_offset, **CHOICE_SIZE_T
54
+ choice :sh_size, **CHOICE_SIZE_T
55
+ uint32 :sh_link
56
+ uint32 :sh_info
57
+ choice :sh_addralign, **CHOICE_SIZE_T
58
+ choice :sh_entsize, **CHOICE_SIZE_T
59
+ end
60
+
61
+ # Program header structure for 32bit.
62
+ class ELF32_Phdr < ELFStruct
63
+ endian :big_and_little
64
+ uint32 :p_type
65
+ uint32 :p_offset
66
+ uint32 :p_vaddr
67
+ uint32 :p_paddr
68
+ uint32 :p_filesz
69
+ uint32 :p_memsz
70
+ uint32 :p_flags
71
+ uint32 :p_align
72
+ end
73
+
74
+ # Program header structure for 64bit.
75
+ class ELF64_Phdr < ELFStruct
76
+ endian :big_and_little
77
+ uint32 :p_type
78
+ uint32 :p_flags
79
+ uint64 :p_offset
80
+ uint64 :p_vaddr
81
+ uint64 :p_paddr
82
+ uint64 :p_filesz
83
+ uint64 :p_memsz
84
+ uint64 :p_align
85
+ end
86
+ ELF_Phdr = {
87
+ 32 => ELF32_Phdr,
88
+ 64 => ELF64_Phdr
89
+ }.freeze
90
+
91
+ # Symbol structure for 32bit.
92
+ class ELF32_sym < ELFStruct
93
+ endian :big_and_little
94
+ uint32 :st_name
95
+ uint32 :st_value
96
+ uint32 :st_size
97
+ uint8 :st_info
98
+ uint8 :st_other
99
+ uint16 :st_shndx
100
+ end
101
+
102
+ # Symbol structure for 64bit.
103
+ class ELF64_sym < ELFStruct
104
+ endian :big_and_little
105
+ uint32 :st_name # Symbol name, index in string tbl
106
+ uint8 :st_info # Type and binding attributes
107
+ uint8 :st_other # No defined meaning, 0
108
+ uint16 :st_shndx # Associated section index
109
+ uint64 :st_value # Value of the symbol
110
+ uint64 :st_size # Associated symbol size
111
+ end
112
+ ELF_sym = {
113
+ 32 => ELF32_sym,
114
+ 64 => ELF64_sym
115
+ }.freeze
116
+
117
+ # Note header.
118
+ class ELF_Nhdr < ELFStruct
119
+ endian :big_and_little
120
+ uint32 :n_namesz # Name size
121
+ uint32 :n_descsz # Content size
122
+ uint32 :n_type # Content type
123
+ end
124
+
125
+ # Dynamic tag header.
126
+ class ELF_Dyn < ELFStruct
127
+ endian :big_and_little
128
+ choice :d_tag, selection: :elf_class, choices: { 32 => :int32, 64 => :int64 }
129
+ # This is an union type named +d_un+ in original source,
130
+ # simplify it to be +d_val+ here.
131
+ choice :d_val, **CHOICE_SIZE_T
132
+ end
133
+ end
@@ -0,0 +1,51 @@
1
+ module ELFTools
2
+ # Define some util methods.
3
+ module Util
4
+ # Class methods.
5
+ module ClassMethods
6
+ # Round up the number to be mulitple of
7
+ # +2**bit+.
8
+ # @param [Integer] num Number to be rounded-up.
9
+ # @param [Integer] bit How many bit to be aligned.
10
+ # @return [Integer] See examples.
11
+ # @example
12
+ # align(10, 1) #=> 10
13
+ # align(10, 2) #=> 12
14
+ # align(10, 3) #=> 16
15
+ # align(10, 4) #=> 16
16
+ # align(10, 5) #=> 32
17
+ def align(num, bit)
18
+ n = 2**bit
19
+ return num if (num % n).zero?
20
+ (num + n) & ~(n - 1)
21
+ end
22
+
23
+ # Fetch the correct value from module +mod+.
24
+ #
25
+ # See {ELFTools::ELFFile#segment_by_type} for how to
26
+ # use this method.
27
+ # @param [Module] mod The module defined constant numbers.
28
+ # @param [Integer, Symbol, String] val
29
+ # Desired value.
30
+ # @return [Integer]
31
+ # Currently this method always return a value
32
+ # from {ELFTools::Constants}.
33
+ def to_constant(mod, val)
34
+ # Ignore the outest name.
35
+ module_name = mod.name.sub('ELFTools::', '')
36
+ # if val is an integer, check if exists in mod
37
+ if val.is_a?(Integer)
38
+ return val if mod.constants.any? { |c| mod.const_get(c) == val }
39
+ raise ArgumentError, "No constants in #{module_name} is #{val}"
40
+ end
41
+ val = val.to_s.upcase
42
+ prefix = module_name.split('::')[-1]
43
+ val = prefix + '_' + val unless val.start_with?(prefix)
44
+ val = val.to_sym
45
+ raise ArgumentError, "No constants in #{module_name} named \"#{val}\"" unless mod.const_defined?(val)
46
+ mod.const_get(val)
47
+ end
48
+ end
49
+ extend ClassMethods
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module ELFTools
2
+ VERSION = '0.1.0'.freeze
3
+ end
data/lib/elftools.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'elftools/constants'
2
+ require 'elftools/elf_file'
3
+ require 'elftools/version'
4
+
5
+ # The ELF parsing tools!
6
+ # Main entry point is {ELFTools::ELFFile}, see it
7
+ # for more information.
8
+ module ELFTools
9
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elftools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - david942j
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bindata
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.47'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.47'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '12.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '12.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.13.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.13.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.6'
97
+ description: |2
98
+ A light weight ELF parser. elftools is designed to be a low-level ELF parser.
99
+ Inspired by https://github.com/eliben/pyelftools.
100
+ email:
101
+ - david942j@gmail.com
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - README.md
107
+ - lib/elftools.rb
108
+ - lib/elftools/constants.rb
109
+ - lib/elftools/dynamic.rb
110
+ - lib/elftools/elf_file.rb
111
+ - lib/elftools/exceptions.rb
112
+ - lib/elftools/lazy_array.rb
113
+ - lib/elftools/note.rb
114
+ - lib/elftools/sections/dynamic_section.rb
115
+ - lib/elftools/sections/note_section.rb
116
+ - lib/elftools/sections/null_section.rb
117
+ - lib/elftools/sections/section.rb
118
+ - lib/elftools/sections/sections.rb
119
+ - lib/elftools/sections/str_tab_section.rb
120
+ - lib/elftools/sections/sym_tab_section.rb
121
+ - lib/elftools/segments/dynamic_segment.rb
122
+ - lib/elftools/segments/interp_segment.rb
123
+ - lib/elftools/segments/note_segment.rb
124
+ - lib/elftools/segments/segment.rb
125
+ - lib/elftools/segments/segments.rb
126
+ - lib/elftools/structures.rb
127
+ - lib/elftools/util.rb
128
+ - lib/elftools/version.rb
129
+ homepage: https://github.com/david942j/rbelftools
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 2.1.0
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 2.6.10
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: ELFTools - Pure ruby library for parsing ELF files
153
+ test_files: []