Skip to content

Commit

Permalink
Loading the limits of prover data from ELF (#2293)
Browse files Browse the repository at this point in the history
  • Loading branch information
lvella authored Dec 31, 2024
1 parent c8d2703 commit fc2426d
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 24 deletions.
61 changes: 46 additions & 15 deletions riscv-runtime/powdr.x
Original file line number Diff line number Diff line change
@@ -1,40 +1,71 @@
# 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
# __powdr_prover_data_init
# __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:
Expand Down
34 changes: 25 additions & 9 deletions riscv/src/elf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
}
}

Expand Down

0 comments on commit fc2426d

Please sign in to comment.