zisk-build 1.1.0-alpha

Build tooling for compiling guest programs to RISC-V ELF for the ZisK zkVM
OUTPUT_FORMAT("elf64-littleriscv")
OUTPUT_ARCH("riscv")
ENTRY(_start)

MEMORY {
  rom   (xa) : ORIGIN = 0x80000000, LENGTH = 0x08000000
  ram   (wxa) : ORIGIN = 0xa0000000, LENGTH = 0x20000000
}

PHDRS {
  text PT_LOAD FLAGS(1);
  rodata PT_LOAD FLAGS(4);
  data PT_LOAD FLAGS(6);
  bss PT_LOAD FLAGS(6);
  output_data PT_LOAD FLAGS(6);
  general_registers_data PT_LOAD FLAGS(6);
  float_registers_data PT_LOAD FLAGS(6);
  stack_data PT_LOAD FLAGS(6);
}

_stack_size                  = 0x400000;  /* 4 MB reserved */
_general_registers_data_size = 0x8000;    /* 32 kB */
_float_registers_data_size   = 0x8000;    /* 32 kB */
_output_data_size            = 0x20000;   /* 128 KB reserved */
_float_ram_data_size         = 0x10000;   /* 64 KB reserved */

/*

0x4000_0000  ┌────────────────────┐← free input entry
             │    input data      │
             │       (1GB)        │
0x7FFF_FFFF  └────────────────────┘
0x8000_0000  ┌────────────────────┐
             │     ROM data       │             
             │      (128MB)       │
0x87FF_FFFF  └────────────────────┘
                       ↑
                    384 MB
                       ↓
0xA000_0000  ┌────────────────────┐← _init_stack_bottom
             │                    │
             │   stack ↑ 4MB      │
             │                    │
0xA040_0000  ├────────────────────┤← _init_stack_top
             │ .general_registers │  32 KB reserved (NOLOAD)
             │  0x8000 bytes      │  _general_registers_data_start / _end
0xA040_8000  ├────────────────────┤
             │ .float_registers   │  32 KB reserved (NOLOAD)
             │  0x8000 bytes      │  _float_registers_data_start / _end
0xA041_0000  ├────────────────────┤
             │ .output_data       │  128 KB reserved (NOLOAD)
             │  0x20000 bytes     │  _output_data_start / _end
0xA043_0000  ├────────────────────┤
             │   .data            │
             │   .bss             │
             ├────────────────────┤ ← _bss_end, _heap_bottom
             │   heap ↓           │
             │                    │
             │                    │
             │                    │
             │                    │
0xBFFF_0000  ├────────────────────┤ ← _heap_top 
             │ float ram          │
0xC000_0000  └────────────────────┘

*/

SECTIONS
{
  .text : { *(.text.init) *(.text .text.*)} >rom AT>rom :text

  . = ALIGN(8);
  PROVIDE(_global_pointer = .);
  .rodata : { *(.rodata .rodata.*)} >rom AT>rom :rodata

  /* C++ static constructors / destructors (run by _start before/after main),
     placed in ROM: the entries are link-time-constant function pointers, never
     written. KEEP prevents --gc-sections from dropping them. The init/fini array
     bound symbols are referenced (strongly) by the runtime, so this section MUST
     be present when linking against libziskos. Empty when the program has no
     static initializers (start == end), so it costs nothing. */
  . = ALIGN(8);
  .init_array : {
    PROVIDE(__init_array_start = .);
    KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)))
    KEEP(*(.init_array))
    PROVIDE(__init_array_end = .);
  } >rom AT>rom :rodata

  .fini_array : {
    PROVIDE(__fini_array_start = .);
    KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*)))
    KEEP(*(.fini_array))
    PROVIDE(__fini_array_end = .);
  } >rom AT>rom :rodata

  /* reserve stack at the beginning of RAM */
  . = ORIGIN(ram);

  /* reserved space for stack data */
  .stack_data (NOLOAD): {
  PROVIDE(_stack_start = .);
  PROVIDE(_init_stack_bottom = .); /* alias for the start of the reserved stack region */
  . = . + _stack_size;
  PROVIDE(_init_stack_top = .); /* reserve 4M bytes for the initialisation stack */
  } >ram AT>ram :stack_data
  
  /* reserved space for general registers */

  .general_registers_data (NOLOAD) : {
    PROVIDE(_general_registers_data_start = .);
    . = . + _general_registers_data_size; 
    PROVIDE(_general_registers_data_end = .);
  } >ram AT>ram :general_registers_data

  /* reserved space for float registers */

  .float_registers_data (NOLOAD) : {
    PROVIDE(_float_registers_data_start = .);
    . = . + _float_registers_data_size; 
    PROVIDE(_float_registers_data_end = .);
  } >ram AT>ram :float_registers_data

  /* reserved space for output data */

  .output_data (NOLOAD) : {
    PROVIDE(_output_data_start = .);
    . = . + _output_data_size; 
    PROVIDE(_output_data_end = .);
  } >ram AT>ram :output_data

  .data : { *(.data .data.* .sdata .sdata.*) } >ram AT>ram :data

  .bss : {
    PROVIDE(_bss_start = .);
    *(.bss .bss.* .sbss .sbss.*);
    PROVIDE(_bss_end = .); /* ... and one at the end */
  } >ram AT>ram :bss

  PROVIDE(_heap_bottom = .);
  PROVIDE(_heap_top = ORIGIN(ram) + LENGTH(ram) - _float_ram_data_size);
  PROVIDE(_heap_size = _heap_top - _heap_bottom);

  _end = .;
}