Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
90373f60
Unverified
Commit
90373f60
authored
Apr 18, 2023
by
protolambda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mipsevm: test-run Go program in unicorn, fix mmap PROT_NONE issue
parent
f5c99714
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
63 additions
and
8 deletions
+63
-8
patch.go
mipsevm/patch.go
+7
-3
state.go
mipsevm/state.go
+6
-3
state_test.go
mipsevm/state_test.go
+31
-0
unicorn.go
mipsevm/unicorn.go
+19
-2
No files found.
mipsevm/patch.go
View file @
90373f60
...
...
@@ -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
)
{
...
...
mipsevm/state.go
View file @
90373f60
...
...
@@ -43,8 +43,8 @@ type State struct {
Memory
map
[
uint32
]
*
Page
`json:"memory"`
Exit
uint
32
`json:"exit"`
Exited
bool
`json:"exited"`
Exit
uint
8
`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
...
...
mipsevm/state_test.go
View file @
90373f60
...
...
@@ -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"
)
}
mipsevm/unicorn.go
View file @
90373f60
...
...
@@ -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
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment