Commit 90373f60 authored by protolambda's avatar protolambda

mipsevm: test-run Go program in unicorn, fix mmap PROT_NONE issue

parent f5c99714
......@@ -13,9 +13,9 @@ func LoadELF(f *elf.File) (*State, error) {
PC: uint32(f.Entry),
Hi: 0,
Lo: 0,
Heap: 1 << 20, // start heap at 1 GiB offset for now
Heap: 0x20000000,
Registers: [32]uint32{},
Memory: nil,
Memory: make(map[uint32]*Page),
Exit: 0,
Exited: false,
Step: 0,
......@@ -83,7 +83,11 @@ func patchVM(f *elf.File, st *State) error {
}
// setup stack pointer
sp := uint32(0xc0_00_00_00)
sp := uint32(0x7f_ff_d0_00)
// allocate 1 page for the initial stack data, and 16KB = 4 pages for the stack to grow
if err := st.SetMemoryRange(sp-4*pageSize, bytes.NewReader(make([]byte, 5*pageSize))); err != nil {
return fmt.Errorf("failed to allocate page for stack content")
}
st.Registers[29] = sp
storeMem := func(addr uint32, v uint32) {
......
......@@ -43,8 +43,8 @@ type State struct {
Memory map[uint32]*Page `json:"memory"`
Exit uint32 `json:"exit"`
Exited bool `json:"exited"`
Exit uint8 `json:"exit"`
Exited bool `json:"exited"`
Step uint64 `json:"step"`
}
......@@ -125,7 +125,10 @@ func (s *State) SetMemory(addr uint32, size uint32, v uint32) {
pageAddr := addr & pageAddrMask
p, ok := s.Memory[pageIndex]
if !ok {
panic(fmt.Errorf("missing page %x (addr write at %x)", pageIndex, addr))
// allocate the page if we have not already.
// Go may mmap relatively large ranges, but we only allocate the pages just in time.
p = &Page{}
s.Memory[pageIndex] = p
}
p[pageAddr] = uint8(v >> (i - 1))
addr += 1
......
......@@ -2,6 +2,8 @@ package main
import (
"bytes"
"debug/elf"
"io"
"os"
"path"
"testing"
......@@ -68,3 +70,32 @@ func TestState(t *testing.T) {
})
}
}
func TestMinimal(t *testing.T) {
elfProgram, err := elf.Open("../example/bin/minimal.elf")
require.NoError(t, err, "open ELF file")
state, err := LoadELF(elfProgram)
require.NoError(t, err, "load ELF into state")
err = patchVM(elfProgram, state)
require.NoError(t, err, "apply Go runtime patches")
mu, err := NewUnicorn()
require.NoError(t, err, "load unicorn")
defer mu.Close()
err = LoadUnicorn(state, mu)
require.NoError(t, err, "load state into unicorn")
var stdOutBuf, stdErrBuf bytes.Buffer
err = HookUnicorn(state, mu, io.MultiWriter(&stdOutBuf, os.Stdout), io.MultiWriter(&stdErrBuf, os.Stderr))
require.NoError(t, err, "hook unicorn to state")
err = RunUnicorn(mu, state.PC, 400_000)
require.NoError(t, err, "must run steps without error")
require.True(t, state.Exited, "must complete program")
require.Equal(t, uint8(0), state.Exit, "exit with 0")
require.Equal(t, "hello world!", stdOutBuf.String(), "stdout says hello")
require.Equal(t, "", stdErrBuf.String(), "stderr silent")
}
......@@ -46,6 +46,7 @@ func HookUnicorn(st *State, mu uc.Unicorn, stdOut, stdErr io.Writer) error {
}
syscallNum, _ := mu.RegRead(uc.MIPS_REG_V0)
fmt.Printf("syscall: %d\n", syscallNum)
v0 := uint64(0)
switch syscallNum {
case 4004: // write
......@@ -63,17 +64,33 @@ func HookUnicorn(st *State, mu uc.Unicorn, stdOut, stdErr io.Writer) error {
case 4090: // mmap
a0, _ := mu.RegRead(uc.MIPS_REG_A0)
sz, _ := mu.RegRead(uc.MIPS_REG_A1)
if sz&pageAddrMask != 0 { // adjust size to align with page size
sz += pageSize - (sz & pageAddrMask)
}
if a0 == 0 {
v0 = uint64(st.Heap)
fmt.Printf("mmap heap 0x%x size 0x%x\n", v0, sz)
st.Heap += uint32(sz)
} else {
v0 = a0
fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz)
}
// Go does this thing where it first gets memory with PROT_NONE,
// and then mmaps with a hint with prot=3 (PROT_READ|WRITE).
// We can ignore the NONE case, to avoid duplicate/overlapping mmap calls to unicorn.
prot, _ := mu.RegRead(uc.MIPS_REG_A2)
if prot != 0 {
if err := mu.MemMap(v0, sz); err != nil {
log.Fatalf("mmap fail: %v", err)
}
}
// TODO mmap
case 4045: // brk
v0 = 0x40000000
case 4246: // exit_group
mu.RegWrite(uc.MIPS_REG_PC, 0x5ead0000)
st.Exited = true
st.Exit = uint8(v0)
mu.Stop()
return
}
mu.RegWrite(uc.MIPS_REG_V0, v0)
mu.RegWrite(uc.MIPS_REG_A3, 0)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment