Class: Rubycc::ObjFile::RelocatableWriter

Inherits:
Object
  • Object
show all
Defined in:
lib/rubycc/objfile/relocatable_writer.rb

Overview

Writes a general ELF64 relocatable object (ET_REL) for Linux x86_64: arbitrary named sections, an arbitrary symbol table, and full SHT_RELA relocation tables carrying any relocation type and addend.

It is the counterpart the linker's ld -r core needs and deliberately NOT the compiler's ELFWriter. That writer serves a single translation unit through a fixed menu — .text/.rodata/.data/.bss and three baked-in relocation kinds — because a compiler always emits the same shape. A merged object is open-ended: it may hold any section the inputs carried (.comment, .note.*, .eh_frame, ...), section symbols pointing at merged sections, and relocations of any numeric type against any symbol with any addend. Rather than contort the compiler's writer, this one accepts those pieces directly and lays them out.

The API is build-then-emit: #add_section, #add_symbol and #add_relocation return opaque handles (the value structs below) that later calls reference by identity, so the caller never computes on-disk indices itself. #to_binary fixes the section order (NULL, the content sections in insertion order, one .rela. per relocated target, then .symtab/.strtab/.shstrtab), resolves every cross-reference (symbol st_shndx, rela sh_link/sh_info, r_info's symbol index) against that order, and concatenates the image.

Two invariants the caller relies on: symbols are emitted with every STB_LOCAL before the first non-local (a stable partition of insertion order, so relocation handles stay valid) and sh_info of .symtab lands on the first global; output is fully deterministic (N4) — identical build calls yield byte-identical bytes, with no timestamps embedded.

Defined Under Namespace

Classes: LaidOut, Relocation, Section, Symbol

Constant Summary collapse

ELFCLASS64 =
2
ELFDATA2LSB =
1
EV_CURRENT =
1
ET_REL =
1
EM_X86_64 =
62
EM_AARCH64 =
183
SHN_UNDEF =
0
SHN_ABS =
0xFFF1
SHT_SYMTAB =
2
SHT_STRTAB =
3
SHT_RELA =
4
SHT_NOBITS =
8
0x40
BINDINGS =

Symbolic bind/type/visibility accepted from the caller, mapped to their st_info / st_other encodings. A caller may also pass the raw integer, which passes through unchanged.

{ local: 0, global: 1, weak: 2 }.freeze
TYPES =
{ notype: 0, object: 1, func: 2, section: 3, file: 4, tls: 6, ifunc: 10 }.freeze
VISIBILITIES =
{ default: 0, internal: 1, hidden: 2, protected: 3 }.freeze
SYM_ENTSIZE =
24
RELA_ENTSIZE =
24
SHDR_ENTSIZE =
64
EHDR_SIZE =
64

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(machine: EM_X86_64) ⇒ RelocatableWriter

machine is the ELF e_machine value stamped into the output header. It defaults to x86_64 so an existing caller is unaffected; the partial-link merge passes the inputs' own machine through so a merged aarch64 object keeps its EM_AARCH64 identity (the final linker reads it back to pick the target's relocation and crt logic).



88
89
90
91
92
93
94
95
96
97
# File 'lib/rubycc/objfile/relocatable_writer.rb', line 88

def initialize(machine: EM_X86_64)
  @machine = machine
  @sections = []
  # The reserved null symbol is index 0 by the ELF ABI; expose it so a
  # caller can re-point a "no symbol" relocation at it.
  @null_symbol = Symbol.new(name: nil, bind: 0, type: 0, visibility: 0,
                            section: nil, shndx: SHN_UNDEF, value: 0, size: 0)
  @symbols = [@null_symbol]
  @relocations = []
end

Instance Attribute Details

#null_symbolObject (readonly)

Returns the value of attribute null_symbol.



99
100
101
# File 'lib/rubycc/objfile/relocatable_writer.rb', line 99

def null_symbol
  @null_symbol
end

Instance Method Details

#add_relocation(target:, offset:, symbol:, type:, addend:) ⇒ Object

Records a relocation to emit into target's .rela table.



125
126
127
128
129
# File 'lib/rubycc/objfile/relocatable_writer.rb', line 125

def add_relocation(target:, offset:, symbol:, type:, addend:)
  @relocations << Relocation.new(target: target, offset: offset, symbol: symbol,
                                 type: type, addend: addend)
  self
end

#add_section(name:, type:, flags:, addralign:, entsize: 0, data: nil, size: nil) ⇒ Object

Registers a content section. type/flags/entsize are raw ELF values; data (bytes) or, for a NOBITS section, size gives its extent. Returns the Section handle to reference from symbols and relocations.



104
105
106
107
108
109
110
# File 'lib/rubycc/objfile/relocatable_writer.rb', line 104

def add_section(name:, type:, flags:, addralign:, entsize: 0, data: nil, size: nil)
  section = Section.new(name: name, type: type, flags: flags,
                        addralign: [addralign, 1].max, entsize: entsize,
                        data: data&.b, size: size)
  @sections << section
  section
end

#add_symbol(name:, bind:, type:, visibility: :default, section: nil, shndx: SHN_UNDEF, value: 0, size: 0) ⇒ Object

Registers a symbol. bind/type/visibility may be the symbolic forms (:local, :func, :hidden, ...) or raw integers. Returns the Symbol handle.



114
115
116
117
118
119
120
121
122
# File 'lib/rubycc/objfile/relocatable_writer.rb', line 114

def add_symbol(name:, bind:, type:, visibility: :default, section: nil, shndx: SHN_UNDEF, value: 0, size: 0)
  symbol = Symbol.new(
    name: name, bind: BINDINGS.fetch(bind, bind), type: TYPES.fetch(type, type),
    visibility: VISIBILITIES.fetch(visibility, visibility),
    section: section, shndx: shndx, value: value, size: size
  )
  @symbols << symbol
  symbol
end

#to_binaryObject

Assembles and returns the object as an ASCII-8BIT String.



132
133
134
135
136
# File 'lib/rubycc/objfile/relocatable_writer.rb', line 132

def to_binary
  order_symbols
  layout = build_layout
  assemble(layout)
end

#write(path) ⇒ Object



138
139
140
# File 'lib/rubycc/objfile/relocatable_writer.rb', line 138

def write(path)
  File.binwrite(path, to_binary)
end