diff --git a/riscv-runtime/powdr.x b/riscv-runtime/powdr.x index a9e7e88c96..2ce52183e9 100644 --- a/riscv-runtime/powdr.x +++ b/riscv-runtime/powdr.x @@ -1,6 +1,14 @@ # Powdr linker script. # -# If you are using powdr-riscv-runtime, it expects the following +# A segment of type LOOS+0xda (0x600000da) is understood by powdr, and it +# delimits an unconstrained memory region that can be used to pass data +# to the ZK program. If present, this region must be bigger than +# one page (2 KB), have a power of 2 size, and be aligned to its own size. +# +# This linker script defines the user data segment to be the second 256 MB +# chunk of the address space. +# +# If you are using powdr-riscv-runtime, it also expects the following # symbols to be defined: # __global_pointer$ # __powdr_stack_start @@ -8,33 +16,56 @@ # __powdr_prover_data_end # # Where "__powdr_prover_data_init" and "__powdr_prover_data_end" -# defines a region bigger than 1 page (2 KB), whose size is a -# power of 2, and aligned to its size. +# defines the bounds of the prover data segment. # # This linker script provides usable definitions to these # symbols, with a stack of almost 256 MB. If you are not building # via powdr-rs, you must manually specify "-C link-arg=-Tpowdr.x" # in rustc to use this linker script (e.g. via RUSTFLAGS). +PHDRS { + # The powdr specific p_type 0x600000da indicates to + # powdr ELF parser where the prover data is. + powdr_prover_data_seg 0x600000da; + + # text is R (4) + X (1) + text_seg PT_LOAD FLAGS(5); + + # data and bss segments are R (4) + W (2) + # there is no point in having a separated rodata segment because powdr doesn't enforce permissions + data_seg PT_LOAD FLAGS(6); + bss_seg PT_LOAD FLAGS(6); +} + SECTIONS { # Stack starts backwards from here (one 2 KB page short of 256 MB) - __powdr_stack_start = 0x10000000 - 0x800; + PROVIDE( __powdr_stack_start = 0x10000000 - 0x800 ); - # The prover data (the second 256 MB chunk of the address space) - __powdr_prover_data_start = 0x10000000; - __powdr_prover_data_end = 0x20000000; + # The prover data section bounds (the second 256 MB chunk of the address space) + . = 0x10000000; + .powdr.prover.data (NOLOAD) : { + PROVIDE( __powdr_prover_data_start = . ); + . += 0x10000000; + PROVIDE( __powdr_prover_data_end = . ); + } :powdr_prover_data_seg - # Data starts here, one page after the prover data. - . = 0x20000000 + 0x800; - .data : { - *(.data) - PROVIDE( __global_pointer$ = . + 0x800 ); - } - .bss : { *(.bss) } + # Place the rest of the segments one page after the prover data. + . += 0x800; # Text addresses are fake in powdr, we use a different address space. - .text : { *(.text) } + .text : ALIGN(4) { *(.text) } :text_seg + + .bss : { *(.sbss .bss .bss.*) } :bss_seg + + .rodata : ALIGN(4) { + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.rodata .rodata.* ) + } :data_seg + + .data : { + *(.data .data.* ) + } :data_seg } # Specify the entry point function provided by powdr-riscv-runtime: diff --git a/riscv/src/elf/mod.rs b/riscv/src/elf/mod.rs index 06fd1593bf..fc621da3d6 100644 --- a/riscv/src/elf/mod.rs +++ b/riscv/src/elf/mod.rs @@ -31,6 +31,9 @@ use self::debug_info::DebugInfo; mod debug_info; +/// The program header type (p_type) for Powdr prover data segments. +pub const PT_POWDR_PROVER_DATA: u32 = 0x600000da; + /// Generates a Powdr Assembly program from a RISC-V 32 executable ELF file. pub fn translate(file_name: &Path, options: CompilerOptions) -> String { let elf_program = load_elf(file_name); @@ -80,13 +83,27 @@ fn load_elf(file_name: &Path) -> ElfProgram { // Map of addresses into memory sections, so we can know what address belong // in what section. - let address_map = AddressMap( - elf.program_headers - .iter() - .filter(|p| p.p_type == PT_LOAD) - .map(|p| (p.p_vaddr as u32, p)) - .collect(), - ); + let mut address_map = AddressMap(BTreeMap::new()); + let mut prover_data_bounds = None; + for ph in elf.program_headers.iter() { + match ph.p_type { + PT_LOAD => { + address_map.0.insert(ph.p_vaddr as u32, ph); + } + PT_POWDR_PROVER_DATA => { + assert_eq!( + prover_data_bounds, None, + "Only one prover data segment is supported!" + ); + prover_data_bounds = + Some((ph.p_vaddr as u32, ph.p_vaddr as u32 + ph.p_memsz as u32)); + } + _ => {} + } + } + + // If no prover data segment was provided, make it empty. + let prover_data_bounds = prover_data_bounds.unwrap_or((0, 0)); // Set of R_RISCV_HI20 relocations, needed in non-PIE code to identify // loading of absolute addresses to text. @@ -191,8 +208,7 @@ fn load_elf(file_name: &Path) -> ElfProgram { text_labels: referenced_text_addrs, instructions: lifted_text_sections, entry_point: elf.entry as u32, - // TODO: properly load these from the ELF instead of hardcoding them. - prover_data_bounds: (0x10000000, 0x20000000), + prover_data_bounds, } }