Rux Compiled Unit

A Rux Compiled Unit (RCU, .rcu) is the object format of the Rux compiler. Each back end — x86-64 and AArch64 — encodes its instructions itself and stores them, with symbols and relocations, in one RCU per compiled source file. The Rux linker reads the units of a build and writes the target's container: a PE image on Windows, ELF on Linux and FreeBSD, Mach-O on macOS, or a relocatable object or static archive in the target's own format. No external assembler, compiler or linker takes part.

A normal build keeps its units in memory. rux build --emit rcu also writes them out, as Temp/Obj/<file>.rcu, with a readable dump of each in Temp/Rcu/<file>.rcu.txt (see rux build).

flowchart LR
    src["`Src/*.rux`"] --> fe["front end<br/>HIR, LIR"]
    fe --> x64["x86-64 back end"]
    fe --> a64["AArch64 back end"]
    x64 --> rcu["RCU units"]
    a64 --> rcu
    rcu --> link["Rux linker"]
    link --> out["PE, ELF or Mach-O"]

File layout

All multi-byte integers are little-endian, and every field sits at the exact offset listed — there is no implicit padding. The regions follow one another in this order:

┌──────────────────────────────────────────┐ offset 0
│ File header                    32 bytes  │
├──────────────────────────────────────────┤ offset 32
│ Section table       section_count × 40   │
├──────────────────────────────────────────┤ 32 + section_count × 40
│ Symbol table         symbol_count × 20   │
├──────────────────────────────────────────┤
│ For each section, in table order:        │
│   raw data        (aligned to section)   │
│   relocations     (aligned to 4)         │
├──────────────────────────────────────────┤
│ String table      string_table_size      │
├──────────────────────────────────────────┤
│ Rux metadata  64 bytes (aligned to 8)    │ when present
└──────────────────────────────────────────┘

Every gap introduced by alignment is filled with 0x00. A reader needs only the header to find everything else.

File header

OffsetSizeTypeFieldMeaning
04u8[4]magic52 43 55 00 — "RCU\0"
42u16version(major << 8) | minor; this format is 1.0, 0x0100
61u8archtarget architecture — see below
71u8flagsbit 0 F_HAS_METADATA; bits 1–7 are 0
82u16section_countentries in the section table
102u16_reserved0
124u32symbol_countentries in the symbol table
164u32string_table_offfile offset of the string table
204u32string_table_sizeits size in bytes
244u32metadata_offsetfile offset of the metadata block, or 0 when there is none
284u32checksumCRC-32C of the whole file, computed with these four bytes as 0
archArchitecture
0x00unknown
0x01x86-64, little-endian
0x02AArch64, little-endian

The architecture byte names the processor only. Operating system and calling convention are not recorded: the instruction bytes were generated for one target, and the linker is told which one it is linking for. A unit is only meaningful to a link for the same architecture, and the linker rejects any other.

A reader must reject a file whose magic differs, and should reject one whose major version is newer than it understands.

Section table

section_count entries of 40 bytes, starting at offset 32.

OffsetSizeTypeFieldMeaning
08char[8]nameASCII name, at most 7 characters, padded with \0
84u32typesection type
124u32flagssection flags
164u32raw_offsetfile offset of the section's bytes, aligned to alignment
204u32raw_sizenumber of bytes stored
244u32virtual_sizesize in memory: raw_size, or 1 for an empty section
282u16alignmentrequired alignment, a power of two
302u16reloc_countrelocation entries for this section
324u32reloc_offsetfile offset of those entries, or 0 when there are none
364u32_reserved0

An empty section still has a raw_offset — the aligned position its bytes would start at.

typeConstantContents
0SEC_NULLan unused entry
1SEC_TEXTmachine code
2SEC_DATAinitialized writable data
3SEC_RODATAread-only data: literals, constant tables
4SEC_BSSzero-initialized data, with no bytes stored
5SEC_METARux-specific metadata
BitConstantMeaning
0SF_ALLOCoccupies memory at run time
1SF_EXECexecutable
2SF_READreadable
3SF_WRITEwritable
4SF_MERGEidentical entries may be merged
5SF_STRINGSholds NUL-terminated strings

Both back ends emit the same three sections, always in this order:

IndexNameTypeFlagsAlignmentContents
0.textSEC_TEXTSF_ALLOC | SF_EXEC | SF_READ16functions, including asm func bodies
1.rodataSEC_RODATASF_ALLOC | SF_READ8string literals and constant data
2.dataSEC_DATASF_ALLOC | SF_READ | SF_WRITE8writable data

Symbol table

symbol_count entries of 20 bytes, starting right after the section table.

OffsetSizeTypeFieldMeaning
04u32name_offstring-table offset of the name
44u32valueoffset within the section; the value itself for an absolute symbol
84u32sizesize in bytes, 0 when unknown
122u16section_idxowning section; 0xFFFF for an external symbol, 0xFFFE for an absolute one
141u8kindsymbol kind
151u8visibilitysymbol binding
164u32type_name_offstring-table offset of a type spelling — a function's result type, such as "int32", or a constant's type; 0 when none
kindConstantMeaning
0SYM_UNKNOWNunclassified
1SYM_FUNCa function defined in this unit
2SYM_DATAwritable data defined in this unit
3SYM_CONSTread-only data defined in this unit
4SYM_SECTIONa section
5SYM_FILEa source file name
6SYM_EXTERN_FUNCa function this unit calls but does not define
7SYM_EXTERN_DATAdata this unit uses but does not define

An external symbol has section_idx = 0xFFFF and value = 0; it is a reference to a definition in another unit of the link or, for an extern declaration, to a shared library.

visibilityConstantMeaning
0VIS_LOCALa package-private definition
1VIS_GLOBALa public definition of a dependency, linkable across units but not part of the artifact's interface; also every external reference
2VIS_WEAKa definition another unit's global definition overrides
3VIS_EXPORTEDa public definition of the package being built — what a library artifact publishes

The linker binds each reference to exactly one definition and reports a duplicate or a missing one. An executable's root is Main; a library's roots are its exported definitions.

Relocations

Each section's relocations are an array of 16-byte entries at reloc_offset.

OffsetSizeTypeFieldMeaning
04u32section_offsetwhere in the section the field to patch begins
44u32symbol_indexthe symbol whose address is used
82u16typerelocation type
102u16_reserved0
124i32addenda signed constant added to the symbol's address

In the formulas, S is the symbol's address, A the addend and P the address of the patched field. Types below 16 patch a whole little-endian field and are architecture-neutral; types from 16 patch bit fields of one AArch64 instruction word and keep the names of their ELF counterparts.

TypeNamePatchesValue
0NONEnothing—
1ABS_6464-bit fieldS + A
2ABS_3232-bit fieldS + A, which must fit in 32 bits
3REL_3232-bit fieldS + A − (P + 4), a signed 32-bit displacement — x86-64 call, jmp, jcc, [rip + …]
16AARCH64_CALL26bl imm26(S + A − P) >> 2
17AARCH64_JUMP26b imm26(S + A − P) >> 2
18AARCH64_CONDBR19b.cond, cbz, cbnz imm19(S + A − P) >> 2
19AARCH64_TSTBR14tbz, tbnz imm14(S + A − P) >> 2
20AARCH64_ADR_PREL_PG_HI21adrp immhi:immlo(page(S + A) − page(P)) >> 12, page(x) = x & ~0xFFF
21AARCH64_ADD_ABS_LO12_NCadd imm12(S + A) & 0xFFF
22AARCH64_LDST_ABS_LO12_NCload/store imm12((S + A) & 0xFFF) >> scale, where scale is the access size; the address must be aligned to it
23–26AARCH64_MOVW_UABS_G0…G3movz/movk imm16bits 0–15, 16–31, 32–47 or 48–63 of S + A
27AARCH64_PREL3232-bit fieldS + A − P, signed
28AARCH64_PREL6464-bit fieldS + A − P

A displacement that does not fit its field, or a misaligned load/store target, is a link error that names the symbol.

String table

A flat block of NUL-terminated UTF-8 strings. Offset 0 is always the empty string, so a 0 offset anywhere means "no name". Each distinct string is stored once. The writer interns, in this order: the source path, the package name, each symbol's name and type spelling, and the section names. Symbol and section names are printable ASCII; the source path may be any UTF-8.

Metadata

When F_HAS_METADATA is set, a 64-byte block sits at metadata_offset, aligned to 8. The compiler writes it into every unit.

OffsetSizeTypeFieldMeaning
04u8[4]magic4D 45 54 41 — "META"
44u32block_size64
84u32source_path_offstring-table offset of the source file's path
124u32package_name_offstring-table offset of the package name
168u64build_timestampthe build's start, in Unix seconds — the same instant as #build.timestamp
244u32rux_versionthe compiler version, (major << 16) | (minor << 8) | patch
284u32compiler_flagsreserved; written as 0
3232u8[32]source_hashreserved for a SHA-256 of the source; written as zeros

Checksum

checksum is a CRC-32C — the Castagnoli polynomial, reflected form 0x82F63B78, initial value and final XOR 0xFFFFFFFF — over every byte of the file, with the checksum field itself counted as four zero bytes. It is computed last, after every other field is written. It is deliberately not the IEEE CRC-32 of ZIP, so the two are not interchangeable.

Example

Src/Main.rux of a package called Math:

func Add(a: int32, b: int32) -> int32 {
    return a + b;
}

func Main() -> int {
    return Add(3, 4) as int;
}

rux build --emit rcu --target linux-x86_64 writes Temp/Obj/Main.rcu, whose dump begins:

; RCU  Rux Compiled Unit  v1.0
; Architecture: x86-64
; Package:       Math
; Rux version:   0.4.0

Sections: 3
  [ 0]  .text       flags:AER    align:16    data:239B  relocs:1
  [ 1]  .rodata     flags:AR     align:8     data:0B  relocs:0
  [ 2]  .data       flags:ARW    align:8     data:0B  relocs:0

Symbols: 2
  [  0]  Add     .text+0x0000  size=150     FUNC      LOCAL   "int32"
  [  1]  Main    .text+0x0096  size=89      FUNC      LOCAL   "int"

Relocations (.text):
  [  0]  off=0x00CE  sym[0]=Add  REL_32  addend=0

The one relocation is the call Add inside Main: four placeholder bytes at .text + 0xCE that the linker replaces with Add − (P + 4). Built for linux-aarch64 instead, the same call is a bl patched by an AARCH64_CALL26 relocation.

With a source path of 120 bytes, the file is laid out as follows:

OffsetRegionSize
0x000header32
0x020section table, 3 entries120
0x098symbol table, 2 entries40
0x0C0.text bytes (aligned to 16)239
0x1B0.text relocations (aligned to 4)16
0x1C0.rodata, .data (empty)0 — both raw_offsets are 0x1C0
0x1C0string table166: \0, the path, Math, Add, int32, Main, int, .text, .rodata, .data
0x268metadata (aligned to 8)64
0x2A8end of file

and its header reads:

52 43 55 00   magic              "RCU\0"
00 01         version            0x0100
01            arch               x86-64
01            flags              F_HAS_METADATA
03 00         section_count      3
00 00         _reserved
02 00 00 00   symbol_count       2
C0 01 00 00   string_table_off   0x1C0
A6 00 00 00   string_table_size  166
68 02 00 00   metadata_offset    0x268
xx xx xx xx   checksum           CRC-32C, computed last

See also

  • Assembly — asm func bodies, whose symbols become relocations
  • rux build — --emit rcu and the other inspection outputs
  • Linking — external symbols bound to shared libraries