Class: Rubycc::ObjFile::ELFWriter
- Inherits:
-
Object
- Object
- Rubycc::ObjFile::ELFWriter
- Defined in:
- lib/rubycc/objfile/elf_writer.rb
Overview
Writes a minimal ELF64 relocatable object (ET_REL) for Linux x86_64.
Section layout (in this order): NULL, .text, .rodata (only with string
literals), .data (only with initialized globals), .bss (only with
zero-initialized globals), .rela.text (only when there are text
relocations), .rela.data (only when a .data pointer slot needs one),
.note.GNU-stack, .symtab, .strtab, .shstrtab. Section indices are not
hard-coded: the ordered name list is assembled in #to_binary and a
name -> index lookup resolves the cross-references (symtab's sh_link,
rela's sh_link/sh_info, and each symbol's st_shndx). Symbol table order is
NULL, STT_FILE, the .text section symbol, the .rodata section symbol (when
present), then the file-local (static) functions and objects — every
STB_LOCAL must precede the first STB_GLOBAL — followed by the external
symbols (defined functions, then defined file-scope objects, then
undefined externals); r_info in .rela.text indexes into that final order.
.rela.text carries five machine-independent relocation kinds — :call,
:func, :string, :global and :got — each translated through the injected
machine description (see MachineDescription) into one or more concrete ELF
relocation entries. On the default x86_64 machine each kind costs exactly
one entry: a call site as an R_X86_64_PLT32 against its (defined or
undefined) symbol, a taken function address the same way, a string
reference as an R_X86_64_PC32 against the .rodata section symbol with the
string's byte offset as its addend, a global reference as an
R_X86_64_PC32 against that global's own object symbol, and a PIC GOT
reference (a "-fPIC" access to a symbol this unit does not define) as an
R_X86_64_REX_GOTPCRELX against that symbol — all four PC-relative kinds
biased by -4 for the rel32 field's placement. On aarch64 an address-forming
kind instead costs a pair of entries, one per instruction of the adrp/add
(or adrp/ldr) sequence that machine needs; the writer emits whatever the
description lists.
.rela.data carries the absolute R_X86_64_64 relocations that patch a .data pointer slot: one against another object's symbol (addend 0) for a "&other" or a decayed global array, and one against the .rodata section symbol (the string's byte offset as its addend) for a string-literal pointer.
.data holds the initialized globals' little-endian bytes; .bss is a NOBITS section that reserves space for the zero-initialized ones without occupying any file bytes. Both are writable (SHF_WRITE | SHF_ALLOC).
The empty .note.GNU-stack marks the stack as non-executable so the linker does not warn about a missing GNU_STACK note. Output is fully deterministic (N4): no timestamps or other varying data are embedded.
Defined Under Namespace
Classes: MachineDescription, RelocDesc
Constant Summary collapse
- ELFCLASS64 =
ELF constants
2- ELFDATA2LSB =
1- EV_CURRENT =
1- ET_REL =
1- EM_X86_64 =
62- EM_AARCH64 =
183- SHN_UNDEF =
0- SHN_ABS =
0xFFF1- SHT_NULL =
Section header types
0- SHT_PROGBITS =
1- SHT_SYMTAB =
2- SHT_STRTAB =
3- SHT_RELA =
4- SHT_NOBITS =
8- SHT_INIT_ARRAY =
The two array section types the runtime walks: SHT_INIT_ARRAY holds pointers to the constructors it calls at startup/dlopen, SHT_FINI_ARRAY the destructors it calls at exit/dlclose. Distinct types (rather than PROGBITS with a magic name) because the linker groups them by type and advertises each run through DT_INIT_ARRAY / DT_FINI_ARRAY.
14- SHT_FINI_ARRAY =
15- SHF_WRITE =
Section header flags
0x1- SHF_ALLOC =
0x2- SHF_EXECINSTR =
0x4- SHF_INFO_LINK =
0x40- STB_LOCAL =
Symbol binding/type (st_info = (bind << 4) | type)
0- STB_GLOBAL =
1- STT_NOTYPE =
0- STT_OBJECT =
1- STT_FUNC =
2- STT_SECTION =
3- STT_FILE =
4- STV =
{ default: 0, internal: 1, hidden: 2, protected: 3 }.freeze
- R_X86_64_64 =
x86_64 relocation types: 64 for an absolute 64-bit address (a pointer slot in .data initialized to another object's address), PC32 for a plain PC-relative reference (e.g. a "lea rip" into .rodata), PLT32 for a near call (PC-relative, PLT-aware) and REX_GOTPCRELX for a PIC data access (a "mov rax, sym@GOTPCREL(rip)" reading the symbol's GOT slot; the "REX" form marks the REX.W-prefixed mov a linker may relax back to a lea).
1- R_X86_64_PC32 =
2- R_X86_64_PLT32 =
4- R_X86_64_REX_GOTPCRELX =
42- R_AARCH64_ABS64 =
aarch64 relocation types. CALL26 patches the 26-bit immediate of a
bl(orb) with the PC-relative word distance to its target. The remaining four come in pairs, because aarch64 forms a symbol's address in two instructions rather than one: ADR_PREL_PG_HI21 fills the 21-bit page-offset immediate of anadrp(the distance from the referring instruction's own 4 KiB page to the symbol's), and ADD_ABS_LO12_NC fills the 12-bit immediate of the followingaddwith the symbol's offset within that page. The GOT pair is the same split applied to the symbol's Global Offset Table slot: ADR_GOT_PAGE names the slot's page and LD64_GOT_LO12_NC the scaled 12-bit immediate of theldrreading it. "NC" is "no check": the low half cannot overflow, so the linker does not range-check it. The numbers were read off realaarch64-linux-gnu-gccoutput rather than transcribed. ABS64 is aarch64's absolute 64-bit pointer slot, the counterpart of R_X86_64_64, used in .data rather than .text. 257- R_AARCH64_ADR_PREL_PG_HI21 =
275- R_AARCH64_ADD_ABS_LO12_NC =
277- R_AARCH64_CALL26 =
283- R_AARCH64_ADR_GOT_PAGE =
311- R_AARCH64_LD64_GOT_LO12_NC =
312- X86_64_NOP =
Inter-function padding: x86_64's one-byte
nop(0x90), and aarch64's four-bytenopword 0xD503201F stored little-endian (every aarch64 instruction is four bytes, so the unit of padding is a whole word and the 16-byte function alignment is always a whole number of them). "\x90".b
- AARCH64_NOP =
[0xD503201F].pack("L<")
- X86_64 =
The default machine: x86_64. Its relocation table fixes the exact ELF types and addend conventions the System V AMD64 psABI defines for each kind. Every .text kind patches a rel32 field measured from the end of the instruction it belongs to, hence the uniform -4 bias; a :call and a taken :func address are both near PC-relative references and share PLT32. A string reference carries the interned string's .rodata byte offset as its recorded addend, which the same -4 then biases.
MachineDescription.new( e_machine: EM_X86_64, relocations: { call: [RelocDesc.new(type: R_X86_64_PLT32, addend: 0, symbol: :named, addend_bias: -4)], func: [RelocDesc.new(type: R_X86_64_PLT32, addend: 0, symbol: :named, addend_bias: -4)], string: [RelocDesc.new(type: R_X86_64_PC32, addend: :recorded, symbol: :rodata_section, addend_bias: -4)], global: [RelocDesc.new(type: R_X86_64_PC32, addend: 0, symbol: :named, addend_bias: -4)], got: [RelocDesc.new(type: R_X86_64_REX_GOTPCRELX, addend: 0, symbol: :named, addend_bias: -4)], symbol: [RelocDesc.new(type: R_X86_64_64, addend: :recorded, symbol: :named)], rodata: [RelocDesc.new(type: R_X86_64_64, addend: :recorded, symbol: :rodata_section)] }.freeze, text_padding: X86_64_NOP )
- AARCH64 =
The aarch64 machine. A direct :call is a single
bland so a single CALL26; every other .text kind forms an address across two instructions and therefore takes two entries, the second four bytes past the first.:string, :global and :func all use the adrp/add pair, differing only in what they resolve against — the .rodata section symbol for a string (biased by the string's own byte offset, which the linker folds into the page computation for both halves alike) and the named symbol otherwise. :got uses the adrp/ldr pair addressing the symbol's GOT slot. None of them takes a bias: aarch64's relocations name the symbol directly rather than a displacement from the end of a field.
The .data kinds (:symbol, :rodata) are absolute 64-bit pointer slots and so are not machine-shaped at all beyond the type number; ABS64 is aarch64's spelling of the same thing R_X86_64_64 does.
MachineDescription.new( e_machine: EM_AARCH64, relocations: { call: [RelocDesc.new(type: R_AARCH64_CALL26, addend: 0, symbol: :named)], string: [RelocDesc.new(type: R_AARCH64_ADR_PREL_PG_HI21, addend: :recorded, symbol: :rodata_section), RelocDesc.new(type: R_AARCH64_ADD_ABS_LO12_NC, addend: :recorded, symbol: :rodata_section, offset_delta: 4)], global: [RelocDesc.new(type: R_AARCH64_ADR_PREL_PG_HI21, addend: 0, symbol: :named), RelocDesc.new(type: R_AARCH64_ADD_ABS_LO12_NC, addend: 0, symbol: :named, offset_delta: 4)], func: [RelocDesc.new(type: R_AARCH64_ADR_PREL_PG_HI21, addend: 0, symbol: :named), RelocDesc.new(type: R_AARCH64_ADD_ABS_LO12_NC, addend: 0, symbol: :named, offset_delta: 4)], got: [RelocDesc.new(type: R_AARCH64_ADR_GOT_PAGE, addend: 0, symbol: :named), RelocDesc.new(type: R_AARCH64_LD64_GOT_LO12_NC, addend: 0, symbol: :named, offset_delta: 4)], symbol: [RelocDesc.new(type: R_AARCH64_ABS64, addend: :recorded, symbol: :named)], rodata: [RelocDesc.new(type: R_AARCH64_ABS64, addend: :recorded, symbol: :rodata_section)] }.freeze, text_padding: AARCH64_NOP )
- ARRAY_ENTSIZE =
--- initializer / finalizer arrays ------------------------------------ An array section is a flat vector of 8-byte function pointers, each slot filled in by an absolute 64-bit relocation against the function it names. Its shape is fixed by the ABI and by what the linker's array pass demands (SharedLinker#split_array_sections): writable and allocatable, entsize 8, 8-byte aligned.
8- ARRAY_ALIGN =
8- ARRAY_SECTION_BASE =
{ init: ".init_array", fini: ".fini_array" }.freeze
- ARRAY_SECTION_TYPE =
{ init: SHT_INIT_ARRAY, fini: SHT_FINI_ARRAY }.freeze
- ARRAY_KIND_ORDER =
Constructors before destructors, so grouping the entries is deterministic whatever order the caller registered them in.
%i[init fini].freeze
- DEFAULT_ARRAY_PRIORITY =
How a priority is spelled. A run-order number below the default goes into its own section named "
.NNNNN", which is how the linker learns the order (a priority is nowhere in the section's contents). Both numbers were measured off gcc's own objects rather than assumed: priority 101 emits .init_array.00101, so the field is zero-padded to five digits, and priority 65535 emits the plain, unnumbered.init_array, so 65535 is the default. The five digits are exactly what the linker's array_priority regexp (\.\d+) reads back. 65535- ARRAY_PRIORITY_DIGITS =
5- SYM_ENTSIZE =
24- RELA_ENTSIZE =
24- SHDR_ENTSIZE =
64- EHDR_SIZE =
64
Instance Method Summary collapse
-
#add_array_entry(kind:, symbol:, priority: DEFAULT_ARRAY_PRIORITY) ⇒ Object
Registers
symbol— a function this object defines — as a constructor (kind:init) or a destructor (kind:fini). -
#add_data_relocation(offset:, symbol:, addend: 0) ⇒ Object
Records an absolute 64-bit reference inside .data to the named file-scope object
symbol(a pointer global initialized with "&other", a decayed global array, or a computed address constant like "&arr"). -
#add_data_rodata_relocation(offset:, addend:) ⇒ Object
Records an absolute 64-bit reference inside .data into .rodata (a pointer global initialized with a string literal).
- #add_file_symbol(filename) ⇒ Object
-
#add_func_relocation(offset:, symbol:) ⇒ Object
Records a taken function address in .text (a function pointer value, as opposed to a call site).
- #add_global_func(name, offset, size, visibility: :default) ⇒ Object
-
#add_global_object(name, section, offset, size, visibility: :default) ⇒ Object
Registers a defined file-scope variable as a global STT_OBJECT symbol.
-
#add_global_relocation(offset:, symbol:) ⇒ Object
Records a PC-relative reference from .text to the named file-scope variable
symbol(a "lea rip" displacement addressing a global). -
#add_got_relocation(offset:, symbol:) ⇒ Object
Records a PIC reference from .text to the named symbol's Global Offset Table slot (a "mov rax, sym@GOTPCREL(rip)" that loads the symbol's run-time address).
-
#add_local_func(name, offset, size) ⇒ Object
Registers a defined
staticfunction as a file-local STT_FUNC symbol (STB_LOCAL): private to this object, so a same-named function elsewhere does not collide with it. -
#add_local_object(name, section, offset, size) ⇒ Object
Registers a
staticfile-scope variable (or a block-scopestaticlowered to a uniquely named one) as a file-local STT_OBJECT symbol (STB_LOCAL). -
#add_rodata_relocation(offset:, addend:) ⇒ Object
Records a reference from .text into .rodata:
offsetis the start of the referring instruction sequence within .text andaddendthe string's plain byte offset within .rodata, with no target-specific bias applied — the machine description supplies that (see RelocDesc#addend_bias). - #add_text_relocation(offset:, symbol:) ⇒ Object
- #add_text_section(bytes) ⇒ Object
-
#add_undefined_symbol(name) ⇒ Object
Registers an external symbol (a call target defined elsewhere).
-
#initialize(machine: X86_64) ⇒ ELFWriter
constructor
machineis the injected MachineDescription selecting the target's e_machine value and relocation-type table; it defaults to x86_64 so an existing caller needs no change. -
#set_bss(size, align: 1) ⇒ Object
Sets the .bss size in bytes (the zero-initialized file-scope variables) and the section's alignment.
-
#set_data(bytes, align: 1) ⇒ Object
Sets the .data payload (the initialized file-scope variables laid out in order) and the section's alignment.
-
#set_rodata(bytes) ⇒ Object
Sets the .rodata payload (the NUL-terminated, concatenated string pool).
-
#to_binary ⇒ Object
Assembles and returns the ELF object as an ASCII-8BIT String.
Constructor Details
#initialize(machine: X86_64) ⇒ ELFWriter
machine is the injected MachineDescription selecting the target's
e_machine value and relocation-type table; it defaults to x86_64 so an
existing caller needs no change.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 271 def initialize(machine: X86_64) @machine = machine @text_bytes = "".b @rodata_bytes = nil @data_bytes = nil @data_align = 1 @bss_size = 0 @bss_align = 1 @file_symbol = nil @func_symbols = [] @object_symbols = [] # Kept as an Array (in first-added order) because that order feeds the # symbol table's layout, and the layout must be deterministic (DESIGN # N4: identical input -> identical binary). @undefined_symbol_set # mirrors its contents purely for O(1) membership checks, so a large # translation unit's undefined-symbol lookups stay linear overall # instead of quadratic. @undefined_symbols = [] @undefined_symbol_set = Set.new @relocations = [] @data_relocations = [] # Constructor/destructor registrations, in registration order (which is # the order their slots are laid out within a section, and so the order # the runtime calls them in). @array_entries = [] end |
Instance Method Details
#add_array_entry(kind:, symbol:, priority: DEFAULT_ARRAY_PRIORITY) ⇒ Object
Registers symbol — a function this object defines — as a constructor
(kind :init) or a destructor (kind :fini). It becomes one 8-byte slot
in the matching array section, filled by an absolute 64-bit relocation
against that symbol. priority selects the section: the default goes to
the plain ".init_array"/".fini_array", a lower number to its own
".init_array.NNNNN". The symbol may be file-local (a static
constructor, which is the common case), since an absolute relocation
against a local symbol resolves within the object just as well.
446 447 448 449 450 451 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 446 def add_array_entry(kind:, symbol:, priority: DEFAULT_ARRAY_PRIORITY) raise ArgumentError, "unknown array kind: #{kind.inspect}" unless ARRAY_SECTION_BASE.key?(kind) @array_entries << { kind: kind, priority: priority, symbol: symbol } self end |
#add_data_relocation(offset:, symbol:, addend: 0) ⇒ Object
Records an absolute 64-bit reference inside .data to the named file-scope
object symbol (a pointer global initialized with "&other", a decayed
global array, or a computed address constant like "&arr"). offset is
the pointer slot's byte offset within .data and addend the constant byte
displacement past the symbol (0 for a bare "&other"). Resolved against that
symbol as R_X86_64_64 with that addend.
419 420 421 422 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 419 def add_data_relocation(offset:, symbol:, addend: 0) @data_relocations << { kind: :symbol, offset: offset, symbol: symbol, addend: addend } self end |
#add_data_rodata_relocation(offset:, addend:) ⇒ Object
Records an absolute 64-bit reference inside .data into .rodata (a pointer
global initialized with a string literal). offset is the pointer slot's
byte offset within .data and addend the string's byte offset within
.rodata. Resolved against the .rodata section symbol as R_X86_64_64.
428 429 430 431 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 428 def add_data_rodata_relocation(offset:, addend:) @data_relocations << { kind: :rodata, offset: offset, addend: addend } self end |
#add_file_symbol(filename) ⇒ Object
433 434 435 436 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 433 def add_file_symbol(filename) @file_symbol = filename self end |
#add_func_relocation(offset:, symbol:) ⇒ Object
Records a taken function address in .text (a function pointer value, as
opposed to a call site). x86_64 resolves it exactly as a call does, but
aarch64 does not — a bl's CALL26 and an adrp/add address pair are
different sequences — so the two kinds stay distinct here and the machine
description decides whether they coincide.
380 381 382 383 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 380 def add_func_relocation(offset:, symbol:) @relocations << { kind: :func, offset: offset, symbol: symbol } self end |
#add_global_func(name, offset, size, visibility: :default) ⇒ Object
329 330 331 332 333 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 329 def add_global_func(name, offset, size, visibility: :default) @func_symbols << { name: name, offset: offset, size: size, bind: STB_GLOBAL, visibility: STV.fetch(visibility, visibility) } self end |
#add_global_object(name, section, offset, size, visibility: :default) ⇒ Object
Registers a defined file-scope variable as a global STT_OBJECT symbol.
section is :data or :bss, offset its byte offset within that section
(st_value) and size its storage width (st_size).
346 347 348 349 350 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 346 def add_global_object(name, section, offset, size, visibility: :default) @object_symbols << { name: name, section: section, offset: offset, size: size, bind: STB_GLOBAL, visibility: STV.fetch(visibility, visibility) } self end |
#add_global_relocation(offset:, symbol:) ⇒ Object
Records a PC-relative reference from .text to the named file-scope
variable symbol (a "lea rip" displacement addressing a global).
Resolved against that symbol as R_X86_64_PC32 with an addend of -4.
398 399 400 401 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 398 def add_global_relocation(offset:, symbol:) @relocations << { kind: :global, offset: offset, symbol: symbol } self end |
#add_got_relocation(offset:, symbol:) ⇒ Object
Records a PIC reference from .text to the named symbol's Global Offset
Table slot (a "mov rax, sym@GOTPCREL(rip)" that loads the symbol's
run-time address). offset is the rel32 field within .text. Resolved
against that symbol as R_X86_64_REX_GOTPCRELX with an addend of -4, the
same PC-relative bias a "lea rip" uses.
408 409 410 411 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 408 def add_got_relocation(offset:, symbol:) @relocations << { kind: :got, offset: offset, symbol: symbol } self end |
#add_local_func(name, offset, size) ⇒ Object
Registers a defined static function as a file-local STT_FUNC symbol
(STB_LOCAL): private to this object, so a same-named function elsewhere
does not collide with it. Laid out in .text like any other function.
338 339 340 341 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 338 def add_local_func(name, offset, size) @func_symbols << { name: name, offset: offset, size: size, bind: STB_LOCAL } self end |
#add_local_object(name, section, offset, size) ⇒ Object
Registers a static file-scope variable (or a block-scope static
lowered to a uniquely named one) as a file-local STT_OBJECT symbol
(STB_LOCAL). Placed in .data/.bss exactly like a global object.
355 356 357 358 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 355 def add_local_object(name, section, offset, size) @object_symbols << { name: name, section: section, offset: offset, size: size, bind: STB_LOCAL } self end |
#add_rodata_relocation(offset:, addend:) ⇒ Object
Records a reference from .text into .rodata: offset is the start of the
referring instruction sequence within .text and addend the string's
plain byte offset within .rodata, with no target-specific bias applied —
the machine description supplies that (see RelocDesc#addend_bias).
Resolved against the .rodata section symbol.
390 391 392 393 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 390 def add_rodata_relocation(offset:, addend:) @relocations << { kind: :string, offset: offset, addend: addend } self end |
#add_text_relocation(offset:, symbol:) ⇒ Object
370 371 372 373 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 370 def add_text_relocation(offset:, symbol:) @relocations << { kind: :call, offset: offset, symbol: symbol } self end |
#add_text_section(bytes) ⇒ Object
298 299 300 301 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 298 def add_text_section(bytes) @text_bytes = bytes.b self end |
#add_undefined_symbol(name) ⇒ Object
Registers an external symbol (a call target defined elsewhere). Repeated names collapse to a single symbol so several call sites share one entry.
362 363 364 365 366 367 368 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 362 def add_undefined_symbol(name) unless @undefined_symbol_set.include?(name) @undefined_symbols << name @undefined_symbol_set << name end self end |
#set_bss(size, align: 1) ⇒ Object
Sets the .bss size in bytes (the zero-initialized file-scope variables) and the section's alignment. The section, a writable NOBITS occupying no file space, is emitted only when the size is positive.
323 324 325 326 327 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 323 def set_bss(size, align: 1) @bss_size = size @bss_align = align self end |
#set_data(bytes, align: 1) ⇒ Object
Sets the .data payload (the initialized file-scope variables laid out in order) and the section's alignment. The section, a writable PROGBITS, is emitted only when this is set to a non-empty value.
314 315 316 317 318 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 314 def set_data(bytes, align: 1) @data_bytes = bytes.b @data_align = align self end |
#set_rodata(bytes) ⇒ Object
Sets the .rodata payload (the NUL-terminated, concatenated string pool). The section, and its section symbol, are emitted only when this is set to a non-empty value.
306 307 308 309 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 306 def set_rodata(bytes) @rodata_bytes = bytes.b self end |
#to_binary ⇒ Object
Assembles and returns the ELF object as an ASCII-8BIT String.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 |
# File 'lib/rubycc/objfile/elf_writer.rb', line 454 def to_binary # Fixed first: the section name list, the layout and the .rela payloads # all read it, and they must agree on one grouping. @array_sections = build_array_sections @symbols = build_symbol_list @section_names = build_section_names symbol_indices = index_symbols_by_name(@symbols) rodata_sym_index = @symbols.index { |sym| sym[:type] == STT_SECTION && sym[:shndx] == :rodata } strtab, sym_name_offsets = build_strtab symtab = build_symtab(@symbols, sym_name_offsets) rela = relocations? ? build_rela(symbol_indices, rodata_sym_index) : nil rela_data = data_relocations? ? build_rela_data(symbol_indices, rodata_sym_index) : nil rela_arrays = @array_sections.to_h do |group| [group[:name], build_rela_array(group[:entries], symbol_indices, rodata_sym_index)] end sections = section_layout(symtab: symtab, strtab: strtab, rela: rela, rela_data: rela_data, rela_arrays: rela_arrays) assemble(sections) end |