elftools 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +16 -1
- data/lib/elftools/constants.rb +74 -43
- data/lib/elftools/dynamic.rb +67 -13
- data/lib/elftools/elf_file.rb +88 -29
- data/lib/elftools/note.rb +5 -5
- data/lib/elftools/sections/relocation_section.rb +108 -0
- data/lib/elftools/sections/section.rb +13 -3
- data/lib/elftools/sections/sections.rb +3 -1
- data/lib/elftools/sections/str_tab_section.rb +2 -10
- data/lib/elftools/sections/sym_tab_section.rb +6 -5
- data/lib/elftools/segments/segment.rb +13 -3
- data/lib/elftools/segments/segments.rb +1 -1
- data/lib/elftools/structs.rb +155 -0
- data/lib/elftools/util.rb +42 -0
- data/lib/elftools/version.rb +1 -1
- metadata +4 -3
- data/lib/elftools/structures.rb +0 -133
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2aa2517a78c13a631e9889e03e380b024c75585
|
4
|
+
data.tar.gz: 6d1de34194028eba0b787b148b648a518e6f0e1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e3b9b1c4f4e0c7cf38efd85ae0c5c250e875fb50bda990286fcf0dc69684970b6eae99f6b2881a0938ea5597da6439ae46fa694823dc3e69322e202b60f622b
|
7
|
+
data.tar.gz: fff51965be7467711d36af1db1374370654a7d8a771ca8f4dcc48e38fd3daff3fe928dc2dd034f3d7ded3ecadb4e8304bdc8feb996623fc052c6eeea03251742
|
data/README.md
CHANGED
@@ -98,6 +98,21 @@ elf.segment_by_type(:interp).interp_name
|
|
98
98
|
#=> "/lib64/ld-linux-x86-64.so.2"
|
99
99
|
```
|
100
100
|
|
101
|
+
## Relocations
|
102
|
+
```ruby
|
103
|
+
elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
|
104
|
+
# Use relocation to get plt names.
|
105
|
+
rela_section = elf.sections_by_type(:rela).last
|
106
|
+
rela_section.name
|
107
|
+
#=> ".rela.plt"
|
108
|
+
relocations = rela_section.relocations
|
109
|
+
relocations.map { |r| '%x' % r.header.r_info }
|
110
|
+
#=> ["100000007", "200000007", "300000007", "400000007", "500000007", "700000007"]
|
111
|
+
symtab = elf.section_at(rela_section.header.sh_link) # get the symbol table section
|
112
|
+
relocations.map { |r| symtab.symbol_at(r.symbol_index).name }
|
113
|
+
#=> ["puts", "__stack_chk_fail", "printf", "__libc_start_main", "fgets", "scanf"]
|
114
|
+
```
|
115
|
+
|
101
116
|
# Why rbelftools
|
102
117
|
|
103
118
|
1. Fully documented
|
@@ -117,7 +132,7 @@ elf.segment_by_type(:interp).interp_name
|
|
117
132
|
**rbelftools** is designed to be a library for furthur usage.
|
118
133
|
It will _not_ add any too trivial features.
|
119
134
|
For example, to check if NX disabled, you can use
|
120
|
-
|
135
|
+
`!elf.segment_by_type(:gnu_stack).executable?` but not `elf.nx?`
|
121
136
|
5. Section and segment parser
|
122
137
|
|
123
138
|
Providing common sections and segments parser. For example, .symtab, .shstrtab
|
data/lib/elftools/constants.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module ELFTools
|
2
2
|
# Define constants from elf.h.
|
3
|
-
# Mostly refer from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h
|
3
|
+
# Mostly refer from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h
|
4
|
+
# and binutils/elfcpp/elfcpp.h.
|
4
5
|
module Constants
|
5
6
|
# ELF magic header
|
6
7
|
ELFMAG = "\x7FELF".freeze
|
@@ -42,53 +43,61 @@ module ELFTools
|
|
42
43
|
PT_LOPROC = 0x70000000
|
43
44
|
PT_HIPROC = 0x7fffffff
|
44
45
|
PT_GNU_EH_FRAME = 0x6474e550
|
45
|
-
PT_GNU_STACK =
|
46
|
+
PT_GNU_STACK = 0x6474e551
|
47
|
+
PT_GNU_RELRO = 0x6474e552 # Read only after relocation
|
46
48
|
end
|
47
49
|
include PT
|
48
50
|
|
49
51
|
# Dynamic table types, records in +d_tag+.
|
50
52
|
module DT
|
51
|
-
DT_NULL
|
52
|
-
DT_NEEDED
|
53
|
-
DT_PLTRELSZ
|
54
|
-
DT_PLTGOT
|
55
|
-
DT_HASH
|
56
|
-
DT_STRTAB
|
57
|
-
DT_SYMTAB
|
58
|
-
DT_RELA
|
59
|
-
DT_RELASZ
|
60
|
-
DT_RELAENT
|
61
|
-
DT_STRSZ
|
62
|
-
DT_SYMENT
|
63
|
-
DT_INIT
|
64
|
-
DT_FINI
|
65
|
-
DT_SONAME
|
66
|
-
DT_RPATH
|
67
|
-
DT_SYMBOLIC
|
68
|
-
DT_REL
|
69
|
-
DT_RELSZ
|
70
|
-
DT_RELENT
|
71
|
-
DT_PLTREL
|
72
|
-
DT_DEBUG
|
73
|
-
DT_TEXTREL
|
74
|
-
DT_JMPREL
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
53
|
+
DT_NULL = 0
|
54
|
+
DT_NEEDED = 1
|
55
|
+
DT_PLTRELSZ = 2
|
56
|
+
DT_PLTGOT = 3
|
57
|
+
DT_HASH = 4
|
58
|
+
DT_STRTAB = 5
|
59
|
+
DT_SYMTAB = 6
|
60
|
+
DT_RELA = 7
|
61
|
+
DT_RELASZ = 8
|
62
|
+
DT_RELAENT = 9
|
63
|
+
DT_STRSZ = 10
|
64
|
+
DT_SYMENT = 11
|
65
|
+
DT_INIT = 12
|
66
|
+
DT_FINI = 13
|
67
|
+
DT_SONAME = 14
|
68
|
+
DT_RPATH = 15
|
69
|
+
DT_SYMBOLIC = 16
|
70
|
+
DT_REL = 17
|
71
|
+
DT_RELSZ = 18
|
72
|
+
DT_RELENT = 19
|
73
|
+
DT_PLTREL = 20
|
74
|
+
DT_DEBUG = 21
|
75
|
+
DT_TEXTREL = 22
|
76
|
+
DT_JMPREL = 23
|
77
|
+
DT_BIND_NOW = 24
|
78
|
+
DT_INIT_ARRAY = 25
|
79
|
+
DT_FINI_ARRAY = 26
|
80
|
+
DT_INIT_ARRAYSZ = 27
|
81
|
+
DT_FINI_ARRAYSZ = 28
|
82
|
+
DT_RUNPATH = 29
|
83
|
+
DT_FLAGS = 30
|
84
|
+
DT_ENCODING = 32
|
85
|
+
DT_LOOS = 0x6000000d
|
86
|
+
DT_HIOS = 0x6ffff000
|
87
|
+
DT_VALRNGLO = 0x6ffffd00
|
88
|
+
DT_VALRNGHI = 0x6ffffdff
|
89
|
+
DT_ADDRRNGLO = 0x6ffffe00
|
90
|
+
DT_ADDRRNGHI = 0x6ffffeff
|
91
|
+
DT_VERSYM = 0x6ffffff0
|
92
|
+
DT_RELACOUNT = 0x6ffffff9
|
93
|
+
DT_RELCOUNT = 0x6ffffffa
|
94
|
+
DT_FLAGS_1 = 0x6ffffffb
|
95
|
+
DT_VERDEF = 0x6ffffffc
|
96
|
+
DT_VERDEFNUM = 0x6ffffffd
|
97
|
+
DT_VERNEED = 0x6ffffffe
|
98
|
+
DT_VERNEEDNUM = 0x6fffffff
|
99
|
+
DT_LOPROC = 0x70000000
|
100
|
+
DT_HIPROC = 0x7fffffff
|
92
101
|
end
|
93
102
|
include DT
|
94
103
|
|
@@ -178,5 +187,27 @@ module ELFTools
|
|
178
187
|
end
|
179
188
|
end
|
180
189
|
include EM
|
190
|
+
|
191
|
+
# This module defines elf file types.
|
192
|
+
module ET
|
193
|
+
ET_NONE = 0
|
194
|
+
ET_REL = 1
|
195
|
+
ET_EXEC = 2
|
196
|
+
ET_DYN = 3
|
197
|
+
ET_CORE = 4
|
198
|
+
# Return the type name according to +e_type+ in ELF file header.
|
199
|
+
# @return [String] Type in string format.
|
200
|
+
def self.mapping(type)
|
201
|
+
case type
|
202
|
+
when Constants::ET_NONE then 'NONE'
|
203
|
+
when Constants::ET_REL then 'REL'
|
204
|
+
when Constants::ET_EXEC then 'EXEC'
|
205
|
+
when Constants::ET_DYN then 'DYN'
|
206
|
+
when Constants::ET_CORE then 'CORE'
|
207
|
+
else '<unknown>'
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
include ET
|
181
212
|
end
|
182
213
|
end
|
data/lib/elftools/dynamic.rb
CHANGED
@@ -14,18 +14,27 @@ module ELFTools
|
|
14
14
|
# header
|
15
15
|
# tag_start
|
16
16
|
# @param [Block] block You can give a block.
|
17
|
-
# @return [Array<ELFTools::Dynamic::Tag>]
|
17
|
+
# @return [Enumerator<ELFTools::Dynamic::Tag>, Array<ELFTools::Dynamic::Tag>]
|
18
|
+
# If block is not given, an enumerator will be returned.
|
19
|
+
# Otherwise, return array of tags.
|
18
20
|
def each_tags
|
21
|
+
return enum_for(:each_tags) unless block_given?
|
19
22
|
arr = []
|
20
23
|
0.step do |i|
|
21
24
|
tag = tag_at(i)
|
22
|
-
yield tag
|
25
|
+
yield tag
|
23
26
|
arr << tag
|
24
27
|
break if tag.header.d_tag == ELFTools::Constants::DT_NULL
|
25
28
|
end
|
26
29
|
arr
|
27
30
|
end
|
28
|
-
|
31
|
+
|
32
|
+
# Use {#tags} to get all tags.
|
33
|
+
# @return [Array<ELFTools::Dynamic::Tag>]
|
34
|
+
# Array of tags.
|
35
|
+
def tags
|
36
|
+
each_tags.to_a
|
37
|
+
end
|
29
38
|
|
30
39
|
# Get a tag of specific type.
|
31
40
|
# @param [Integer, Symbol, String] type
|
@@ -53,10 +62,7 @@ module ELFTools
|
|
53
62
|
# #=> #<ELFTools::Dynamic::Tag:0x0055d3d2d91b28 @header={:d_tag=>3, :d_val=>6295552}>
|
54
63
|
def tag_by_type(type)
|
55
64
|
type = Util.to_constant(Constants::DT, type)
|
56
|
-
each_tags
|
57
|
-
return tag if tag.header.d_tag == type
|
58
|
-
end
|
59
|
-
nil
|
65
|
+
each_tags.find { |tag| tag.header.d_tag == type }
|
60
66
|
end
|
61
67
|
|
62
68
|
# Get the +n+-th tag.
|
@@ -75,10 +81,10 @@ module ELFTools
|
|
75
81
|
return if n < 0
|
76
82
|
@tag_at_map ||= {}
|
77
83
|
return @tag_at_map[n] if @tag_at_map[n]
|
78
|
-
dyn = ELF_Dyn.new(endian: endian)
|
84
|
+
dyn = Structs::ELF_Dyn.new(endian: endian)
|
79
85
|
dyn.elf_class = header.elf_class
|
80
86
|
stream.pos = tag_start + n * dyn.num_bytes
|
81
|
-
@tag_at_map[n] = Tag.new(dyn.read(stream), stream)
|
87
|
+
@tag_at_map[n] = Tag.new(dyn.read(stream), stream, method(:str_offset))
|
82
88
|
end
|
83
89
|
|
84
90
|
private
|
@@ -87,20 +93,68 @@ module ELFTools
|
|
87
93
|
header.class.self_endian
|
88
94
|
end
|
89
95
|
|
96
|
+
# Get the DT_STRTAB's +d_val+ offset related to file.
|
97
|
+
def str_offset
|
98
|
+
# TODO: handle DT_STRTAB not exitsts.
|
99
|
+
@str_offset ||= @offset_from_vma.call(tag_by_type(:strtab).header.d_val.to_i)
|
100
|
+
end
|
101
|
+
|
90
102
|
# A tag class.
|
91
103
|
class Tag
|
92
|
-
attr_reader :header # @return [ELFTools::ELF_Dyn] The dynamic tag header.
|
104
|
+
attr_reader :header # @return [ELFTools::Structs::ELF_Dyn] The dynamic tag header.
|
93
105
|
attr_reader :stream # @return [File] Streaming object.
|
94
106
|
|
95
107
|
# Instantiate a {ELFTools::Dynamic::Tag} object.
|
96
108
|
# @param [ELF_Dyn] header The dynamic tag header.
|
97
109
|
# @param [File] stream Streaming object.
|
98
|
-
|
110
|
+
# @param [Method] str_offset
|
111
|
+
# Call this method to get the string offset related
|
112
|
+
# to file.
|
113
|
+
def initialize(header, stream, str_offset)
|
99
114
|
@header = header
|
100
115
|
@stream = stream
|
116
|
+
@str_offset = str_offset
|
117
|
+
end
|
118
|
+
|
119
|
+
TYPE_WITH_NAME = [Constants::DT_NEEDED,
|
120
|
+
Constants::DT_SONAME,
|
121
|
+
Constants::DT_RPATH,
|
122
|
+
Constants::DT_RUNPATH].freeze
|
123
|
+
# Return the content of this tag records.
|
124
|
+
#
|
125
|
+
# For normal tags, this method just return
|
126
|
+
# +header.d_val+. For tags with +header.d_val+
|
127
|
+
# in meaning of string offset (e.g. DT_NEEDED), this method would
|
128
|
+
# return the string it specified.
|
129
|
+
# Tags with type in {TYPE_WITH_NAME} are those tags with name.
|
130
|
+
# @return [Integer, String] The content this tag records.
|
131
|
+
# @example
|
132
|
+
# dynamic = elf.segment_by_type(:dynamic)
|
133
|
+
# dynamic.tag_by_type(:init).value
|
134
|
+
# #=> 4195600 # 0x400510
|
135
|
+
# dynamic.tag_by_type(:needed).value
|
136
|
+
# #=> 'libc.so.6'
|
137
|
+
def value
|
138
|
+
name || header.d_val.to_i
|
139
|
+
end
|
140
|
+
|
141
|
+
# Is this tag has a name?
|
142
|
+
#
|
143
|
+
# The criteria here is if this tag's type is in {TYPE_WITH_NAME}.
|
144
|
+
# @return [Boolean] Is this tag has a name.
|
145
|
+
def name?
|
146
|
+
TYPE_WITH_NAME.include?(header.d_tag)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Return the name of this tag.
|
150
|
+
#
|
151
|
+
# Only tags with name would return a name.
|
152
|
+
# Others would return +nil+.
|
153
|
+
# @return [String, NilClass] The name.
|
154
|
+
def name
|
155
|
+
return nil unless name?
|
156
|
+
Util.cstring(stream, @str_offset.call + header.d_val.to_i)
|
101
157
|
end
|
102
|
-
# TODO: Get the name of tags, e.g. SONAME
|
103
|
-
# TODO: Handle (non)-PIE ELF correctly.
|
104
158
|
end
|
105
159
|
end
|
106
160
|
end
|
data/lib/elftools/elf_file.rb
CHANGED
@@ -3,7 +3,7 @@ require 'elftools/exceptions'
|
|
3
3
|
require 'elftools/lazy_array'
|
4
4
|
require 'elftools/sections/sections'
|
5
5
|
require 'elftools/segments/segments'
|
6
|
-
require 'elftools/
|
6
|
+
require 'elftools/structs'
|
7
7
|
|
8
8
|
module ELFTools
|
9
9
|
# The main class for using elftools.
|
@@ -27,11 +27,11 @@ module ELFTools
|
|
27
27
|
# Return the file header.
|
28
28
|
#
|
29
29
|
# Lazy loading.
|
30
|
-
# @retrn [ELFTools::ELF_Ehdr] The header.
|
30
|
+
# @retrn [ELFTools::Structs::ELF_Ehdr] The header.
|
31
31
|
def header
|
32
|
-
return @header if @header
|
32
|
+
return @header if defined?(@header)
|
33
33
|
stream.pos = 0
|
34
|
-
@header = ELF_Ehdr.new(endian: endian)
|
34
|
+
@header = Structs::ELF_Ehdr.new(endian: endian)
|
35
35
|
@header.elf_class = elf_class
|
36
36
|
@header.read(stream)
|
37
37
|
end
|
@@ -65,6 +65,17 @@ module ELFTools
|
|
65
65
|
ELFTools::Constants::EM.mapping(header.e_machine)
|
66
66
|
end
|
67
67
|
|
68
|
+
# Return the ELF type according to +e_type+.
|
69
|
+
# @return [String] Type in string format.
|
70
|
+
# @example
|
71
|
+
# ELFFile.new(File.open('spec/files/libc.so.6')).elf_type
|
72
|
+
# #=> 'DYN'
|
73
|
+
# ELFFile.new(File.open('spec/files/amd64.elf')).elf_type
|
74
|
+
# #=> 'EXEC'
|
75
|
+
def elf_type
|
76
|
+
ELFTools::Constants::ET.mapping(header.e_type)
|
77
|
+
end
|
78
|
+
|
68
79
|
#========= method about sections
|
69
80
|
|
70
81
|
# Number of sections in this file.
|
@@ -87,13 +98,7 @@ module ELFTools
|
|
87
98
|
# elf.section_by_name('no such section')
|
88
99
|
# #=> nil
|
89
100
|
def section_by_name(name)
|
90
|
-
|
91
|
-
return @section_name_map[name] if @section_name_map[name]
|
92
|
-
each_sections do |section|
|
93
|
-
@section_name_map[section.name] = section
|
94
|
-
return section if section.name == name
|
95
|
-
end
|
96
|
-
nil
|
101
|
+
each_sections.find { |sec| sec.name == name }
|
97
102
|
end
|
98
103
|
|
99
104
|
# Iterate all sections.
|
@@ -104,17 +109,24 @@ module ELFTools
|
|
104
109
|
# since not all sections need to be created.
|
105
110
|
# @param [Block] block
|
106
111
|
# Just like +Array#each+, you can give a block.
|
107
|
-
# @return [ Array<ELFTools::Sections::Section>]
|
108
|
-
#
|
112
|
+
# @return [Enumerator<ELFTools::Sections::Section>, Array<ELFTools::Sections::Section>]
|
113
|
+
# As +Array#each+, if block is not given, a enumerator will be returned,
|
114
|
+
# otherwise, the whole sections will be returned.
|
109
115
|
def each_sections
|
116
|
+
return enum_for(:each_sections) unless block_given?
|
110
117
|
Array.new(num_sections) do |i|
|
111
118
|
sec = section_at(i)
|
112
|
-
|
119
|
+
yield sec
|
120
|
+
sec
|
113
121
|
end
|
114
122
|
end
|
115
123
|
|
116
|
-
# Simply use {#sections}
|
117
|
-
|
124
|
+
# Simply use {#sections} to get all sections.
|
125
|
+
# @return [Array<ELFTools::Sections::Section>]
|
126
|
+
# Whole sections.
|
127
|
+
def sections
|
128
|
+
each_sections.to_a
|
129
|
+
end
|
118
130
|
|
119
131
|
# Acquire the +n+-th section, 0-based.
|
120
132
|
#
|
@@ -128,6 +140,26 @@ module ELFTools
|
|
128
140
|
@sections[n]
|
129
141
|
end
|
130
142
|
|
143
|
+
# Fetch all sections with specific type.
|
144
|
+
#
|
145
|
+
# The available types are listed in {ELFTools::Constants},
|
146
|
+
# start with +SHT_+.
|
147
|
+
# This method accept giving block.
|
148
|
+
# @param [Integer, Symbol, String] type
|
149
|
+
# The type needed, similar format as {#segment_by_type}.
|
150
|
+
# @param [Block] block
|
151
|
+
# Block will be yielded whenever find a section.
|
152
|
+
# @return [Array<ELFTools::Sections::section>] The target sections.
|
153
|
+
# @example
|
154
|
+
# elf = ELFTools::ELFFile.new(File.open('spec/files/amd64.elf'))
|
155
|
+
# elf.sections_by_type(:rela)
|
156
|
+
# #=> [#<ELFTools::Sections::RelocationSection:0x00563cd3219970>,
|
157
|
+
# # #<ELFTools::Sections::RelocationSection:0x00563cd3b89d70>]
|
158
|
+
def sections_by_type(type, &block)
|
159
|
+
type = Util.to_constant(Constants::SHT, type)
|
160
|
+
Util.select_by_type(each_sections, type, &block)
|
161
|
+
end
|
162
|
+
|
131
163
|
# Get the string table section.
|
132
164
|
#
|
133
165
|
# This section is acquired by using the +e_shstrndx+
|
@@ -156,18 +188,24 @@ module ELFTools
|
|
156
188
|
# @return [Array<ELFTools::Segments::Segment>]
|
157
189
|
# Whole segments will be returned.
|
158
190
|
def each_segments
|
191
|
+
return enum_for(:each_segments) unless block_given?
|
159
192
|
Array.new(num_segments) do |i|
|
160
193
|
seg = segment_at(i)
|
161
|
-
|
194
|
+
yield seg
|
195
|
+
seg
|
162
196
|
end
|
163
197
|
end
|
164
198
|
|
165
|
-
# Simply use {#segments}
|
166
|
-
|
199
|
+
# Simply use {#segments} to get all segments.
|
200
|
+
# @return [Array<ELFTools::Segments::Segment>]
|
201
|
+
# Whole segments.
|
202
|
+
def segments
|
203
|
+
each_segments.to_a
|
204
|
+
end
|
167
205
|
|
168
206
|
# Get the first segment with +p_type=type+.
|
169
207
|
# The available types are listed in {ELFTools::Constants},
|
170
|
-
#
|
208
|
+
# start with +PT_+.
|
171
209
|
#
|
172
210
|
# Notice: this method will return the first segment found,
|
173
211
|
# to found all segments with specific type you can use {#segments_by_type}.
|
@@ -208,21 +246,22 @@ module ELFTools
|
|
208
246
|
# #=> nil # no such segment exists
|
209
247
|
def segment_by_type(type)
|
210
248
|
type = Util.to_constant(Constants::PT, type)
|
211
|
-
each_segments
|
212
|
-
return seg if seg.header.p_type == type
|
213
|
-
end
|
214
|
-
nil
|
249
|
+
each_segments.find { |seg| seg.header.p_type == type }
|
215
250
|
end
|
216
251
|
|
217
252
|
# Fetch all segments with specific type.
|
253
|
+
#
|
218
254
|
# If you want to find only one segment,
|
219
255
|
# use {#segment_by_type} instead.
|
256
|
+
# This method accept giving block.
|
220
257
|
# @param [Integer, Symbol, String] type
|
221
258
|
# The type needed, same format as {#segment_by_type}.
|
259
|
+
# @param [Block] block
|
260
|
+
# Block will be yielded whenever find a segement.
|
222
261
|
# @return [Array<ELFTools::Segments::Segment>] The target segments.
|
223
|
-
def segments_by_type(type)
|
262
|
+
def segments_by_type(type, &block)
|
224
263
|
type = Util.to_constant(Constants::PT, type)
|
225
|
-
|
264
|
+
Util.select_by_type(each_segments, type, &block)
|
226
265
|
end
|
227
266
|
|
228
267
|
# Acquire the +n+-th segment, 0-based.
|
@@ -237,6 +276,25 @@ module ELFTools
|
|
237
276
|
@segments[n]
|
238
277
|
end
|
239
278
|
|
279
|
+
# Get the offset related to file, given virtual memory address.
|
280
|
+
#
|
281
|
+
# This method should work no matter ELF is a PIE or not.
|
282
|
+
# This method refers from (actually equals to) binutils/readelf.c#offset_from_vma.
|
283
|
+
# @param [Integer] vma The address need query.
|
284
|
+
# @return [Integer] Offset related to file.
|
285
|
+
# @example
|
286
|
+
# elf = ELFTools::ELFFile.new(File.open('/bin/cat'))
|
287
|
+
# elf.offset_from_vma(0x401337)
|
288
|
+
# #=> 4919 # 0x1337
|
289
|
+
def offset_from_vma(vma, size = 0)
|
290
|
+
segments_by_type(:load) do |seg|
|
291
|
+
if vma >= (seg.header.p_vaddr & -seg.header.p_align) &&
|
292
|
+
vma + size <= seg.header.p_vaddr + seg.header.p_filesz
|
293
|
+
return vma - seg.header.p_vaddr + seg.header.p_offset
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
240
298
|
private
|
241
299
|
|
242
300
|
def identify
|
@@ -259,19 +317,20 @@ module ELFTools
|
|
259
317
|
|
260
318
|
def create_section(n)
|
261
319
|
stream.pos = header.e_shoff + n * header.e_shentsize
|
262
|
-
shdr = ELF_Shdr.new(endian: endian)
|
320
|
+
shdr = Structs::ELF_Shdr.new(endian: endian)
|
263
321
|
shdr.elf_class = elf_class
|
264
322
|
shdr.read(stream)
|
265
323
|
Sections::Section.create(shdr, stream,
|
324
|
+
offset_from_vma: method(:offset_from_vma),
|
266
325
|
strtab: method(:strtab_section),
|
267
326
|
section_at: method(:section_at))
|
268
327
|
end
|
269
328
|
|
270
329
|
def create_segment(n)
|
271
330
|
stream.pos = header.e_phoff + n * header.e_phentsize
|
272
|
-
phdr = ELF_Phdr[elf_class].new(endian: endian)
|
331
|
+
phdr = Structs::ELF_Phdr[elf_class].new(endian: endian)
|
273
332
|
phdr.elf_class = elf_class
|
274
|
-
Segments::Segment.create(phdr.read(stream), stream)
|
333
|
+
Segments::Segment.create(phdr.read(stream), stream, offset_from_vma: method(:offset_from_vma))
|
275
334
|
end
|
276
335
|
end
|
277
336
|
end
|
data/lib/elftools/note.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'elftools/
|
1
|
+
require 'elftools/structs'
|
2
2
|
require 'elftools/util'
|
3
3
|
|
4
4
|
module ELFTools
|
@@ -10,10 +10,10 @@ module ELFTools
|
|
10
10
|
# {ELFTools::Segments::NoteSegment} since some methods assume some attributes already
|
11
11
|
# exist.
|
12
12
|
module Note
|
13
|
-
# Since size of {ELFTools::ELF_Nhdr} will not change no
|
13
|
+
# Since size of {ELFTools::Structs::ELF_Nhdr} will not change no
|
14
14
|
# matter what endian and what arch, we can do this here.
|
15
15
|
# This value should equal to 12.
|
16
|
-
SIZE_OF_NHDR = ELF_Nhdr.new(endian: :little).num_bytes
|
16
|
+
SIZE_OF_NHDR = Structs::ELF_Nhdr.new(endian: :little).num_bytes
|
17
17
|
|
18
18
|
# Iterate all notes in a note section or segment.
|
19
19
|
#
|
@@ -68,13 +68,13 @@ module ELFTools
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def create_note(cur)
|
71
|
-
nhdr = ELF_Nhdr.new(endian: endian).read(stream)
|
71
|
+
nhdr = Structs::ELF_Nhdr.new(endian: endian).read(stream)
|
72
72
|
ELFTools::Note::Note.new(nhdr, stream, cur)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Class of a note.
|
76
76
|
class Note
|
77
|
-
attr_reader :header # @return [ELFTools::ELF_Nhdr] Note header.
|
77
|
+
attr_reader :header # @return [ELFTools::Structs::ELF_Nhdr] Note header.
|
78
78
|
attr_reader :stream # @return [File] Streaming object.
|
79
79
|
attr_reader :offset # @return [Integer] Address of this note start, includes note header.
|
80
80
|
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'elftools/constants'
|
2
|
+
require 'elftools/sections/section'
|
3
|
+
require 'elftools/structs'
|
4
|
+
|
5
|
+
module ELFTools
|
6
|
+
module Sections
|
7
|
+
# Class of note section.
|
8
|
+
# Note section records notes
|
9
|
+
class RelocationSection < Section
|
10
|
+
# Is this relocation a RELA or REL type.
|
11
|
+
# @return [Boolean] If is RELA.
|
12
|
+
def rela?
|
13
|
+
header.sh_type == Constants::SHT_RELA
|
14
|
+
end
|
15
|
+
|
16
|
+
# Number of relocations in this section.
|
17
|
+
# @return [Integer] The number.
|
18
|
+
def num_relocations
|
19
|
+
header.sh_size / header.sh_entsize
|
20
|
+
end
|
21
|
+
|
22
|
+
# Acquire the +n+-th relocation, 0-based.
|
23
|
+
#
|
24
|
+
# relocations are lazy loaded.
|
25
|
+
# @param [Integer] n The index.
|
26
|
+
# @return [ELFTools::Relocation, NilClass]
|
27
|
+
# The target relocation.
|
28
|
+
# If +n+ is out of bound, +nil+ is returned.
|
29
|
+
def relocation_at(n)
|
30
|
+
@relocations ||= LazyArray.new(num_relocations, &method(:create_relocation))
|
31
|
+
@relocations[n]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Iterate all relocations.
|
35
|
+
#
|
36
|
+
# All relocations are lazy loading, the relocation
|
37
|
+
# only be created whenever accessing it.
|
38
|
+
# @param [Block] block
|
39
|
+
# Just like +Array#each+, you can give a block.
|
40
|
+
# @return [Enumerator<ELFTools::Relocation>, Array<ELFTools::Relocation>]
|
41
|
+
# If block is not given, an enumerator will be returned.
|
42
|
+
# Otherwise, the whole relocations will be returned.
|
43
|
+
def each_relocations
|
44
|
+
return enum_for(:each_relocations) unless block_given?
|
45
|
+
Array.new(num_relocations) do |i|
|
46
|
+
rel = relocation_at(i)
|
47
|
+
yield rel
|
48
|
+
rel
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Simply use {#relocations} to get all relocations.
|
53
|
+
# @return [Array<ELFTools::Relocation>]
|
54
|
+
# Whole relocations.
|
55
|
+
def relocations
|
56
|
+
each_relocations.to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def create_relocation(n)
|
62
|
+
stream.pos = header.sh_offset + n * header.sh_entsize
|
63
|
+
klass = rela? ? Structs::ELF_Rela : Structs::ELF_Rel
|
64
|
+
rel = klass.new(endian: header.class.self_endian)
|
65
|
+
rel.elf_class = header.elf_class
|
66
|
+
rel.read(stream)
|
67
|
+
Relocation.new(rel, stream)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# A relocation entry.
|
73
|
+
#
|
74
|
+
# Can be either a REL or RELA relocation.
|
75
|
+
# XXX: move this to an independent file?
|
76
|
+
class Relocation
|
77
|
+
attr_reader :header # @return [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela] Rel(a) header.
|
78
|
+
attr_reader :stream # @return [File] Streaming object.
|
79
|
+
|
80
|
+
# Instantiate a {Relocation} object.
|
81
|
+
def initialize(header, stream)
|
82
|
+
@header = header
|
83
|
+
@stream = stream
|
84
|
+
end
|
85
|
+
|
86
|
+
# +r_info+ contains sym and type, use two methods
|
87
|
+
# to access them easier.
|
88
|
+
# @return [Integer] sym infor.
|
89
|
+
def r_info_sym
|
90
|
+
header.r_info >> mask_bit
|
91
|
+
end
|
92
|
+
alias symbol_index r_info_sym
|
93
|
+
|
94
|
+
# +r_info+ contains sym and type, use two methods
|
95
|
+
# to access them easier.
|
96
|
+
# @return [Integer] type infor.
|
97
|
+
def r_info_type
|
98
|
+
header.r_info & ((1 << mask_bit) - 1)
|
99
|
+
end
|
100
|
+
alias type r_info_type
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def mask_bit
|
105
|
+
header.elf_class == 32 ? 8 : 32
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -3,11 +3,11 @@ module ELFTools
|
|
3
3
|
module Sections
|
4
4
|
# Base class of sections.
|
5
5
|
class Section
|
6
|
-
attr_reader :header # @return [ELFTools::ELF_Shdr] Section header.
|
6
|
+
attr_reader :header # @return [ELFTools::Structs::ELF_Shdr] Section header.
|
7
7
|
attr_reader :stream # @return [File] Streaming object.
|
8
8
|
|
9
9
|
# Instantiate a {Section} object.
|
10
|
-
# @param [ELFTools::ELF_Shdr] header
|
10
|
+
# @param [ELFTools::Structs::ELF_Shdr] header
|
11
11
|
# The section header object.
|
12
12
|
# @param [File] stream
|
13
13
|
# The streaming object for further dump.
|
@@ -15,10 +15,20 @@ module ELFTools
|
|
15
15
|
# The string table object. For fetching section names.
|
16
16
|
# If +Proc+ if given, it will call at the first
|
17
17
|
# time access +#name+.
|
18
|
-
|
18
|
+
# @param [Method] offset_from_vma
|
19
|
+
# The method to get offset of file, given virtual memory address.
|
20
|
+
def initialize(header, stream, offset_from_vma: nil, strtab: nil, **_kwargs)
|
19
21
|
@header = header
|
20
22
|
@stream = stream
|
21
23
|
@strtab = strtab
|
24
|
+
@offset_from_vma = offset_from_vma
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return +header.sh_type+ in a simplier way.
|
28
|
+
# @return [Integer]
|
29
|
+
# The type, meaning of types are defined in {Constants::SHT}.
|
30
|
+
def type
|
31
|
+
header.sh_type
|
22
32
|
end
|
23
33
|
|
24
34
|
# Get name of this section.
|
@@ -5,6 +5,7 @@ require 'elftools/sections/section'
|
|
5
5
|
require 'elftools/sections/dynamic_section'
|
6
6
|
require 'elftools/sections/note_section'
|
7
7
|
require 'elftools/sections/null_section'
|
8
|
+
require 'elftools/sections/relocation_section'
|
8
9
|
require 'elftools/sections/str_tab_section'
|
9
10
|
require 'elftools/sections/sym_tab_section'
|
10
11
|
|
@@ -14,7 +15,7 @@ module ELFTools
|
|
14
15
|
# Class methods of {Sections::Section}.
|
15
16
|
class << Section
|
16
17
|
# Use different class according to +header.sh_type+.
|
17
|
-
# @param [ELFTools::ELF_Shdr] header Section header.
|
18
|
+
# @param [ELFTools::Structs::ELF_Shdr] header Section header.
|
18
19
|
# @param [File] stream Streaming object.
|
19
20
|
# @return [ELFTools::Sections::Section]
|
20
21
|
# Return object dependes on +header.sh_type+.
|
@@ -23,6 +24,7 @@ module ELFTools
|
|
23
24
|
when Constants::SHT_DYNAMIC then DynamicSection
|
24
25
|
when Constants::SHT_NULL then NullSection
|
25
26
|
when Constants::SHT_NOTE then NoteSection
|
27
|
+
when Constants::SHT_RELA, Constants::SHT_REL then RelocationSection
|
26
28
|
when Constants::SHT_STRTAB then StrTabSection
|
27
29
|
when Constants::SHT_SYMTAB, Constants::SHT_DYNSYM then SymTabSection
|
28
30
|
else Section
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'elftools/sections/section'
|
2
|
+
require 'elftools/util'
|
2
3
|
|
3
4
|
module ELFTools
|
4
5
|
module Sections
|
@@ -11,16 +12,7 @@ module ELFTools
|
|
11
12
|
# Usually from +shdr.sh_name+ or +sym.st_name+.
|
12
13
|
# @return [String] The name without null bytes.
|
13
14
|
def name_at(offset)
|
14
|
-
stream
|
15
|
-
# read until "\x00"
|
16
|
-
ret = ''
|
17
|
-
loop do
|
18
|
-
c = stream.read(1)
|
19
|
-
return nil if c.nil? # reach EOF
|
20
|
-
break if c == "\x00"
|
21
|
-
ret += c
|
22
|
-
end
|
23
|
-
ret
|
15
|
+
Util.cstring(stream, header.sh_offset + offset)
|
24
16
|
end
|
25
17
|
end
|
26
18
|
end
|
@@ -9,7 +9,7 @@ module ELFTools
|
|
9
9
|
# Instantiate a {SymTabSection} object.
|
10
10
|
# There's a +section_at+ lambda for {SymTabSection}
|
11
11
|
# to easily fetch other sections.
|
12
|
-
# @param [ELFTools::ELF_Shdr] header
|
12
|
+
# @param [ELFTools::Structs::ELF_Shdr] header
|
13
13
|
# See {Section#initialize} for more information.
|
14
14
|
# @param [File] stream
|
15
15
|
# See {Section#initialize} for more information.
|
@@ -36,7 +36,7 @@ module ELFTools
|
|
36
36
|
#
|
37
37
|
# Symbols are lazy loaded.
|
38
38
|
# @param [Integer] n The index.
|
39
|
-
# @return [ELFTools
|
39
|
+
# @return [ELFTools::Symbol, NilClass]
|
40
40
|
# The target symbol.
|
41
41
|
# If +n+ is out of bound, +nil+ is returned.
|
42
42
|
def symbol_at(n)
|
@@ -87,20 +87,21 @@ module ELFTools
|
|
87
87
|
|
88
88
|
def create_symbol(n)
|
89
89
|
stream.pos = header.sh_offset + n * header.sh_entsize
|
90
|
-
sym = ELF_sym[header.elf_class].new(endian: header.class.self_endian)
|
90
|
+
sym = Structs::ELF_sym[header.elf_class].new(endian: header.class.self_endian)
|
91
91
|
sym.read(stream)
|
92
92
|
Symbol.new(sym, stream, symstr: method(:symstr))
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
96
|
# Class of symbol.
|
97
|
+
#
|
97
98
|
# XXX: Should this class be defined in an independent file?
|
98
99
|
class Symbol
|
99
|
-
attr_reader :header # @return [ELFTools::ELF32_sym, ELFTools::ELF64_sym] Section header.
|
100
|
+
attr_reader :header # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] Section header.
|
100
101
|
attr_reader :stream # @return [File] Streaming object.
|
101
102
|
|
102
103
|
# Instantiate a {ELFTools::Symbol} object.
|
103
|
-
# @param [ELFTools::ELF32_sym, ELFTools::ELF64_sym] header
|
104
|
+
# @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] header
|
104
105
|
# The symbol header.
|
105
106
|
# @param [File] stream The streaming object.
|
106
107
|
# @param [ELFTools::Sections::StrTabSection, Proc] symstr
|
@@ -2,17 +2,27 @@ module ELFTools
|
|
2
2
|
module Segments
|
3
3
|
# Base class of segments.
|
4
4
|
class Segment
|
5
|
-
attr_reader :header # @return [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] Program header.
|
5
|
+
attr_reader :header # @return [ELFTools::Structs::ELF32_Phdr, ELFTools::Structs::ELF64_Phdr] Program header.
|
6
6
|
attr_reader :stream # @return [File] Streaming object.
|
7
7
|
|
8
8
|
# Instantiate a {Segment} object.
|
9
|
-
# @param [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] header
|
9
|
+
# @param [ELFTools::Structs::ELF32_Phdr, ELFTools::Structs::ELF64_Phdr] header
|
10
10
|
# Program header.
|
11
11
|
# @param [File] stream
|
12
12
|
# Streaming object.
|
13
|
-
|
13
|
+
# @param [Method] offset_from_vma
|
14
|
+
# The method to get offset of file, given virtual memory address.
|
15
|
+
def initialize(header, stream, offset_from_vma: nil)
|
14
16
|
@header = header
|
15
17
|
@stream = stream
|
18
|
+
@offset_from_vma = offset_from_vma
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return +header.p_type+ in a simplier way.
|
22
|
+
# @return [Integer]
|
23
|
+
# The type, meaning of types are defined in {Constants::PT}.
|
24
|
+
def type
|
25
|
+
header.p_type
|
16
26
|
end
|
17
27
|
|
18
28
|
# The content in this segment.
|
@@ -12,7 +12,7 @@ module ELFTools
|
|
12
12
|
# Class methods of {Segments::Segment}.
|
13
13
|
class << Segment
|
14
14
|
# Use different class according to +header.p_type+.
|
15
|
-
# @param [ELFTools::ELF32_Phdr, ELFTools::ELF64_Phdr] header Program header of a segment.
|
15
|
+
# @param [ELFTools::Structs::ELF32_Phdr, ELFTools::Structs::ELF64_Phdr] header Program header of a segment.
|
16
16
|
# @param [File] stream Streaming object.
|
17
17
|
# @return [ELFTools::Segments::Segment]
|
18
18
|
# Return object dependes on +header.p_type+.
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
module ELFTools
|
3
|
+
# Define ELF related structures in this module.
|
4
|
+
#
|
5
|
+
# Structures are fetch from https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h.
|
6
|
+
# Using the bindata gem to make these structures support 32/64 bits and
|
7
|
+
# little/big endian simultaneously.
|
8
|
+
module Structs
|
9
|
+
# The base structure to define common methods.
|
10
|
+
class ELFStruct < BinData::Record
|
11
|
+
CHOICE_SIZE_T = {
|
12
|
+
selection: :elf_class, choices: { 32 => :uint32, 64 => :uint64 }
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
attr_accessor :elf_class # @return [Integer] 32 or 64.
|
16
|
+
|
17
|
+
# Hacking to get endian of current class
|
18
|
+
# @return [Symbol, NilClass] +:little+ or +:big+.
|
19
|
+
def self.self_endian
|
20
|
+
bindata_name[-2..-1] == 'ge' ? :big : :little
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# ELF header structure.
|
25
|
+
class ELF_Ehdr < ELFStruct
|
26
|
+
endian :big_and_little
|
27
|
+
struct :e_ident do
|
28
|
+
string :magic, read_length: 4
|
29
|
+
int8 :ei_class
|
30
|
+
int8 :ei_data
|
31
|
+
int8 :ei_version
|
32
|
+
int8 :ei_osabi
|
33
|
+
int8 :ei_abiversion
|
34
|
+
string :ei_padding, read_length: 7 # no use
|
35
|
+
end
|
36
|
+
uint16 :e_type
|
37
|
+
uint16 :e_machine
|
38
|
+
uint32 :e_version
|
39
|
+
# entry point
|
40
|
+
choice :e_entry, **CHOICE_SIZE_T
|
41
|
+
choice :e_phoff, **CHOICE_SIZE_T
|
42
|
+
choice :e_shoff, **CHOICE_SIZE_T
|
43
|
+
uint32 :e_flags
|
44
|
+
uint16 :e_ehsize # size of this header
|
45
|
+
uint16 :e_phentsize # size of each segment
|
46
|
+
uint16 :e_phnum # number of segments
|
47
|
+
uint16 :e_shentsize # size of each section
|
48
|
+
uint16 :e_shnum # number of sections
|
49
|
+
uint16 :e_shstrndx # index of string table section
|
50
|
+
end
|
51
|
+
|
52
|
+
# Section header structure.
|
53
|
+
class ELF_Shdr < ELFStruct
|
54
|
+
endian :big_and_little
|
55
|
+
uint32 :sh_name
|
56
|
+
uint32 :sh_type
|
57
|
+
choice :sh_flags, **CHOICE_SIZE_T
|
58
|
+
choice :sh_addr, **CHOICE_SIZE_T
|
59
|
+
choice :sh_offset, **CHOICE_SIZE_T
|
60
|
+
choice :sh_size, **CHOICE_SIZE_T
|
61
|
+
uint32 :sh_link
|
62
|
+
uint32 :sh_info
|
63
|
+
choice :sh_addralign, **CHOICE_SIZE_T
|
64
|
+
choice :sh_entsize, **CHOICE_SIZE_T
|
65
|
+
end
|
66
|
+
|
67
|
+
# Program header structure for 32bit.
|
68
|
+
class ELF32_Phdr < ELFStruct
|
69
|
+
endian :big_and_little
|
70
|
+
uint32 :p_type
|
71
|
+
uint32 :p_offset
|
72
|
+
uint32 :p_vaddr
|
73
|
+
uint32 :p_paddr
|
74
|
+
uint32 :p_filesz
|
75
|
+
uint32 :p_memsz
|
76
|
+
uint32 :p_flags
|
77
|
+
uint32 :p_align
|
78
|
+
end
|
79
|
+
|
80
|
+
# Program header structure for 64bit.
|
81
|
+
class ELF64_Phdr < ELFStruct
|
82
|
+
endian :big_and_little
|
83
|
+
uint32 :p_type
|
84
|
+
uint32 :p_flags
|
85
|
+
uint64 :p_offset
|
86
|
+
uint64 :p_vaddr
|
87
|
+
uint64 :p_paddr
|
88
|
+
uint64 :p_filesz
|
89
|
+
uint64 :p_memsz
|
90
|
+
uint64 :p_align
|
91
|
+
end
|
92
|
+
ELF_Phdr = {
|
93
|
+
32 => ELF32_Phdr,
|
94
|
+
64 => ELF64_Phdr
|
95
|
+
}.freeze
|
96
|
+
|
97
|
+
# Symbol structure for 32bit.
|
98
|
+
class ELF32_sym < ELFStruct
|
99
|
+
endian :big_and_little
|
100
|
+
uint32 :st_name
|
101
|
+
uint32 :st_value
|
102
|
+
uint32 :st_size
|
103
|
+
uint8 :st_info
|
104
|
+
uint8 :st_other
|
105
|
+
uint16 :st_shndx
|
106
|
+
end
|
107
|
+
|
108
|
+
# Symbol structure for 64bit.
|
109
|
+
class ELF64_sym < ELFStruct
|
110
|
+
endian :big_and_little
|
111
|
+
uint32 :st_name # Symbol name, index in string tbl
|
112
|
+
uint8 :st_info # Type and binding attributes
|
113
|
+
uint8 :st_other # No defined meaning, 0
|
114
|
+
uint16 :st_shndx # Associated section index
|
115
|
+
uint64 :st_value # Value of the symbol
|
116
|
+
uint64 :st_size # Associated symbol size
|
117
|
+
end
|
118
|
+
ELF_sym = {
|
119
|
+
32 => ELF32_sym,
|
120
|
+
64 => ELF64_sym
|
121
|
+
}.freeze
|
122
|
+
|
123
|
+
# Note header.
|
124
|
+
class ELF_Nhdr < ELFStruct
|
125
|
+
endian :big_and_little
|
126
|
+
uint32 :n_namesz # Name size
|
127
|
+
uint32 :n_descsz # Content size
|
128
|
+
uint32 :n_type # Content type
|
129
|
+
end
|
130
|
+
|
131
|
+
# Dynamic tag header.
|
132
|
+
class ELF_Dyn < ELFStruct
|
133
|
+
endian :big_and_little
|
134
|
+
choice :d_tag, selection: :elf_class, choices: { 32 => :int32, 64 => :int64 }
|
135
|
+
# This is an union type named +d_un+ in original source,
|
136
|
+
# simplify it to be +d_val+ here.
|
137
|
+
choice :d_val, **CHOICE_SIZE_T
|
138
|
+
end
|
139
|
+
|
140
|
+
# Rel header in .rel section.
|
141
|
+
class ELF_Rel < ELFStruct
|
142
|
+
endian :big_and_little
|
143
|
+
choice :r_offset, **CHOICE_SIZE_T
|
144
|
+
choice :r_info, **CHOICE_SIZE_T
|
145
|
+
end
|
146
|
+
|
147
|
+
# Rela header in .rela section.
|
148
|
+
class ELF_Rela < ELFStruct
|
149
|
+
endian :big_and_little
|
150
|
+
choice :r_offset, **CHOICE_SIZE_T
|
151
|
+
choice :r_info, **CHOICE_SIZE_T
|
152
|
+
choice :r_addend, selection: :elf_class, choices: { 32 => :int32, 64 => :int64 }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/elftools/util.rb
CHANGED
@@ -45,6 +45,48 @@ module ELFTools
|
|
45
45
|
raise ArgumentError, "No constants in #{module_name} named \"#{val}\"" unless mod.const_defined?(val)
|
46
46
|
mod.const_get(val)
|
47
47
|
end
|
48
|
+
|
49
|
+
# Read from stream until reach a null-byte.
|
50
|
+
# @param [File] stream Streaming object
|
51
|
+
# @param [Integer] offset Start from here.
|
52
|
+
# @return [String] Result string will never contain null byte.
|
53
|
+
# @example
|
54
|
+
# Util.cstring(File.open('/bin/cat'), 0)
|
55
|
+
# #=> "\x7FELF\x02\x01\x01"
|
56
|
+
def cstring(stream, offset)
|
57
|
+
stream.pos = offset
|
58
|
+
# read until "\x00"
|
59
|
+
ret = ''
|
60
|
+
loop do
|
61
|
+
c = stream.read(1)
|
62
|
+
return nil if c.nil? # reach EOF
|
63
|
+
break if c == "\x00"
|
64
|
+
ret += c
|
65
|
+
end
|
66
|
+
ret
|
67
|
+
end
|
68
|
+
|
69
|
+
# Select objects from enumerator with +.type+ property
|
70
|
+
# equals to +type+.
|
71
|
+
#
|
72
|
+
# Different from naive +Array#select+ is this method
|
73
|
+
# will yield block whenever find a desired object.
|
74
|
+
#
|
75
|
+
# This method is used to simplify the same logic in methods
|
76
|
+
# {ELFFile#sections_by_type}, {ELFFile#segments_by_type}, etc.
|
77
|
+
# @param [Enumerator] enum An enumerator for further select.
|
78
|
+
# @param [Object] type The type you want.
|
79
|
+
# @return [Array<Object>]
|
80
|
+
# The return value will be objects in +enum+ with attribute
|
81
|
+
# +.type+ equals to +type+.
|
82
|
+
def select_by_type(enum, type)
|
83
|
+
enum.select do |sec|
|
84
|
+
if sec.type == type
|
85
|
+
yield sec if block_given?
|
86
|
+
true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
48
90
|
end
|
49
91
|
extend ClassMethods
|
50
92
|
end
|
data/lib/elftools/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elftools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bindata
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- lib/elftools/sections/dynamic_section.rb
|
115
115
|
- lib/elftools/sections/note_section.rb
|
116
116
|
- lib/elftools/sections/null_section.rb
|
117
|
+
- lib/elftools/sections/relocation_section.rb
|
117
118
|
- lib/elftools/sections/section.rb
|
118
119
|
- lib/elftools/sections/sections.rb
|
119
120
|
- lib/elftools/sections/str_tab_section.rb
|
@@ -123,7 +124,7 @@ files:
|
|
123
124
|
- lib/elftools/segments/note_segment.rb
|
124
125
|
- lib/elftools/segments/segment.rb
|
125
126
|
- lib/elftools/segments/segments.rb
|
126
|
-
- lib/elftools/
|
127
|
+
- lib/elftools/structs.rb
|
127
128
|
- lib/elftools/util.rb
|
128
129
|
- lib/elftools/version.rb
|
129
130
|
homepage: https://github.com/david942j/rbelftools
|
data/lib/elftools/structures.rb
DELETED
@@ -1,133 +0,0 @@
|
|
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
|