浏览代码

Remove sol_alloc_free_ syscall on Solana

Since Solana v1.11, this syscall is no longer available.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 3 年之前
父节点
当前提交
29ddbb37c3
共有 13 个文件被更改,包括 160 次插入201 次删除
  1. 2 1
      src/emit/binary.rs
  2. 1 17
      src/emit/solana.rs
  3. 1 1
      stdlib/Makefile
  4. 二进制
      stdlib/bpf/solana.bc
  5. 二进制
      stdlib/bpf/wasmheap.bc
  6. 3 18
      stdlib/solana.c
  7. 0 21
      stdlib/solana_sdk.h
  8. 二进制
      stdlib/wasm/substrate.bc
  9. 26 3
      stdlib/wasmheap.c
  10. 126 93
      tests/solana.rs
  11. 0 43
      tests/solana_helpers/allocator_bump.rs
  12. 0 3
      tests/solana_helpers/mod.rs
  13. 1 1
      tests/solana_tests/yul.rs

+ 2 - 1
src/emit/binary.rs

@@ -1139,12 +1139,13 @@ fn load_stdlib<'a>(context: &'a Context, target: &Target) -> Module<'a> {
     module
 }
 
-static BPF_IR: [&[u8]; 5] = [
+static BPF_IR: [&[u8]; 6] = [
     include_bytes!("../../stdlib/bpf/stdlib.bc"),
     include_bytes!("../../stdlib/bpf/bigint.bc"),
     include_bytes!("../../stdlib/bpf/format.bc"),
     include_bytes!("../../stdlib/bpf/solana.bc"),
     include_bytes!("../../stdlib/bpf/ripemd160.bc"),
+    include_bytes!("../../stdlib/bpf/wasmheap.bc"),
 ];
 
 static WASM_IR: [&[u8]; 4] = [

+ 1 - 17
src/emit/solana.rs

@@ -106,13 +106,7 @@ impl SolanaTarget {
             }],
         );
 
-        binary.internalize(&[
-            "entrypoint",
-            "sol_log_",
-            "sol_alloc_free_",
-            // This entry is produced by llvm due to merging of stdlib.bc with solidity llvm ir
-            "sol_alloc_free_.1",
-        ]);
+        binary.internalize(&["entrypoint", "sol_log_"]);
 
         binary
     }
@@ -207,7 +201,6 @@ impl SolanaTarget {
             "sol_log_pubkey",
             "sol_invoke_signed_c",
             "sol_panic_",
-            "sol_alloc_free_",
             "sol_get_return_data",
             "sol_set_return_data",
             "sol_create_program_address",
@@ -236,15 +229,6 @@ impl SolanaTarget {
             .struct_type(&[u8_ptr.into(), u64_ty.into()], false)
             .ptr_type(AddressSpace::Generic);
 
-        let function = binary.module.add_function(
-            "sol_alloc_free_",
-            u8_ptr.fn_type(&[u8_ptr.into(), u64_ty.into()], false),
-            None,
-        );
-        function
-            .as_global_value()
-            .set_unnamed_address(UnnamedAddress::Local);
-
         let function = binary.module.add_function(
             "sol_log_",
             void_ty.fn_type(&[u8_ptr.into(), u64_ty.into()], false),

+ 1 - 1
stdlib/Makefile

@@ -4,7 +4,7 @@ CFLAGS=$(TARGET_FLAGS) -emit-llvm -O3 -ffreestanding -fno-builtin -Wall -Wno-unu
 bpf/%.bc wasm/%.bc: %.c
 	$(CC) -c $(CFLAGS) $< -o $@
 
-SOLANA=$(addprefix bpf/,solana.bc bigint.bc format.bc stdlib.bc ripemd160.bc)
+SOLANA=$(addprefix bpf/,solana.bc bigint.bc format.bc stdlib.bc ripemd160.bc wasmheap.bc)
 WASM=$(addprefix wasm/,ripemd160.bc stdlib.bc substrate.bc bigint.bc format.bc wasmheap.bc)
 
 all: $(SOLANA) $(WASM)

二进制
stdlib/bpf/solana.bc


二进制
stdlib/bpf/wasmheap.bc


+ 3 - 18
stdlib/solana.c

@@ -5,6 +5,7 @@
 #include "solana_sdk.h"
 
 extern uint64_t solang_dispatch(const SolParameters *param);
+extern void __init_heap();
 
 // The address 'SysvarC1ock11111111111111111111111111111111' base58 decoded
 static const SolPubkey clock_address = {0x06, 0xa7, 0xd5, 0x17, 0x18, 0xc7, 0x74, 0xc9, 0x28, 0x56, 0x63, 0x98, 0x69, 0x1d, 0x5e, 0xb6, 0x8b, 0x5e, 0xb8, 0xa3, 0x9b, 0x4b, 0x6d, 0x5c, 0x73, 0x55, 0x5b, 0x21, 0x00, 0x00, 0x00, 0x00};
@@ -55,12 +56,9 @@ entrypoint(const uint8_t *input)
         return ERROR_INVALID_INSTRUCTION_DATA;
     }
 
-    return solang_dispatch(&params);
-}
+    __init_heap();
 
-void *__malloc(uint32_t size)
-{
-    return sol_alloc_free_(size, NULL);
+    return solang_dispatch(&params);
 }
 
 uint64_t sol_invoke_signed_c(
@@ -904,18 +902,5 @@ void sol_panic_(const char *s, uint64_t len, uint64_t line, uint64_t column)
     printf("panic: %s line %lld", s, line);
 }
 
-void *sol_alloc_free_(uint64_t size, void *ptr)
-{
-    if (size)
-    {
-        return realloc(ptr, size);
-    }
-    else
-    {
-        free(ptr);
-        return NULL;
-    }
-}
-
 int solang_dispatch(const uint8_t *input, uint64_t input_len, SolAccountInfo *ka) {}
 #endif

+ 0 - 21
stdlib/solana_sdk.h

@@ -175,27 +175,6 @@ static size_t sol_strlen(const char *s)
  */
 #define SOL_ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
 
-/**
- * Internal memory alloc/free function
- */
-void *sol_alloc_free_(uint64_t size, void *ptr);
-
-/**
- * Alloc zero-initialized memory
- */
-static void *sol_calloc(size_t nitems, size_t size)
-{
-  return sol_alloc_free_(nitems * size, 0);
-}
-
-/**
- * Deallocates the memory previously allocated by sol_calloc
- */
-static void sol_free(void *ptr)
-{
-  (void)sol_alloc_free_(0, ptr);
-}
-
 /**
  * Panics
  *

二进制
stdlib/wasm/substrate.bc


+ 26 - 3
stdlib/wasmheap.c

@@ -1,9 +1,12 @@
 #include <stdint.h>
 #include <stddef.h>
 #include <stdbool.h>
-
 #include "stdlib.h"
 
+#ifndef __wasm__
+#include "solana_sdk.h"
+#endif
+
 /*
   There are many tradeoffs in heaps. I think for Solidity, we want:
    - small code size to reduce code length
@@ -20,14 +23,28 @@ struct chunk
     size_t allocated;
 };
 
+#ifdef __wasm__
+#define HEAP_START ((struct chunk *)0x10000)
+
 void __init_heap()
 {
-    struct chunk *first = (struct chunk *)0x10000;
+    struct chunk *first = HEAP_START;
     first->next = first->prev = NULL;
     first->allocated = false;
     first->length = (size_t)(__builtin_wasm_memory_size(0) * 0x10000 -
                              (size_t)first - sizeof(struct chunk));
 }
+#else
+#define HEAP_START ((struct chunk *)0x300000000)
+
+void __init_heap()
+{
+    struct chunk *first = HEAP_START;
+    first->next = first->prev = NULL;
+    first->allocated = false;
+    first->length = (32 * 1024) - sizeof(struct chunk);
+}
+#endif
 
 void __attribute__((noinline)) __free(void *m)
 {
@@ -79,7 +96,7 @@ static void shrink_chunk(struct chunk *cur, size_t size)
 
 void *__attribute__((noinline)) __malloc(uint32_t size)
 {
-    struct chunk *cur = (struct chunk *)0x10000;
+    struct chunk *cur = HEAP_START;
 
     while (cur && (cur->allocated || size > cur->length))
         cur = cur->next;
@@ -93,7 +110,13 @@ void *__attribute__((noinline)) __malloc(uint32_t size)
     else
     {
         // go bang
+#ifdef __wasm__
         __builtin_unreachable();
+#else
+        sol_log("out of heap memory");
+        sol_panic();
+#endif
+        return NULL;
     }
 }
 

+ 126 - 93
tests/solana.rs

@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
 
-mod solana_helpers;
-
 use base58::{FromBase58, ToBase58};
 use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
 use ethabi::{ethereum_types::H256, RawLog, Token};
@@ -9,7 +7,6 @@ use libc::c_char;
 use rand::Rng;
 use serde::{Deserialize, Serialize};
 use sha2::{Digest, Sha256};
-use solana_helpers::allocator_bump::Allocator;
 use solana_rbpf::{
     ebpf,
     elf::Executable,
@@ -32,13 +29,7 @@ use solang::{
     Target,
 };
 use std::{
-    alloc::Layout,
-    cell::RefCell,
-    collections::HashMap,
-    convert::TryInto,
-    ffi::OsStr,
-    io::Write,
-    mem::{align_of, size_of},
+    cell::RefCell, collections::HashMap, convert::TryInto, ffi::OsStr, io::Write, mem::size_of,
     rc::Rc,
 };
 use tiny_keccak::{Hasher, Keccak};
@@ -383,7 +374,93 @@ struct SyscallContext<'a> {
     vm: Rc<RefCell<&'a mut VirtualMachine>>,
     input: &'a [u8],
     refs: Rc<RefCell<&'a mut Vec<AccountRef>>>,
-    allocator: Rc<RefCell<&'a mut Allocator>>,
+    heap: *const u8,
+}
+
+impl<'a> SyscallContext<'a> {
+    pub fn heap_verify(&self) {
+        const VERBOSE: bool = false;
+
+        let heap: &[u8] = unsafe { std::slice::from_raw_parts(self.heap, DEFAULT_HEAP_SIZE) };
+
+        const HEAP_START: u64 = 0x3_0000_0000;
+        let mut current_elem = HEAP_START;
+        let mut last_elem = 0;
+
+        let read_u64 = |offset: u64| {
+            let offset = (offset - HEAP_START) as usize;
+            u64::from_le_bytes(heap[offset..offset + 8].try_into().unwrap())
+        };
+
+        if VERBOSE {
+            println!("heap verify:");
+        }
+
+        loop {
+            let next: u64 = read_u64(current_elem);
+            let prev: u64 = read_u64(current_elem + 8);
+            let length: u64 = read_u64(current_elem + 16);
+            let allocated: u64 = read_u64(current_elem + 24);
+
+            if VERBOSE {
+                println!(
+                    "next:{:08x} prev:{:08x} length:{} allocated:{}",
+                    next, prev, length, allocated
+                );
+            }
+
+            let start = (current_elem + 8 * 4 - HEAP_START) as usize;
+
+            let buf = &heap[start..start + length as usize];
+
+            if allocated == 0 {
+                if VERBOSE {
+                    println!("{:08x} {} not allocated", current_elem + 32, length);
+                }
+            } else {
+                if VERBOSE {
+                    println!("{:08x} {} allocated", current_elem + 32, length);
+                }
+
+                assert_eq!(allocated & 0xffff, 1);
+
+                for offset in (0..buf.len()).step_by(16) {
+                    use std::fmt::Write;
+
+                    let mut hex = "\t".to_string();
+                    let mut chars = "\t".to_string();
+                    for i in 0..16 {
+                        if offset + i >= buf.len() {
+                            break;
+                        }
+                        let b = buf[offset + i];
+                        write!(hex, " {:02x}", b).unwrap();
+                        if b.is_ascii() && !b.is_ascii_control() {
+                            write!(chars, "  {}", b as char).unwrap();
+                        } else {
+                            chars.push_str("   ");
+                        }
+                    }
+                    if VERBOSE {
+                        println!("{}\n{}", hex, chars);
+                    }
+                }
+            }
+
+            assert_eq!(last_elem, prev);
+
+            if next == 0 {
+                break;
+            }
+
+            last_elem = current_elem;
+            current_elem = next;
+        }
+
+        if VERBOSE {
+            println!("heap verify done");
+        }
+    }
 }
 
 struct SolPanic();
@@ -431,6 +508,8 @@ impl<'a> SyscallObject<UserError> for SolLog<'a> {
         memory_mapping: &mut MemoryMapping,
         result: &mut Result<u64, EbpfError<UserError>>,
     ) {
+        self.context.heap_verify();
+
         let host_addr = question_mark!(memory_mapping.map(AccessType::Load, vm_addr, len), result);
         let c_buf: *const c_char = host_addr as *const c_char;
         unsafe {
@@ -475,6 +554,8 @@ impl<'a> SyscallObject<UserError> for SolLogPubKey<'a> {
         memory_mapping: &mut MemoryMapping,
         result: &mut Result<u64, EbpfError<UserError>>,
     ) {
+        self.context.heap_verify();
+
         let account = question_mark!(
             translate_slice::<Account>(memory_mapping, pubkey_addr, 1),
             result
@@ -516,6 +597,8 @@ impl<'a> SyscallObject<UserError> for SolLogU64<'a> {
 
         println!("log64: {}", message);
 
+        self.context.heap_verify();
+
         if let Ok(mut vm) = self.context.vm.try_borrow_mut() {
             vm.logs.push_str(&message);
         }
@@ -523,15 +606,17 @@ impl<'a> SyscallObject<UserError> for SolLogU64<'a> {
     }
 }
 
-struct SolSha256();
-impl SolSha256 {
+struct SolSha256<'a> {
+    context: SyscallContext<'a>,
+}
+impl<'a> SolSha256<'a> {
     /// new
-    pub fn init<C, E>(_unused: C) -> Box<dyn SyscallObject<UserError>> {
-        Box::new(Self {})
+    pub fn init(context: SyscallContext<'a>) -> Box<(dyn SyscallObject<UserError> + 'a)> {
+        Box::new(Self { context })
     }
 }
 
-impl SyscallObject<UserError> for SolSha256 {
+impl<'a> SyscallObject<UserError> for SolSha256<'a> {
     fn call(
         &mut self,
         src: u64,
@@ -542,6 +627,8 @@ impl SyscallObject<UserError> for SolSha256 {
         memory_mapping: &mut MemoryMapping,
         result: &mut Result<u64, EbpfError<UserError>>,
     ) {
+        self.context.heap_verify();
+
         let arrays = question_mark!(
             translate_slice::<(u64, u64)>(memory_mapping, src, len),
             result
@@ -569,15 +656,17 @@ impl SyscallObject<UserError> for SolSha256 {
     }
 }
 
-struct SolKeccak256();
-impl SolKeccak256 {
+struct SolKeccak256<'a> {
+    context: SyscallContext<'a>,
+}
+impl<'a> SolKeccak256<'a> {
     /// new
-    pub fn init<C, E>(_unused: C) -> Box<dyn SyscallObject<UserError>> {
-        Box::new(Self {})
+    pub fn init(context: SyscallContext<'a>) -> Box<(dyn SyscallObject<UserError> + 'a)> {
+        Box::new(Self { context })
     }
 }
 
-impl SyscallObject<UserError> for SolKeccak256 {
+impl<'a> SyscallObject<UserError> for SolKeccak256<'a> {
     fn call(
         &mut self,
         src: u64,
@@ -588,6 +677,8 @@ impl SyscallObject<UserError> for SolKeccak256 {
         memory_mapping: &mut MemoryMapping,
         result: &mut Result<u64, EbpfError<UserError>>,
     ) {
+        self.context.heap_verify();
+
         let arrays = question_mark!(
             translate_slice::<(u64, u64)>(memory_mapping, src, len),
             result
@@ -764,6 +855,8 @@ impl<'a> SyscallObject<UserError> for SyscallSetReturnData<'a> {
         memory_mapping: &mut MemoryMapping,
         result: &mut Result<u64, EbpfError<UserError>>,
     ) {
+        self.context.heap_verify();
+
         assert!(len <= 1024, "sol_set_return_data: length is {}", len);
 
         let buf = question_mark!(translate_slice::<u8>(memory_mapping, addr, len), result);
@@ -803,6 +896,8 @@ impl<'a> SyscallObject<UserError> for SyscallGetReturnData<'a> {
         memory_mapping: &mut MemoryMapping,
         result: &mut Result<u64, EbpfError<UserError>>,
     ) {
+        self.context.heap_verify();
+
         if let Ok(vm) = self.context.vm.try_borrow() {
             if let Some((program_id, return_data)) = &vm.return_data {
                 let length = std::cmp::min(len, return_data.len() as u64);
@@ -854,7 +949,10 @@ impl<'a> SyscallObject<UserError> for SyscallLogData<'a> {
         memory_mapping: &mut MemoryMapping,
         result: &mut Result<u64, EbpfError<UserError>>,
     ) {
+        self.context.heap_verify();
+
         if let Ok(mut vm) = self.context.vm.try_borrow_mut() {
+            print!("sol_log_data");
             let untranslated_events =
                 question_mark!(translate_slice::<&[u8]>(memory_mapping, addr, len), result);
 
@@ -870,9 +968,13 @@ impl<'a> SyscallObject<UserError> for SyscallLogData<'a> {
                     result
                 );
 
+                print!(" {}", hex::encode(&event));
+
                 events.push(event.to_vec());
             }
 
+            println!();
+
             vm.events.push(events.to_vec());
 
             *result = Ok(0);
@@ -910,58 +1012,7 @@ impl From<Ed25519SigCheckError> for u64 {
     }
 }
 
-// Shamelessly stolen from solana source
-
-/// Dynamic memory allocation syscall called when the BPF program calls
-/// `sol_alloc_free_()`.  The allocator is expected to allocate/free
-/// from/to a given chunk of memory and enforce size restrictions.  The
-/// memory chunk is given to the allocator during allocator creation and
-/// information about that memory (start address and size) is passed
-/// to the VM to use for enforcement.
-struct SyscallAllocFree<'a> {
-    context: SyscallContext<'a>,
-}
-impl<'a> SyscallAllocFree<'a> {
-    /// new
-    pub fn init(context: SyscallContext<'a>) -> Box<(dyn SyscallObject<UserError> + 'a)> {
-        Box::new(Self { context })
-    }
-}
-
 const DEFAULT_HEAP_SIZE: usize = 32 * 1024;
-/// Start of the input buffers in the memory map
-
-impl<'a> SyscallObject<UserError> for SyscallAllocFree<'a> {
-    fn call(
-        &mut self,
-        size: u64,
-        free_addr: u64,
-        _arg3: u64,
-        _arg4: u64,
-        _arg5: u64,
-        _memory_mapping: &mut MemoryMapping,
-        result: &mut Result<u64, EbpfError<UserError>>,
-    ) {
-        if let Ok(mut allocator) = self.context.allocator.try_borrow_mut() {
-            let align = align_of::<u128>();
-            let layout = match Layout::from_size_align(size as usize, align) {
-                Ok(layout) => layout,
-                Err(_) => {
-                    *result = Ok(0);
-                    return;
-                }
-            };
-            *result = if free_addr == 0 {
-                Ok(allocator.alloc(layout))
-            } else {
-                allocator.dealloc(free_addr, layout);
-                Ok(0)
-            }
-        } else {
-            panic!();
-        }
-    }
-}
 
 /// Rust representation of C's SolInstruction
 #[derive(Debug)]
@@ -1418,19 +1469,11 @@ impl VirtualMachine {
             .unwrap();
 
         syscall_registry
-            .register_syscall_by_name(
-                b"sol_sha256",
-                SolSha256::init::<BpfSyscallContext, UserError>,
-                SolSha256::call,
-            )
+            .register_syscall_by_name(b"sol_sha256", SolSha256::init, SolSha256::call)
             .unwrap();
 
         syscall_registry
-            .register_syscall_by_name(
-                b"sol_keccak256",
-                SolKeccak256::init::<BpfSyscallContext, UserError>,
-                SolKeccak256::call,
-            )
+            .register_syscall_by_name(b"sol_keccak256", SolKeccak256::init, SolKeccak256::call)
             .unwrap();
 
         syscall_registry
@@ -1457,14 +1500,6 @@ impl VirtualMachine {
             )
             .unwrap();
 
-        syscall_registry
-            .register_syscall_by_name(
-                b"sol_alloc_free_",
-                SyscallAllocFree::init,
-                SyscallAllocFree::call,
-            )
-            .unwrap();
-
         syscall_registry
             .register_syscall_by_name(
                 b"sol_set_return_data",
@@ -1508,13 +1543,11 @@ impl VirtualMachine {
         )
         .unwrap();
 
-        let mut allocator = Allocator::new(DEFAULT_HEAP_SIZE as u64, ebpf::MM_HEAP_START);
-
         let context = SyscallContext {
             vm: Rc::new(RefCell::new(self)),
-            allocator: Rc::new(RefCell::new(&mut allocator)),
             input: &parameter_bytes,
             refs: Rc::new(RefCell::new(&mut refs)),
+            heap: heap.as_ptr(),
         };
 
         vm.bind_syscall_context_objects(context).unwrap();

+ 0 - 43
tests/solana_helpers/allocator_bump.rs

@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use std::alloc::Layout;
-
-#[derive(Debug)]
-pub struct Allocator {
-    start: u64,
-    len: u64,
-    pos: u64,
-}
-
-impl Allocator {
-    pub fn new(len: u64, virtual_address: u64) -> Self {
-        Allocator {
-            start: virtual_address,
-            len,
-            pos: 0,
-        }
-    }
-}
-
-impl Allocator {
-    pub fn alloc(&mut self, layout: Layout) -> u64 {
-        let bytes_to_align = (self.pos as *const u8).align_offset(layout.align()) as u64;
-        if self
-            .pos
-            .saturating_add(layout.size() as u64)
-            .saturating_add(bytes_to_align)
-            <= self.len
-        {
-            self.pos += bytes_to_align;
-            let addr = self.start + self.pos;
-            self.pos += layout.size() as u64;
-            addr
-        } else {
-            panic!("out of heap");
-        }
-    }
-
-    pub fn dealloc(&mut self, _addr: u64, _layout: Layout) {
-        // It's a bump allocator, free not supported
-    }
-}

+ 0 - 3
tests/solana_helpers/mod.rs

@@ -1,3 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-pub mod allocator_bump;

+ 1 - 1
tests/solana_tests/yul.rs

@@ -79,7 +79,7 @@ contract testing  {
     assert_eq!(
         returns,
         vec![
-            Token::Uint(U256::from(12884901888u64)),
+            Token::Uint(U256::from(12884901920u64)),
             Token::Uint(U256::from(4))
         ]
     );