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
788522a9
Unverified
Commit
788522a9
authored
Apr 21, 2023
by
protolambda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mipsevm: source-map util
parent
48c65767
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
251 additions
and
3 deletions
+251
-3
Hello.sol
contracts/src/Hello.sol
+10
-0
evm.go
mipsevm/evm.go
+7
-1
evm_test.go
mipsevm/evm_test.go
+6
-2
solutil.go
mipsevm/solutil.go
+210
-0
solutil_test.go
mipsevm/solutil_test.go
+18
-0
No files found.
contracts/src/Hello.sol
0 → 100644
View file @
788522a9
pragma solidity ^0.7.6;
// Hello-world contract to test source maps and instrumentation
contract Hello {
function helloWorld(uint32 x) public returns (uint32) {
require(x > 3, "test");
return x + 100;
}
}
mipsevm/evm.go
View file @
788522a9
...
@@ -57,11 +57,17 @@ func LoadContract(name string) (*Contract, error) {
...
@@ -57,11 +57,17 @@ func LoadContract(name string) (*Contract, error) {
type
Contract
struct
{
type
Contract
struct
{
DeployedBytecode
struct
{
DeployedBytecode
struct
{
Object
hexutil
.
Bytes
`json:"object"`
Object
hexutil
.
Bytes
`json:"object"`
SourceMap
string
`json:"sourceMap"`
}
`json:"deployedBytecode"`
}
`json:"deployedBytecode"`
// ignore abi,bytecode,etc.
// ignore abi,bytecode,etc.
}
}
func
(
c
*
Contract
)
SourceMap
(
sourcePaths
[]
string
)
(
*
SourceMap
,
error
)
{
return
ParseSourceMap
(
sourcePaths
,
c
.
DeployedBytecode
.
Object
,
c
.
DeployedBytecode
.
SourceMap
)
}
type
Contracts
struct
{
type
Contracts
struct
{
MIPS
*
Contract
MIPS
*
Contract
MIPSMemory
*
Contract
MIPSMemory
*
Contract
...
...
mipsevm/evm_test.go
View file @
788522a9
...
@@ -11,7 +11,6 @@ import (
...
@@ -11,7 +11,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
uc
"github.com/unicorn-engine/unicorn/bindings/go/unicorn"
uc
"github.com/unicorn-engine/unicorn/bindings/go/unicorn"
...
@@ -26,6 +25,10 @@ func TestEVM(t *testing.T) {
...
@@ -26,6 +25,10 @@ func TestEVM(t *testing.T) {
contracts
,
err
:=
LoadContracts
()
contracts
,
err
:=
LoadContracts
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
// the first unlisted source seems to be the ABIDecoderV2 code that the compiler inserts
mipsSrcMap
,
err
:=
contracts
.
MIPS
.
SourceMap
([]
string
{
"~ABIDecoderV2?"
,
"~compiler?"
,
"../contracts/src/MIPS.sol"
})
require
.
NoError
(
t
,
err
)
addrs
:=
&
Addresses
{
addrs
:=
&
Addresses
{
MIPS
:
common
.
Address
{
0
:
0xff
,
19
:
1
},
MIPS
:
common
.
Address
{
0
:
0xff
,
19
:
1
},
MIPSMemory
:
common
.
Address
{
0
:
0xff
,
19
:
2
},
MIPSMemory
:
common
.
Address
{
0
:
0xff
,
19
:
2
},
...
@@ -41,7 +44,8 @@ func TestEVM(t *testing.T) {
...
@@ -41,7 +44,8 @@ func TestEVM(t *testing.T) {
env
:=
NewEVMEnv
(
contracts
,
addrs
)
env
:=
NewEVMEnv
(
contracts
,
addrs
)
env
.
Config
.
Debug
=
true
env
.
Config
.
Debug
=
true
env
.
Config
.
Tracer
=
logger
.
NewMarkdownLogger
(
&
logger
.
Config
{},
os
.
Stdout
)
//env.Config.Tracer = logger.NewMarkdownLogger(&logger.Config{}, os.Stdout)
env
.
Config
.
Tracer
=
mipsSrcMap
.
Tracer
(
os
.
Stdout
)
fn
:=
path
.
Join
(
"test/bin"
,
f
.
Name
())
fn
:=
path
.
Join
(
"test/bin"
,
f
.
Name
())
programMem
,
err
:=
os
.
ReadFile
(
fn
)
programMem
,
err
:=
os
.
ReadFile
(
fn
)
...
...
mipsevm/solutil.go
0 → 100644
View file @
788522a9
package
main
import
(
"fmt"
"io"
"math/big"
"os"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)
type
LineCol
struct
{
Line
uint32
Col
uint32
}
type
InstrMapping
struct
{
S
int32
// start offset in bytes within source (negative when non-existent!)
L
int32
// length in bytes within source (negative when non-existent!)
F
int32
// file index of source (negative when non-existent!)
J
byte
// jump type (i=into, o=out, -=regular)
M
int32
// modifier depth
}
func
parseInstrMapping
(
last
InstrMapping
,
v
string
)
(
InstrMapping
,
error
)
{
data
:=
strings
.
Split
(
v
,
":"
)
out
:=
last
if
len
(
data
)
<
1
{
return
out
,
nil
}
if
len
(
data
)
>
5
{
return
out
,
fmt
.
Errorf
(
"unexpected length: %d"
,
len
(
data
))
}
var
err
error
parse
:=
func
(
x
string
)
int32
{
p
,
e
:=
strconv
.
ParseInt
(
x
,
10
,
32
)
err
=
e
return
int32
(
p
)
}
if
data
[
0
]
!=
""
{
out
.
S
=
parse
(
data
[
0
])
}
if
len
(
data
)
<
2
||
err
!=
nil
{
return
out
,
err
}
if
data
[
1
]
!=
""
{
out
.
L
=
parse
(
data
[
1
])
}
if
len
(
data
)
<
3
||
err
!=
nil
{
return
out
,
err
}
if
data
[
2
]
!=
""
{
out
.
F
=
parse
(
data
[
2
])
}
if
len
(
data
)
<
4
||
err
!=
nil
{
return
out
,
err
}
if
data
[
3
]
!=
""
{
out
.
J
=
data
[
3
][
0
]
}
if
len
(
data
)
<
5
||
err
!=
nil
{
return
out
,
err
}
if
data
[
4
]
!=
""
{
out
.
M
=
parse
(
data
[
4
])
}
return
out
,
err
}
type
SourceMap
struct
{
// source names
Sources
[]
string
// per source, source offset -> line/col
PosData
[][]
LineCol
// per bytecode byte, byte index -> instr
Instr
[]
InstrMapping
}
func
(
s
*
SourceMap
)
Info
(
pc
uint64
)
(
source
string
,
line
uint32
,
col
uint32
)
{
instr
:=
s
.
Instr
[
pc
]
if
instr
.
F
<
0
{
return
}
if
instr
.
F
>=
int32
(
len
(
s
.
Sources
))
{
source
=
"unknown"
return
}
source
=
s
.
Sources
[
instr
.
F
]
if
instr
.
S
<
0
{
return
}
if
s
.
PosData
[
instr
.
F
]
==
nil
{
// when the source file is known to be unavailable
return
}
lc
:=
s
.
PosData
[
instr
.
F
][
instr
.
S
]
line
=
lc
.
Line
col
=
lc
.
Col
return
}
func
(
s
*
SourceMap
)
FormattedInfo
(
pc
uint64
)
string
{
f
,
l
,
c
:=
s
.
Info
(
pc
)
return
fmt
.
Sprintf
(
"%s:%d:%d %v"
,
f
,
l
,
c
,
s
.
Instr
[
pc
])
}
// ParseSourceMap parses a solidity sourcemap: mapping bytecode indices to source references.
// See https://docs.soliditylang.org/en/latest/internals/source_mappings.html
func
ParseSourceMap
(
sources
[]
string
,
bytecode
[]
byte
,
sourceMap
string
)
(
*
SourceMap
,
error
)
{
instructions
:=
strings
.
Split
(
sourceMap
,
";"
)
srcMap
:=
&
SourceMap
{
Sources
:
sources
,
PosData
:
make
([][]
LineCol
,
0
,
len
(
sources
)),
Instr
:
make
([]
InstrMapping
,
0
,
len
(
bytecode
)),
}
// map source code position byte offsets to line/column pairs
for
i
,
s
:=
range
sources
{
if
strings
.
HasPrefix
(
s
,
"~"
)
{
srcMap
.
PosData
=
append
(
srcMap
.
PosData
,
nil
)
continue
}
dat
,
err
:=
os
.
ReadFile
(
s
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to read source %d %q: %w"
,
i
,
s
,
err
)
}
datStr
:=
string
(
dat
)
out
:=
make
([]
LineCol
,
len
(
datStr
))
line
:=
uint32
(
1
)
lastLinePos
:=
uint32
(
0
)
for
i
,
b
:=
range
datStr
{
// iterate the utf8 or the bytes?
col
:=
uint32
(
i
)
-
lastLinePos
out
[
i
]
=
LineCol
{
Line
:
line
,
Col
:
col
}
if
b
==
'\n'
{
lastLinePos
=
uint32
(
i
)
line
+=
1
}
}
srcMap
.
PosData
=
append
(
srcMap
.
PosData
,
out
)
}
instIndex
:=
0
// bytecode offset to instruction
lastInstr
:=
InstrMapping
{}
for
i
:=
0
;
i
<
len
(
bytecode
);
{
inst
:=
bytecode
[
i
]
instLen
:=
1
if
inst
>=
0x60
&&
inst
<=
0x7f
{
// push instructions
pushDataLen
:=
inst
-
0x60
+
1
instLen
+=
int
(
pushDataLen
)
}
var
instMapping
string
if
instIndex
>=
len
(
instructions
)
{
// truncated source-map? Or some instruction that's longer than we accounted for?
// probably the contract-metadata bytes that are not accounted for in source map
fmt
.
Printf
(
"out of instructions: %d
\n
"
,
instIndex
-
len
(
instructions
))
}
else
{
instMapping
=
instructions
[
instIndex
]
}
m
,
err
:=
parseInstrMapping
(
lastInstr
,
instMapping
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to parse instr element in source map: %w"
,
err
)
}
for
j
:=
0
;
j
<
instLen
;
j
++
{
srcMap
.
Instr
=
append
(
srcMap
.
Instr
,
m
)
}
i
+=
instLen
instIndex
+=
1
}
return
srcMap
,
nil
}
func
(
s
*
SourceMap
)
Tracer
(
out
io
.
Writer
)
*
SourceMapTracer
{
return
&
SourceMapTracer
{
s
,
out
}
}
type
SourceMapTracer
struct
{
srcMap
*
SourceMap
out
io
.
Writer
}
func
(
s
*
SourceMapTracer
)
CaptureTxStart
(
gasLimit
uint64
)
{}
func
(
s
*
SourceMapTracer
)
CaptureTxEnd
(
restGas
uint64
)
{}
func
(
s
*
SourceMapTracer
)
CaptureStart
(
env
*
vm
.
EVM
,
from
common
.
Address
,
to
common
.
Address
,
create
bool
,
input
[]
byte
,
gas
uint64
,
value
*
big
.
Int
)
{
}
func
(
s
*
SourceMapTracer
)
CaptureEnd
(
output
[]
byte
,
gasUsed
uint64
,
err
error
)
{}
func
(
s
*
SourceMapTracer
)
CaptureEnter
(
typ
vm
.
OpCode
,
from
common
.
Address
,
to
common
.
Address
,
input
[]
byte
,
gas
uint64
,
value
*
big
.
Int
)
{
}
func
(
s
*
SourceMapTracer
)
CaptureExit
(
output
[]
byte
,
gasUsed
uint64
,
err
error
)
{}
func
(
s
*
SourceMapTracer
)
CaptureState
(
pc
uint64
,
op
vm
.
OpCode
,
gas
,
cost
uint64
,
scope
*
vm
.
ScopeContext
,
rData
[]
byte
,
depth
int
,
err
error
)
{
fmt
.
Fprintf
(
s
.
out
,
"%s: pc %x opcode %s map %v
\n
"
,
s
.
srcMap
.
FormattedInfo
(
pc
),
pc
,
op
.
String
(),
s
.
srcMap
.
Instr
[
pc
])
}
func
(
s
*
SourceMapTracer
)
CaptureFault
(
pc
uint64
,
op
vm
.
OpCode
,
gas
,
cost
uint64
,
scope
*
vm
.
ScopeContext
,
depth
int
,
err
error
)
{
fmt
.
Fprintf
(
s
.
out
,
"%s: FAULT %v
\n
"
,
s
.
srcMap
.
FormattedInfo
(
pc
),
err
)
}
var
_
vm
.
EVMLogger
=
(
*
SourceMapTracer
)(
nil
)
mipsevm/solutil_test.go
0 → 100644
View file @
788522a9
package
main
import
(
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func
TestSourcemap
(
t
*
testing
.
T
)
{
contract
,
err
:=
LoadContract
(
"Hello"
)
require
.
NoError
(
t
,
err
)
srcMap
,
err
:=
contract
.
SourceMap
([]
string
{
"../contracts/src/Hello.sol"
})
require
.
NoError
(
t
,
err
)
for
i
:=
0
;
i
<
len
(
contract
.
DeployedBytecode
.
Object
);
i
++
{
fmt
.
Println
(
srcMap
.
FormattedInfo
(
uint64
(
i
))
+
": test"
)
}
}
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