Guo Hui 6 lat temu
rodzic
commit
916b3d0dfb

+ 2 - 1
.gitignore

@@ -12,4 +12,5 @@ CMakeSettings.json
 *.dtb
 bin
 obj
-src/Native/Generated
+src/Native/Generated
+out

+ 1 - 1
CMakeLists.txt

@@ -27,7 +27,7 @@ if (MSVC_VERSION GREATER_EQUAL "1900")
         add_definitions(/D_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS)
     endif()
 else()
-    add_compile_options(-Wno-multichar -std=c++17)
+    add_compile_options(-Wno-multichar -std=c++17 -Os -ffunction-sections -fdata-sections -mcmodel=medany)
 endif()
 
 add_subdirectory(src/Native)

+ 13 - 0
cmake/toolchain.cmake

@@ -0,0 +1,13 @@
+set(CMAKE_SYSTEM_NAME Generic)
+set(CMAKE_SYSTEM_PROCESSOR riscv64)
+
+set(tools /opt/chino)
+set(CMAKE_C_COMPILER ${tools}/bin/riscv64-unknown-chino-gcc)
+set(CMAKE_CXX_COMPILER ${tools}/bin/riscv64-unknown-chino-g++)
+
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+SET(CMAKE_C_COMPILER_WORKS 1)
+SET(CMAKE_CXX_COMPILER_WORKS 1)

+ 19 - 2
src/Native/CMakeLists.txt

@@ -4,9 +4,26 @@ include_directories(${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/Generate
 
 set(SRCS natsu.fcall.cpp
          natsu.runtime.cpp
+         natsu.gc.cpp
          main.cpp
          Generated/System.Private.CorLib.cpp
          Generated/Chino.Kernel.cpp)
+         
+if (NOT WIN32)
+    set(ASM_SRCS crt.S)
+    SET_PROPERTY(SOURCE ${ASM_SRCS} PROPERTY LANGUAGE C)
+endif()
 
-add_executable(chino ${SRCS})
-target_link_libraries(chino)
+add_executable(chino ${SRCS} ${ASM_SRCS})
+
+if (NOT WIN32)
+    target_link_libraries(chino -Wl,-gc-sections -nostartfiles -Wl,-static -T ${CMAKE_CURRENT_LIST_DIR}/chino.ld)
+endif()
+
+ADD_CUSTOM_COMMAND(OUTPUT chino.bin
+		COMMAND rm -f chino.bin
+		COMMAND ${CMAKE_OBJCOPY} -O binary ${CMAKE_CURRENT_BINARY_DIR}/chino chino.bin
+        DEPENDS chino
+        COMMENT "Generating chino.bin ...")
+
+ADD_CUSTOM_TARGET(firmware DEPENDS chino.bin)

+ 266 - 0
src/Native/chino.ld

@@ -0,0 +1,266 @@
+/*
+ * The MEMORY command describes the location and size of blocks of memory
+ * in the target. You can use it to describe which memory regions may be
+ * used by the linker, and which memory regions it must avoid.
+ */
+MEMORY
+{
+  /*
+   * Memory with CPU cache.
+   *6M CPU SRAM
+   */
+  ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = (6 * 1024 * 1024)
+  /*
+   * Memory without CPU cache
+   * 6M CPU SRAM
+  */
+  ram_nocache (wxa!ri) : ORIGIN = 0x40000000, LENGTH = (6 * 1024 * 1024)
+}
+
+PROVIDE( _ram_end    = ORIGIN(ram) + LENGTH(ram) );
+
+/*
+ * The OUTPUT_ARCH command specifies the machine architecture where the
+ * argument is one of the names used in the Kendryte library.
+ */
+OUTPUT_ARCH( "riscv" )
+
+ENTRY(_chino_startup)
+_system_stack_size = 0x1000;
+
+/*
+ * The linker only pays attention to the PHDRS command when generating
+ * an ELF output file. In other cases, the linker will simply ignore PHDRS.
+ */
+PHDRS
+{
+  DATA      PT_LOAD;
+  DYN_DATA  PT_NULL;
+}
+
+/*
+ * This is where we specify how the input sections map to output
+ * sections.
+ */
+SECTIONS
+{
+  .text.start :
+  {
+    KEEP( *(.text.start) )
+  } > ram : DATA
+
+  .init :
+  {
+    KEEP (*(SORT_NONE(.init)))
+  } > ram : DATA
+
+  .text :
+  {
+    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+    *(.text.exit .text.exit.*)
+    *(.text.startup .text.startup.*)
+    *(.text.hot .text.hot.*)
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+  } > ram : DATA
+  
+  .fini :
+  {
+    KEEP (*(SORT_NONE(.fini)))
+  } > ram : DATA
+  
+  /* Read-only data segment */
+  .rodata :
+  {
+    *(.rdata)
+    *(.rodata .rodata.*)
+    *(.gnu.linkonce.r.*)
+  } > ram : DATA
+  .rodata1 : { *(.rodata1) } > ram : DATA
+  .sdata2 :
+  {
+    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+  } > ram : DATA
+  
+  .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } > ram : DATA
+  .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } > ram : DATA
+  .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table
+  .gcc_except_table.*) } > ram : DATA
+  .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } > ram : DATA
+  /* These sections are generated by the Sun/Oracle C++ compiler.  */
+  .exception_ranges : ONLY_IF_RO { *(.exception_ranges
+  .exception_ranges*) } > ram : DATA
+
+  /* Exception handling  */
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } > ram : DATA
+  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) } > ram : DATA
+  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } > ram : DATA
+  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } > ram : DATA
+  
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } > ram : DATA
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } > ram : DATA
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } > ram : DATA
+
+  .ctors :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*crtbegin?.o(.ctors))
+    /* We don't want to include the .ctor section from
+       the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  } > ram : DATA
+  .dtors :
+  {
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*crtbegin?.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  } > ram : DATA
+  
+  .data :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    SORT(CONSTRUCTORS)
+  } > ram : DATA
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata :
+  {
+    __global_pointer$ = . + 0x800;
+    *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+  } > ram : DATA
+  _edata = .; PROVIDE (edata = .);
+  
+  __bss_start = .;
+  .sbss :
+  {
+    *(.dynsbss)
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+  } > ram : DYN_DATA
+  .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } > ram : DYN_DATA
+  .bss :
+  {
+    *(.dynbss)
+    *(.bss .bss.* .gnu.linkonce.b.*)
+    *(COMMON)
+  } > ram : DYN_DATA
+  __bss_end = .;
+
+  PROVIDE( _tls_data = ABSOLUTE(.) );
+  /*
+   * Thread Local Storage (TLS) are per-thread global variables.
+   * Compilers such as GCC provide a __thread keyword to mark global
+   * variables as per-thread. Support is required in the program loader
+   * and thread creator.
+   */
+
+  /* Thread-local data segment, .tdata (initialized tls). */
+  .tdata :
+  {
+    KEEP( *(.tdata.begin) )
+    *(.tdata .tdata.*)
+    *(.gnu.linkonce.td.*)
+    KEEP( *(.tdata.end) )
+  } > ram : DATA
+
+  /* Thread-local bss segment, .tbss (zero-initialized tls). */
+  .tbss :
+  {
+    *(.tbss .tbss.*)
+    *(.gnu.linkonce.tb.*)
+    KEEP( *(.tbss.end) )
+  } > ram : DYN_DATA
+
+  .stack : 
+  {
+      . = . + _system_stack_size;
+      . = ALIGN(4);
+      _estack0 = .;
+      . = . + _system_stack_size;
+      . = ALIGN(4);
+      _estack1 = .;
+  } > ram : DYN_DATA
+
+  /*
+   * End of uninitalized data segement
+   *
+   * Actually the stack needs 16B alignment, and it won't hurt to also slightly
+   * increase the alignment to 32 or even 64 (cache line size).
+   *
+   * Align _heap_start to cache line size
+   */
+  . = ALIGN(64);
+
+  PROVIDE( _heap_start = ABSOLUTE(.) );
+  PROVIDE( _heap_end = _ram_end );
+  
+
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sections.
+   * Symbols in the DWARF debugging sections are relative to the beginning
+   * of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+}

+ 118 - 0
src/Native/crt.S

@@ -0,0 +1,118 @@
+
+# define IRQ_STACK_SIZE 20480
+
+.section .text.start, "ax", @progbits
+.globl _chino_startup
+_chino_startup:
+  j 1f
+  nop
+  .word 0xdeadbeef
+1:
+  csrw mideleg, 0
+  csrw medeleg, 0
+  csrw mie, 0
+  csrw mip, 0
+  la t0, trap_entry
+  csrw mtvec, t0
+  
+  li x1, 0
+  li x2, 0
+  li x3, 0
+  li x4, 0
+  li x5, 0
+  li x6, 0
+  li x7, 0
+  li x8, 0
+  li x9, 0
+  li x10,0
+  li x11,0
+  li x12,0
+  li x13,0
+  li x14,0
+  li x15,0
+  li x16,0
+  li x17,0
+  li x18,0
+  li x19,0
+  li x20,0
+  li x21,0
+  li x22,0
+  li x23,0
+  li x24,0
+  li x25,0
+  li x26,0
+  li x27,0
+  li x28,0
+  li x29,0
+  li x30,0
+  li x31,0
+  
+  fssr    x0
+  fmv.d.x f0, x0
+  fmv.d.x f1, x0
+  fmv.d.x f2, x0
+  fmv.d.x f3, x0
+  fmv.d.x f4, x0
+  fmv.d.x f5, x0
+  fmv.d.x f6, x0
+  fmv.d.x f7, x0
+  fmv.d.x f8, x0
+  fmv.d.x f9, x0
+  fmv.d.x f10,x0
+  fmv.d.x f11,x0
+  fmv.d.x f12,x0
+  fmv.d.x f13,x0
+  fmv.d.x f14,x0
+  fmv.d.x f15,x0
+  fmv.d.x f16,x0
+  fmv.d.x f17,x0
+  fmv.d.x f18,x0
+  fmv.d.x f19,x0
+  fmv.d.x f20,x0
+  fmv.d.x f21,x0
+  fmv.d.x f22,x0
+  fmv.d.x f23,x0
+  fmv.d.x f24,x0
+  fmv.d.x f25,x0
+  fmv.d.x f26,x0
+  fmv.d.x f27,x0
+  fmv.d.x f28,x0
+  fmv.d.x f29,x0
+  fmv.d.x f30,x0
+  fmv.d.x f31,x0
+
+.option push
+.option norelax
+  la gp, __global_pointer$
+.option pop
+  csrr t0, mhartid
+  beqz t0, 2f
+  j .
+2:
+  la sp, _idle_stack0_top
+  
+  # clear the bss segment
+  la t0, __bss_start
+  la t1, __bss_end
+1:
+  sd zero, 0(t0)
+  addi t0, t0, 8
+  bltu t0, t1, 1b
+
+  la t0, main
+  jr t0
+
+trap_entry:
+  j .
+
+  .section .bss
+  .align 3
+_irq_stack_base:
+  .space IRQ_STACK_SIZE
+_irq_stack0_top:
+  .space IRQ_STACK_SIZE
+_irq_stack1_top:
+  .space IRQ_STACK_SIZE
+_idle_stack0_top:
+  .space IRQ_STACK_SIZE
+_idle_stack1_top:

+ 3 - 0
src/Native/main.cpp

@@ -2,8 +2,11 @@
 
 using namespace Chino_Kernel::Chino::Kernel;
 
+void InitializeHeap() noexcept;
+
 int main()
 {
+    InitializeHeap();
     Program::_s_Main();
     return 0;
 }

+ 2 - 2
src/Native/natsu.fcall.cpp

@@ -1,10 +1,10 @@
 #include "System.Private.CorLib.h"
-#include <iostream>
+//#include <iostream>
 
 namespace System_Private_CorLib
 {
 void System::Console::_s_SayHello(::System_Private_CorLib::System::Int32 value)
 {
-    std::cout << "Hello from natsu clr! (" << value << ")" << std::endl;
+    //std::cout << "Hello from natsu clr! (" << value << ")" << std::endl;
 }
 }

+ 554 - 0
src/Native/natsu.gc.cpp

@@ -0,0 +1,554 @@
+//
+// Chino Memory
+//
+#include "natsu.runtime.h"
+#include <cstring>
+
+#define traceMALLOC(p, size)
+#define traceFREE(p, size)
+
+#define portBYTE_ALIGNMENT 8
+
+#if portBYTE_ALIGNMENT == 32
+#define portBYTE_ALIGNMENT_MASK (0x001f)
+#endif
+
+#if portBYTE_ALIGNMENT == 16
+#define portBYTE_ALIGNMENT_MASK (0x000f)
+#endif
+
+#if portBYTE_ALIGNMENT == 8
+#define portBYTE_ALIGNMENT_MASK (0x0007)
+#endif
+
+#if portBYTE_ALIGNMENT == 4
+#define portBYTE_ALIGNMENT_MASK (0x0003)
+#endif
+
+#if portBYTE_ALIGNMENT == 2
+#define portBYTE_ALIGNMENT_MASK (0x0001)
+#endif
+
+#if portBYTE_ALIGNMENT == 1
+#define portBYTE_ALIGNMENT_MASK (0x0000)
+#endif
+
+#ifndef portBYTE_ALIGNMENT_MASK
+#error "Invalid portBYTE_ALIGNMENT definition"
+#endif
+
+/* Block sizes must not get too small. */
+#define heapMINIMUM_BLOCK_SIZE ((size_t)(heapStructSize_ << 1))
+
+/* Assumes 8bit bytes! */
+#define heapBITS_PER_BYTE ((size_t)8)
+
+typedef struct A_BLOCK_LINK
+{
+    A_BLOCK_LINK *pNextFreeBlock; /*<< The next free block in the list. */
+    size_t BlockSize; /*<< The size of the free block. */
+} BlockLink_t;
+
+/*
+* Inserts a block of memory that is being freed into the correct position in
+* the list of free memory blocks.  The block being freed will be merged with
+* the block in front it and/or the block behind it if the memory blocks are
+* adjacent to each other.
+*/
+static void prvInsertBlockIntoFreeList(BlockLink_t *pBlockToInsert);
+
+/*-----------------------------------------------------------*/
+BlockLink_t heapStart_, *pHeapEnd_ = nullptr;
+size_t freeBytesRemaining_ = 0;
+size_t minimumEverFreeBytesRemaining_ = 0;
+size_t blockAllocatedBit_ = 0;
+/* The size of the structure placed at the beginning of each allocated memory
+block must by correctly byte aligned. */
+static constexpr size_t heapStructSize_ = (sizeof(BlockLink_t) + ((size_t)(portBYTE_ALIGNMENT - 1))) & ~((size_t)portBYTE_ALIGNMENT_MASK);
+
+struct HeapRegionDesc
+{
+    uintptr_t StartAddress;
+    size_t SizeInBytes;
+};
+
+#ifdef WIN32
+static char _sram[6 * 1024 * 1024];
+#else
+extern "C"
+{
+    extern char _heap_start[];
+    extern char _heap_end[];
+}
+#endif
+
+void InitializeHeap() noexcept
+{
+    BlockLink_t *pFirstFreeBlockInRegion = nullptr, *pPreviousFreeBlock;
+    uintptr_t alignedHeap;
+    size_t totalRegionSize, totalHeapSize = 0;
+    size_t definedRegions = 0;
+    uintptr_t address;
+
+    /* Can only call once! */
+    assert(pHeapEnd_ == nullptr);
+    HeapRegionDesc regionDesc;
+#ifdef WIN32
+    regionDesc.StartAddress = uintptr_t(&_sram[0]);
+    regionDesc.SizeInBytes = size_t(std::size(_sram));
+#else
+    regionDesc.StartAddress = uintptr_t(&_heap_start[0]);
+    regionDesc.SizeInBytes = size_t(&_heap_end[0] - &_heap_start[0]);
+#endif
+    do
+    {
+        totalRegionSize = regionDesc.SizeInBytes;
+        address = regionDesc.StartAddress;
+        /* Ensure the heap region starts on a correctly aligned boundary. */
+        if ((address & portBYTE_ALIGNMENT_MASK) != 0)
+        {
+            address += (portBYTE_ALIGNMENT - 1);
+            address &= ~portBYTE_ALIGNMENT_MASK;
+
+            /* Adjust the size for the bytes lost to alignment. */
+            totalRegionSize -= address - (size_t)regionDesc.StartAddress;
+        }
+
+        alignedHeap = address;
+
+        /* Set xStart if it has not already been set. */
+        if (definedRegions == 0)
+        {
+            /* xStart is used to hold a pointer to the first item in the list of
+			free blocks.  The void cast is used to prevent compiler warnings. */
+            heapStart_.pNextFreeBlock = reinterpret_cast<BlockLink_t *>(alignedHeap);
+            heapStart_.BlockSize = 0;
+        }
+        else
+        {
+            /* Should only get here if one region has already been added to the
+			heap. */
+            assert(pHeapEnd_);
+
+            /* Check blocks are passed in with increasing start addresses. */
+            assert(address > uintptr_t(pHeapEnd_));
+        }
+
+        /* Remember the location of the end marker in the previous region, if
+		any. */
+        pPreviousFreeBlock = pHeapEnd_;
+
+        /* pxEnd is used to mark the end of the list of free blocks and is
+		inserted at the end of the region space. */
+        address = alignedHeap + totalRegionSize;
+        address -= heapStructSize_;
+        address &= ~portBYTE_ALIGNMENT_MASK;
+        pHeapEnd_ = reinterpret_cast<BlockLink_t *>(address);
+        pHeapEnd_->BlockSize = 0;
+        pHeapEnd_->pNextFreeBlock = nullptr;
+
+        /* To start with there is a single free block in this region that is
+		sized to take up the entire heap region minus the space taken by the
+		free block structure. */
+        pFirstFreeBlockInRegion = reinterpret_cast<BlockLink_t *>(alignedHeap);
+        pFirstFreeBlockInRegion->BlockSize = address - uintptr_t(pFirstFreeBlockInRegion);
+        pFirstFreeBlockInRegion->pNextFreeBlock = pHeapEnd_;
+
+        /* If this is not the first region that makes up the entire heap space
+		then link the previous region to this region. */
+        if (pPreviousFreeBlock)
+            pPreviousFreeBlock->pNextFreeBlock = pFirstFreeBlockInRegion;
+
+        totalHeapSize += pFirstFreeBlockInRegion->BlockSize;
+
+        /* Move onto the next HeapRegion_t structure. */
+        definedRegions++;
+    } while (false);
+
+    minimumEverFreeBytesRemaining_ = totalHeapSize;
+    freeBytesRemaining_ = totalHeapSize;
+
+    /* Check something was actually defined before it is accessed. */
+    assert(totalHeapSize);
+
+    /* Work out the position of the top bit in a size_t variable. */
+    blockAllocatedBit_ = ((size_t)1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1);
+}
+
+void *HeapAlloc(size_t wantedSize) noexcept
+{
+    BlockLink_t *pBlock, *pPreviousBlock, *pNewBlockLink;
+    void *pReturn = nullptr;
+    uintptr_t alignOffset = 0;
+
+    /* The heap must be initialised before the first call to
+	prvPortMalloc(). */
+    assert(pHeapEnd_);
+
+    //vTaskSuspendAll();
+    {
+        /* Check the requested block size is not so large that the top bit is
+		set.  The top bit of the block size member of the BlockLink_t structure
+		is used to determine who owns the block - the application or the
+		kernel, so it must be free. */
+        if ((wantedSize & blockAllocatedBit_) == 0)
+        {
+            /* The wanted size is increased so it can contain a BlockLink_t
+			structure in addition to the requested amount of bytes. */
+            if (wantedSize > 0)
+            {
+                wantedSize += heapStructSize_;
+
+                /* Ensure that blocks are always aligned to the required number
+				of bytes. */
+                if ((wantedSize & portBYTE_ALIGNMENT_MASK) != 0x00)
+                {
+                    /* Byte alignment required. */
+                    wantedSize += (portBYTE_ALIGNMENT - (wantedSize & portBYTE_ALIGNMENT_MASK));
+                }
+            }
+
+            if ((wantedSize > 0) && (wantedSize <= freeBytesRemaining_))
+            {
+                /* Traverse the list from the start	(lowest address) block until
+				one	of adequate size is found. */
+                pPreviousBlock = &heapStart_;
+                pBlock = heapStart_.pNextFreeBlock;
+                while ((pBlock->BlockSize < wantedSize) && pBlock->pNextFreeBlock)
+                {
+                    pPreviousBlock = pBlock;
+                    pBlock = pBlock->pNextFreeBlock;
+                }
+
+                /* If the end marker was reached then a block of adequate size
+				was	not found. */
+                if (pBlock != pHeapEnd_)
+                {
+                    /* Return the memory space pointed to - jumping over the
+					BlockLink_t structure at its start. */
+                    pReturn = reinterpret_cast<void *>(uintptr_t(pPreviousBlock->pNextFreeBlock) + heapStructSize_);
+
+                    /* This block is being returned for use so must be taken out
+					of the list of free blocks. */
+                    pPreviousBlock->pNextFreeBlock = pBlock->pNextFreeBlock;
+
+                    /* If the block is larger than required it can be split into
+					two. */
+                    if ((pBlock->BlockSize - wantedSize) > heapMINIMUM_BLOCK_SIZE)
+                    {
+                        /* This block is to be split into two.  Create a new
+						block following the number of bytes requested. The void
+						cast is used to prevent byte alignment warnings from the
+						compiler. */
+                        pNewBlockLink = reinterpret_cast<BlockLink_t *>(uintptr_t(pBlock) + wantedSize);
+
+                        /* Calculate the sizes of two blocks split from the
+						single block. */
+                        pNewBlockLink->BlockSize = pBlock->BlockSize - wantedSize;
+                        pBlock->BlockSize = wantedSize;
+
+                        /* Insert the new block into the list of free blocks. */
+                        prvInsertBlockIntoFreeList(pNewBlockLink);
+                    }
+
+                    freeBytesRemaining_ -= pBlock->BlockSize;
+
+                    if (freeBytesRemaining_ < minimumEverFreeBytesRemaining_)
+                        minimumEverFreeBytesRemaining_ = freeBytesRemaining_;
+
+                    /* The block is being returned - it is allocated and owned
+					by the application and has no "next" block. */
+                    pBlock->BlockSize |= blockAllocatedBit_;
+                    pBlock->pNextFreeBlock = nullptr;
+                }
+            }
+        }
+
+        traceMALLOC(pvReturn, xWantedSize);
+    }
+    //(void)xTaskResumeAll();
+
+#if (configUSE_MALLOC_FAILED_HOOK == 1)
+    {
+        if (pvReturn == NULL)
+        {
+            extern void vApplicationMallocFailedHook(void);
+            vApplicationMallocFailedHook();
+        }
+        else
+        {
+            mtCOVERAGE_TEST_MARKER();
+        }
+    }
+#endif
+
+    return pReturn;
+}
+
+void HeapFree(void *ptr) noexcept
+{
+    auto puc = uintptr_t(ptr);
+    BlockLink_t *pLink;
+
+    if (ptr)
+    {
+        /* The memory being freed will have an BlockLink_t structure immediately
+		before it. */
+        puc -= heapStructSize_;
+
+        /* This casting is to keep the compiler from issuing warnings. */
+        pLink = reinterpret_cast<BlockLink_t *>(puc);
+
+        /* Check the block is actually allocated. */
+        assert(pLink->BlockSize & blockAllocatedBit_);
+        assert(pLink->pNextFreeBlock == nullptr);
+
+        if (pLink->BlockSize & blockAllocatedBit_)
+        {
+            if (pLink->pNextFreeBlock == nullptr)
+            {
+                /* The block is being returned to the heap - it is no longer
+				allocated. */
+                pLink->BlockSize &= ~blockAllocatedBit_;
+
+                //vTaskSuspendAll();
+                {
+                    /* Add this block to the list of free blocks. */
+                    freeBytesRemaining_ += pLink->BlockSize;
+                    traceFREE(pv, pxLink->xBlockSize);
+                    prvInsertBlockIntoFreeList(pLink);
+                }
+                //(void)xTaskResumeAll();
+            }
+        }
+    }
+}
+
+static void prvInsertBlockIntoFreeList(BlockLink_t *pBlockToInsert)
+{
+    BlockLink_t *pIterator;
+    uintptr_t puc;
+
+    /* Iterate through the list until a block is found that has a higher address
+	than the block being inserted. */
+    for (pIterator = &heapStart_; pIterator->pNextFreeBlock < pBlockToInsert; pIterator = pIterator->pNextFreeBlock)
+    {
+        /* Nothing to do here, just iterate to the right position. */
+    }
+
+    /* Do the block being inserted, and the block it is being inserted after
+	make a contiguous block of memory? */
+    puc = uintptr_t(pIterator);
+    if ((puc + pIterator->BlockSize) == uintptr_t(pBlockToInsert))
+    {
+        pIterator->BlockSize += pBlockToInsert->BlockSize;
+        pBlockToInsert = pIterator;
+    }
+
+    /* Do the block being inserted, and the block it is being inserted before
+	make a contiguous block of memory? */
+    puc = uintptr_t(pBlockToInsert);
+    if ((puc + pBlockToInsert->BlockSize) == uintptr_t(pIterator->pNextFreeBlock))
+    {
+        if (pIterator->pNextFreeBlock != pHeapEnd_)
+        {
+            /* Form one big block from the two blocks. */
+            pBlockToInsert->BlockSize += pIterator->pNextFreeBlock->BlockSize;
+            pBlockToInsert->pNextFreeBlock = pIterator->pNextFreeBlock->pNextFreeBlock;
+        }
+        else
+        {
+            pBlockToInsert->pNextFreeBlock = pHeapEnd_;
+        }
+    }
+    else
+    {
+        pBlockToInsert->pNextFreeBlock = pIterator->pNextFreeBlock;
+    }
+
+    /* If the block being inserted plugged a gab, so was merged with the block
+	before and the block after, then it's pxNextFreeBlock pointer will have
+	already been set, and should not be set here as that would make it point
+	to itself. */
+    if (pIterator != pBlockToInsert)
+    {
+        pIterator->pNextFreeBlock = pBlockToInsert;
+    }
+}
+
+namespace natsu
+{
+uint8_t *gc_alloc(size_t size)
+{
+    auto ptr = (uint8_t *)HeapAlloc(size);
+    assert(ptr);
+    return ptr;
+}
+} // namespace  natsu
+
+#ifndef _WIN32
+extern "C"
+{
+    void *malloc(size_t n)
+    {
+        auto p = HeapAlloc(n);
+        assert(p);
+        return p;
+    }
+
+    void free(void *p)
+    {
+        HeapFree(p);
+    }
+
+    void *realloc(void *p, size_t n)
+    {
+        auto np = malloc(n);
+        if (p)
+        {
+            memcpy(np, p, n);
+            free(p);
+        }
+
+        return np;
+    }
+
+    void *calloc(size_t num, size_t size)
+    {
+        const auto n = num * size;
+        auto p = malloc(n);
+        if (p)
+            memset(p, 0, n);
+        return p;
+    }
+
+    void *_malloc_r(struct _reent *, size_t n)
+    {
+        return malloc(n);
+    }
+
+    void _free_r(struct _reent *, void *p)
+    {
+        free(p);
+    }
+
+    void *_realloc_r(struct _reent *, void *p, size_t n)
+    {
+        return realloc(p, n);
+    }
+
+    void *_calloc_r(struct _reent *, size_t num, size_t size)
+    {
+        return calloc(num, size);
+    }
+
+#include <stdio.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+
+#define STDIN_FILENO 0 /* standard input file descriptor */
+#define STDOUT_FILENO 1 /* standard output file descriptor */
+#define STDERR_FILENO 2 /* standard error file descriptor */
+
+    int _close(int file) __attribute__((alias("close")));
+    int _fstat(int file, struct stat *st) __attribute__((alias("fstat")));
+    int _getpid() __attribute__((alias("getpid")));
+    int _isatty(int file) __attribute__((alias("isatty")));
+    int _kill(int pid, int sig) __attribute__((alias("kill")));
+    int _lseek(int file, int ptr, int dir) __attribute__((alias("lseek")));
+    int _open(const char *name, int flags, ...) __attribute__((alias("open")));
+    int _read(int file, char *ptr, int len) __attribute__((alias("read")));
+    int _write(int file, char *ptr, int len) __attribute__((alias("write")));
+    int _gettimeofday(struct timeval *__restrict p, void *__restrict tz) __attribute__((alias("gettimeofday")));
+
+    void _exit(int)
+    {
+        assert(!"Kernel exit");
+        while (1)
+            ;
+    }
+
+    int close(int file)
+    {
+        errno = ENOSYS;
+        return -1;
+    }
+
+    char **environ; /* pointer to array of char * strings that define the current environment variables */
+    int execve(const char *name, char *const *argv, char *const *env);
+    int fork();
+
+    int fstat(int file, struct stat *st)
+    {
+        errno = ENOSYS;
+        return -1;
+    }
+
+    int getpid()
+    {
+        errno = ENOSYS;
+        return -1;
+    }
+
+    int isatty(int file)
+    {
+        errno = ENOSYS;
+        return 0;
+    }
+
+    int kill(int pid, int sig)
+    {
+        //g_Logger->PutString("kill\n");
+        errno = ENOSYS;
+        return -1;
+    }
+
+    int link(char *old, char *newl);
+
+    int lseek(int file, int ptr, int dir)
+    {
+        errno = ENOSYS;
+        return -1;
+    }
+
+    int open(const char *name, int flags, ...)
+    {
+        errno = ENOSYS;
+        return -1;
+    }
+
+    int read(int file, char *ptr, int len)
+    {
+        errno = ENOSYS;
+        return -1;
+    }
+
+    caddr_t sbrk(int incr);
+    int stat(const char *file, struct stat *st);
+    clock_t times(struct tms *buf);
+    int unlink(char *name);
+    int wait(int *status);
+
+    int write(int file, char *ptr, int len)
+    {
+        if (file == STDOUT_FILENO || file == STDERR_FILENO)
+        {
+            //g_Logger->PutString({ ptr, size_t(len) });
+            return len;
+        }
+
+        errno = ENOSYS;
+        return -1;
+    }
+
+    int gettimeofday(struct timeval *__restrict p, void *__restrict tz)
+    {
+        errno = ENOSYS;
+        return -1;
+    }
+}
+#endif

+ 1 - 6
src/Native/natsu.runtime.cpp

@@ -27,11 +27,6 @@ gc_ptr<::System_Private_CorLib::System::String> load_string(std::u16string_view
     auto ptr = reinterpret_cast<::System_Private_CorLib::System::String *>(obj);
     ptr->_stringLength = string.length();
     std::copy(string.begin(), string.end(), &ptr->_firstChar);
-    return ptr;
-}
-
-uint8_t *gc_alloc(size_t size)
-{
-    return new uint8_t[size];
+    return gc_ptr<::System_Private_CorLib::System::String>(ptr);
 }
 } // namespace  natsu

+ 6 - 6
src/Native/natsu.runtime.h

@@ -249,7 +249,7 @@ struct gc_ptr
     {
     }
 
-    constexpr gc_ptr(T *ptr) noexcept
+    explicit constexpr gc_ptr(T *ptr) noexcept
         : ptr_(ptr)
     {
     }
@@ -292,7 +292,7 @@ struct gc_ptr
     template <class U>
     gc_ptr<U> as() const noexcept
     {
-        return dynamic_cast<U *>(ptr_);
+        return gc_ptr<U>(dynamic_cast<U *>(ptr_));
     }
 
     template <class U>
@@ -315,7 +315,7 @@ template <class T>
 gc_ptr<T> gc_new()
 {
     auto ptr = gc_alloc(sizeof(T));
-    return reinterpret_cast<T *>(ptr);
+    return gc_ptr<T>(reinterpret_cast<T *>(ptr));
 }
 
 template <class T>
@@ -325,7 +325,7 @@ gc_ptr<::System_Private_CorLib::System::SZArray_1<T>> gc_new_array(int length)
     auto obj = gc_alloc(size);
     auto ptr = reinterpret_cast<::System_Private_CorLib::System::SZArray_1<T> *>(obj);
     ptr->header_.length_ = length;
-    return ptr;
+    return gc_ptr<::System_Private_CorLib::System::SZArray_1<T>>(ptr);
 }
 
 template <class T, bool IsValueType, class... TArgs>
@@ -334,13 +334,13 @@ auto make_object(TArgs... args)
     if constexpr (IsValueType)
     {
         T value;
-        value._ctor(std::move(args)...);
+        value._ctor(std::forward<TArgs>(args)...);
         return value;
     }
     else
     {
         auto value = gc_new<T>();
-        value->_ctor(std::move(args)...);
+        value->_ctor(std::forward<TArgs>(args)...);
         return value;
     }
 }