mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-24 22:57:58 +00:00
disable sbrk and emulate EVM linear memory internally (#76)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com> Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
@@ -13,6 +13,10 @@ pub mod exports {
|
||||
}
|
||||
|
||||
pub mod imports {
|
||||
pub static SBRK: &str = "__sbrk_internal";
|
||||
|
||||
pub static MEMORY_SIZE: &str = "__msize";
|
||||
|
||||
pub static ADDRESS: &str = "address";
|
||||
|
||||
pub static BALANCE: &str = "balance";
|
||||
@@ -57,7 +61,9 @@ pub mod imports {
|
||||
|
||||
/// All imported runtime API symbols.
|
||||
/// Useful for configuring common attributes and linkage.
|
||||
pub static IMPORTS: [&str; 21] = [
|
||||
pub static IMPORTS: [&str; 23] = [
|
||||
SBRK,
|
||||
MEMORY_SIZE,
|
||||
ADDRESS,
|
||||
BALANCE,
|
||||
BALANCE_OF,
|
||||
@@ -81,6 +87,3 @@ pub mod imports {
|
||||
VALUE_TRANSFERRED,
|
||||
];
|
||||
}
|
||||
|
||||
/// PolkaVM __sbrk API symbol to extend the heap memory.
|
||||
pub static SBRK: &str = "__sbrk";
|
||||
|
||||
@@ -61,7 +61,10 @@ impl Entry {
|
||||
context
|
||||
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
|
||||
.into(),
|
||||
context.build_sbrk(context.integer_const(crate::polkavm::XLEN, 0))?,
|
||||
context.build_sbrk(
|
||||
context.xlen_type().const_zero(),
|
||||
context.xlen_type().const_zero(),
|
||||
)?,
|
||||
)?;
|
||||
|
||||
context.set_global(
|
||||
|
||||
@@ -1088,16 +1088,20 @@ where
|
||||
Ok(truncated)
|
||||
}
|
||||
|
||||
/// Build a call to PolkaVM `sbrk` for extending the heap by `size`.
|
||||
/// Build a call to PolkaVM `sbrk` for extending the heap from offset by `size`.
|
||||
/// The allocation is aligned to 32 bytes.
|
||||
///
|
||||
/// This emulates the EVM linear memory until the runtime supports metered memory.
|
||||
pub fn build_sbrk(
|
||||
&self,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
size: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
|
||||
Ok(self
|
||||
.builder()
|
||||
.build_call(
|
||||
self.runtime_api_method(runtime_api::SBRK),
|
||||
&[size.into()],
|
||||
self.runtime_api_method(runtime_api::imports::SBRK),
|
||||
&[offset.into(), size.into()],
|
||||
"call_sbrk",
|
||||
)?
|
||||
.try_as_basic_value()
|
||||
@@ -1106,14 +1110,29 @@ where
|
||||
.into_pointer_value())
|
||||
}
|
||||
|
||||
/// Call PolkaVM `sbrk` for extending the heap by `size`,
|
||||
/// Build a call to PolkaVM `msize` for querying the linear memory size.
|
||||
pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
|
||||
Ok(self
|
||||
.builder()
|
||||
.build_call(
|
||||
self.runtime_api_method(runtime_api::imports::MEMORY_SIZE),
|
||||
&[],
|
||||
"call_msize",
|
||||
)?
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.expect("sbrk returns an int")
|
||||
.into_int_value())
|
||||
}
|
||||
|
||||
/// Call PolkaVM `sbrk` for extending the heap by `offset` + `size`,
|
||||
/// trapping the contract if the call failed.
|
||||
/// Returns the end of memory pointer.
|
||||
pub fn build_heap_alloc(
|
||||
&self,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
size: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
|
||||
let end_of_memory = self.build_sbrk(size)?;
|
||||
) -> anyhow::Result<()> {
|
||||
let end_of_memory = self.build_sbrk(offset, size)?;
|
||||
let return_is_nil = self.builder().build_int_compare(
|
||||
inkwell::IntPredicate::EQ,
|
||||
end_of_memory,
|
||||
@@ -1131,7 +1150,7 @@ where
|
||||
|
||||
self.set_basic_block(continue_block);
|
||||
|
||||
Ok(end_of_memory)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a pointer to `offset` into the heap, allocating
|
||||
@@ -1146,40 +1165,12 @@ where
|
||||
assert_eq!(offset.get_type(), self.xlen_type());
|
||||
assert_eq!(length.get_type(), self.xlen_type());
|
||||
|
||||
self.build_heap_alloc(offset, length)?;
|
||||
|
||||
let heap_start = self
|
||||
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
|
||||
.value
|
||||
.as_pointer_value();
|
||||
let heap_end = self.build_sbrk(self.integer_const(crate::polkavm::XLEN, 0))?;
|
||||
let value_end = self.build_gep(
|
||||
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
|
||||
&[self.builder().build_int_nuw_add(offset, length, "end")?],
|
||||
self.byte_type(),
|
||||
"heap_end_gep",
|
||||
);
|
||||
let is_out_of_bounds = self.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
value_end.value,
|
||||
heap_end,
|
||||
"is_value_overflowing_heap",
|
||||
)?;
|
||||
|
||||
let out_of_bounds_block = self.append_basic_block("heap_offset_out_of_bounds");
|
||||
let heap_offset_block = self.append_basic_block("build_heap_pointer");
|
||||
self.build_conditional_branch(is_out_of_bounds, out_of_bounds_block, heap_offset_block)?;
|
||||
|
||||
self.set_basic_block(out_of_bounds_block);
|
||||
let size = self.builder().build_int_nuw_sub(
|
||||
self.builder()
|
||||
.build_ptr_to_int(value_end.value, self.xlen_type(), "value_end")?,
|
||||
self.builder()
|
||||
.build_ptr_to_int(heap_end, self.xlen_type(), "heap_end")?,
|
||||
"heap_alloc_size",
|
||||
)?;
|
||||
self.build_heap_alloc(size)?;
|
||||
self.build_unconditional_branch(heap_offset_block);
|
||||
|
||||
self.set_basic_block(heap_offset_block);
|
||||
Ok(self.build_gep(
|
||||
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
|
||||
&[offset],
|
||||
|
||||
@@ -107,33 +107,6 @@ where
|
||||
Ok(context.word_const(0).as_basic_value_enum())
|
||||
}
|
||||
|
||||
/// Translates the `msize` instruction.
|
||||
pub fn msize<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let heap_end = context.build_sbrk(context.xlen_type().const_zero())?;
|
||||
let heap_start = context
|
||||
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
|
||||
.value
|
||||
.as_pointer_value();
|
||||
let heap_size = context.builder().build_int_nuw_sub(
|
||||
context
|
||||
.builder()
|
||||
.build_ptr_to_int(heap_end, context.xlen_type(), "heap_end")?,
|
||||
context
|
||||
.builder()
|
||||
.build_ptr_to_int(heap_start, context.xlen_type(), "heap_start")?,
|
||||
"heap_size",
|
||||
)?;
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_z_extend(heap_size, context.word_type(), "heap_size_extended")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
/// Translates the `address` instruction.
|
||||
pub fn address<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
//! Translates the heap memory operations.
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::pointer::Pointer;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
|
||||
/// Translates the `msize` instruction.
|
||||
pub fn msize<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_z_extend(
|
||||
context.build_msize()?,
|
||||
context.word_type(),
|
||||
"heap_size_extended",
|
||||
)?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
/// Translates the `mload` instruction.
|
||||
/// Uses the main heap.
|
||||
pub fn load<'ctx, D>(
|
||||
|
||||
Reference in New Issue
Block a user