Class: Rubycc::ObjFile::ELFReader
- Inherits:
-
Object
- Object
- Rubycc::ObjFile::ELFReader
- Defined in:
- lib/rubycc/objfile/elf_reader.rb
Overview
Reads an ELF64 little-endian x86_64 object for the linker. Two shapes are
supported: a relocatable object (ET_REL, a compiler-emitted .o) is fully
parsed — sections with their raw bytes, the .symtab, and every .rela.*
relocation table; a shared object (ET_DYN, a .so) is read for its
dynamic-linking interface — the .dynsym exported/imported symbols and the
.dynamic array's DT_SONAME / DT_NEEDED strings.
This is the counterpart of ELFWriter and is designed to round-trip
everything that writer emits (see test/test_elf_reader.rb): the parsed
Section/Symbol/Relocation value objects carry exactly the fields the writer
sets, resolved back from their on-disk encoding (section names via
.shstrtab, symbol names via the linked string table, relocation targets
via r_info's symbol index and the RELA section's sh_info).
The model is load-then-query: read/read_file parse the whole image up
front into arrays and name-indexed lookups so the linker can address any
section, symbol, or relocation randomly rather than through a callback
stream. Nothing here writes or mutates an object.
For a .so the dynamic tables are located strictly through the section
header table (.dynsym / .dynstr / .dynamic by sh_type, sized by
sh_size / sh_entsize). The allocated dynamic sections survive strip,
so a section-header-less .so is rare enough that the PT_DYNAMIC program
header fallback (deriving the symbol count from DT_HASH / DT_GNU_HASH) is
deliberately not built here — YAGNI until a real input demands it.
Defined Under Namespace
Classes: DynamicEntry, RawSection, Relocation, RelocationSection, Section, Symbol, VersionDefinition
Constant Summary collapse
- ELFMAG =
ELF identification (e_ident) and header field encodings.
"\x7FELF".b
- EI_CLASS =
4- EI_DATA =
5- EI_VERSION =
6- ELFCLASS64 =
2- ELFDATA2LSB =
1- EV_CURRENT =
1- EHDR_SIZE =
64- SHDR_SIZE =
64- ET_REL =
e_type values the reader consumes; others are reported numerically and rejected by #read. ET_EXEC is read back like a shared object — through the allocated .dynsym / .dynamic tables located by the section header table — so the executable writer's output can be inspected the same way.
1- ET_EXEC =
2- ET_DYN =
3- EM_X86_64 =
The machines this reader understands. Both are little-endian ELF64, so the whole header/section/symbol layer is shared; only the relocation type names differ (see RELOC_TYPES).
62- EM_AARCH64 =
183- SUPPORTED_MACHINES =
[EM_X86_64, EM_AARCH64].freeze
- SHT_NULL =
Section header types.
0- SHT_PROGBITS =
1- SHT_SYMTAB =
2- SHT_STRTAB =
3- SHT_RELA =
4- SHT_DYNAMIC =
6- SHT_NOBITS =
8- SHT_DYNSYM =
11- SHT_GNU_VERDEF =
The version-definition table (.gnu.version_d): the symbol versions this object defines. Its type lives in the gABI's OS-specific range (SHT_LOOS = 0x60000000 upwards), which is why the number looks nothing like the ones above.
0x6FFFFFFD- SHN_UNDEF =
Reserved section indices that a symbol's st_shndx may carry instead of a real section number: an undefined (imported) symbol, an absolute value, and a not-yet-allocated COMMON block.
0- SHN_LORESERVE =
0xFF00- SHN_ABS =
0xFFF1- SHN_COMMON =
0xFFF2- SHN_XINDEX =
0xFFFF- SYM_ENTSIZE =
24- RELA_ENTSIZE =
24- DYN_ENTSIZE =
16- VERDEF_SIZE =
An Elf64_Verdef record and the Elf64_Verdaux records behind it. Neither is an array element: each carries its own byte distance to the next one (vd_next / vda_next), so these sizes bound a single record rather than stride a table (see #parse_version_definitions).
20- VERDAUX_SIZE =
8- VER_DEF_CURRENT =
The only vd_version (VER_DEF_CURRENT) this layout is defined for; a record announcing another revision is not the structure decoded below.
1- VER_FLG_BASE =
vd_flags bits: the definition that names the object itself rather than a version its symbols can bind to, and one whose version holds no symbols.
0x1- VER_FLG_WEAK =
0x2- SYM_BINDINGS =
Symbol binding (st_info >> 4) and type (st_info & 0xF), mapped to symbols for readable queries; an unrecognized value passes through as its integer.
{ 0 => :local, 1 => :global, 2 => :weak }.freeze
- SYM_TYPES =
{ 0 => :notype, 1 => :object, 2 => :func, 3 => :section, 4 => :file, 6 => :tls, 10 => :ifunc }.freeze
- SYM_VISIBILITIES =
Symbol visibility (st_other & 0x3).
{ 0 => :default, 1 => :internal, 2 => :hidden, 3 => :protected }.freeze
- RELOC_TYPES =
Relocation types (r_info & 0xFFFFFFFF), keyed by e_machine because the numbering is per-architecture. The ones the toolchain emits are named; any other type is preserved numerically (its name is nil) rather than rejected, so an unfamiliar object is still fully readable. The x86_64 GOTPCREL family addresses a symbol's Global Offset Table slot: 9 is the plain PC-relative GOT reference, and 41/42 its "relaxable" forms the psABI defines for a
movthat a linker may rewrite in place to alea(42 is the REX-prefixed form a 64-bitmov rax, sym@GOTPCREL(%rip)uses).On aarch64 the picture is different because that machine forms an address in two instructions: CALL26 is the 26-bit branch immediate of a
bl/bto a named symbol, ABS64 an absolute 64-bit pointer slot, and the remaining four come in pairs. ADR_PREL_PG_HI21 carries the 21-bit page distance of anadrpand ADD_ABS_LO12_NC the 12-bit within-page offset of theaddbehind it; ADR_GOT_PAGE and LD64_GOT_LO12_NC are the same split applied to a symbol's Global Offset Table slot, the second patching the scaled immediate of theldrthat reads it. { EM_X86_64 => { 1 => :R_X86_64_64, 2 => :R_X86_64_PC32, 4 => :R_X86_64_PLT32, 9 => :R_X86_64_GOTPCREL, 10 => :R_X86_64_32, 11 => :R_X86_64_32S, 41 => :R_X86_64_GOTPCRELX, 42 => :R_X86_64_REX_GOTPCRELX }.freeze, EM_AARCH64 => { 257 => :R_AARCH64_ABS64, 275 => :R_AARCH64_ADR_PREL_PG_HI21, 277 => :R_AARCH64_ADD_ABS_LO12_NC, 283 => :R_AARCH64_CALL26, 311 => :R_AARCH64_ADR_GOT_PAGE, 312 => :R_AARCH64_LD64_GOT_LO12_NC }.freeze }.freeze
- DT_NULL =
Dynamic array tags (.dynamic) this reader interprets: the terminator, the DT_NEEDED shared-library dependencies, and the DT_SONAME of the object itself. Every tag's raw value is still exposed; only these drive behavior.
0- DT_NEEDED =
1- DT_SONAME =
14
Instance Attribute Summary collapse
-
#dynamic_entries ⇒ Object
readonly
Returns the value of attribute dynamic_entries.
-
#dynamic_symbols ⇒ Object
readonly
Returns the value of attribute dynamic_symbols.
-
#entry ⇒ Object
readonly
Returns the value of attribute entry.
-
#machine ⇒ Object
readonly
Returns the value of attribute machine.
-
#needed ⇒ Object
readonly
Returns the value of attribute needed.
-
#relocation_sections ⇒ Object
readonly
Returns the value of attribute relocation_sections.
-
#sections ⇒ Object
readonly
Returns the value of attribute sections.
-
#soname ⇒ Object
readonly
Returns the value of attribute soname.
-
#symbols ⇒ Object
readonly
Returns the value of attribute symbols.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
-
#version_definitions ⇒ Object
readonly
Returns the value of attribute version_definitions.
Class Method Summary collapse
-
.read(bytes) ⇒ Object
Parses an in-memory ELF image (an ASCII-8BIT String) and returns a ready-to-query ELFReader.
-
.read_file(path) ⇒ Object
Reads a file from disk and parses it.
Instance Method Summary collapse
-
#dynamic_symbol(name) ⇒ Object
The first dynamic symbol with the given name in .dynsym, or nil — the export query for a shared object ("is printf here and defined?").
- #executable? ⇒ Boolean
-
#initialize(bytes) ⇒ ELFReader
constructor
A new instance of ELFReader.
- #parse! ⇒ Object
- #relocatable? ⇒ Boolean
-
#relocations_for(target_name) ⇒ Object
The relocations that patch the named section (e.g. "relocations_for('.text')" yields the .rela.text entries), or an empty array.
-
#section(name) ⇒ Object
The first section with the given name, or nil.
- #shared_object? ⇒ Boolean
-
#symbol(name) ⇒ Object
The first symbol with the given name in .symtab, or nil.
Constructor Details
#initialize(bytes) ⇒ ELFReader
Returns a new instance of ELFReader.
235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 235 def initialize(bytes) @data = bytes.b @sections = [] @symbols = [] @relocation_sections = [] @dynamic_symbols = [] @dynamic_entries = [] @needed = [] @soname = nil @version_definitions = [] end |
Instance Attribute Details
#dynamic_entries ⇒ Object (readonly)
Returns the value of attribute dynamic_entries.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def dynamic_entries @dynamic_entries end |
#dynamic_symbols ⇒ Object (readonly)
Returns the value of attribute dynamic_symbols.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def dynamic_symbols @dynamic_symbols end |
#entry ⇒ Object (readonly)
Returns the value of attribute entry.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def entry @entry end |
#machine ⇒ Object (readonly)
Returns the value of attribute machine.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def machine @machine end |
#needed ⇒ Object (readonly)
Returns the value of attribute needed.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def needed @needed end |
#relocation_sections ⇒ Object (readonly)
Returns the value of attribute relocation_sections.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def relocation_sections @relocation_sections end |
#sections ⇒ Object (readonly)
Returns the value of attribute sections.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def sections @sections end |
#soname ⇒ Object (readonly)
Returns the value of attribute soname.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def soname @soname end |
#symbols ⇒ Object (readonly)
Returns the value of attribute symbols.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def symbols @symbols end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def type @type end |
#version_definitions ⇒ Object (readonly)
Returns the value of attribute version_definitions.
231 232 233 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 231 def version_definitions @version_definitions end |
Class Method Details
.read(bytes) ⇒ Object
Parses an in-memory ELF image (an ASCII-8BIT String) and returns a ready-to-query ELFReader.
221 222 223 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 221 def read(bytes) new(bytes).tap(&:parse!) end |
.read_file(path) ⇒ Object
Reads a file from disk and parses it. Convenience over read(File.binread).
226 227 228 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 226 def read_file(path) read(File.binread(path)) end |
Instance Method Details
#dynamic_symbol(name) ⇒ Object
The first dynamic symbol with the given name in .dynsym, or nil — the export query for a shared object ("is printf here and defined?").
276 277 278 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 276 def dynamic_symbol(name) @dynamic_symbols.find { |sym| sym.name == name } end |
#executable? ⇒ Boolean
259 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 259 def executable? = @type == ET_EXEC |
#parse! ⇒ Object
247 248 249 250 251 252 253 254 255 256 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 247 def parse! parse_header parse_sections @symbols = symbol_table_by_index[symtab_section&.index] || [] parse_relocations parse_dynamic_symbols parse_dynamic parse_version_definitions self end |
#relocatable? ⇒ Boolean
258 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 258 def relocatable? = @type == ET_REL |
#relocations_for(target_name) ⇒ Object
The relocations that patch the named section (e.g. "relocations_for('.text')" yields the .rela.text entries), or an empty array.
282 283 284 285 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 282 def relocations_for(target_name) rs = @relocation_sections.find { |r| r.target&.name == target_name } rs ? rs.relocations : [] end |
#section(name) ⇒ Object
The first section with the given name, or nil. Section names are not unique in ELF, but the ones this reader is asked about (.text, .symtab, .rela.text, .dynsym, ...) are, so first-match is the useful lookup.
265 266 267 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 265 def section(name) @sections.find { |sec| sec.name == name } end |
#shared_object? ⇒ Boolean
260 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 260 def shared_object? = @type == ET_DYN |
#symbol(name) ⇒ Object
The first symbol with the given name in .symtab, or nil.
270 271 272 |
# File 'lib/rubycc/objfile/elf_reader.rb', line 270 def symbol(name) @symbols.find { |sym| sym.name == name } end |