Skip to content

Commit f23f93b

Browse files
committed
Disassemble bytecode
1 parent 53a72e7 commit f23f93b

File tree

12 files changed

+412
-103
lines changed

12 files changed

+412
-103
lines changed

package-lock.json

Lines changed: 24 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@
6666
},
6767
"dependencies": {
6868
"@types/express": "^4.16.0",
69-
"@webassemblyjs/leb128": "^1.8.5",
70-
"@webassemblyjs/wasm-parser": "^1.8.5",
7169
"async-polling": "^0.2.1",
7270
"bn.js": "^4.11.8",
7371
"body-parser": "^1.18.3",

src/api/bytecode/ewasm/BytesReader.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Int64 } from "./Int64";
2+
13
export class BytesReader {
24

35
private pointer: number
@@ -43,7 +45,7 @@ export class BytesReader {
4345
return result
4446
}
4547

46-
private readVarInt32(): number {
48+
readVarInt32(): number {
4749
var result = 0
4850
var shift = 0
4951
while (true) {
@@ -61,6 +63,32 @@ export class BytesReader {
6163
return (result << ashift) >> ashift
6264
}
6365

66+
readVarInt64(): number {
67+
var result = new Uint8Array(8);
68+
var i = 0;
69+
var c = 0;
70+
var shift = 0;
71+
while (true) {
72+
var byte = parseInt(this.readBytesToHex(1), 16)
73+
c |= (byte & 0x7F) << shift;
74+
shift += 7;
75+
if (shift > 8) {
76+
result[i++] = c & 0xFF;
77+
c >>= 8;
78+
shift -= 8;
79+
}
80+
if ((byte & 0x80) === 0)
81+
break;
82+
}
83+
var ashift = (32 - shift);
84+
c = (c << ashift) >> ashift;
85+
while (i < 8) {
86+
result[i++] = c & 0xFF;
87+
c >>= 8;
88+
}
89+
return new Int64(result).toDouble();
90+
}
91+
6492
getPointer(): number {
6593
return this.pointer
6694
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
11
import { WasmValueType } from "./wasmTypes";
2+
import { WasmOpcode, WasmOpcodes } from "./WasmOpcodes";
23

34
export interface FunctionBody {
45
locals: FunctionLocal[]
56
bytecodeHex: string
7+
opcodes: WasmOpcode[]
8+
formattedOpcodes: string
69
}
710

811
export interface FunctionLocal {
912
count: number
1013
valueType: WasmValueType
1114
}
15+
16+
export const formatOpcodes = (opcodes: WasmOpcode[]): string => {
17+
const newLine = '\n'
18+
const tab = ' '
19+
let formattedCode = ''
20+
let indentation = 0
21+
for (const op of opcodes) {
22+
formattedCode += `${tab.repeat(indentation)}${op.opcode.name} ${op.immediates}`
23+
formattedCode += newLine
24+
if (WasmOpcodes.isBlockStart(op)) {
25+
indentation++
26+
} else if (WasmOpcodes.isBlockEnd(op)) {
27+
indentation--
28+
}
29+
}
30+
return formattedCode
31+
}

src/api/bytecode/ewasm/Int64.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export class Int64 {
2+
3+
private _data: Uint8Array;
4+
5+
constructor (data) {
6+
this._data = data || new Uint8Array(8);
7+
}
8+
9+
public toInt32(): number {
10+
return this._data[0] | (this._data[1] << 8) | (this._data[2] << 16) | (this._data[3] << 24);
11+
}
12+
13+
public toDouble(): number {
14+
var power = 1;
15+
var sum;
16+
if (this._data[7] & 0x80) {
17+
sum = -1;
18+
for (var i = 0; i < 8; i++, power *= 256)
19+
sum -= power * (0xFF ^ this._data[i]);
20+
} else {
21+
sum = 0;
22+
for (var i = 0; i < 8; i++, power *= 256)
23+
sum += power * this._data[i];
24+
}
25+
return sum;
26+
}
27+
28+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export enum OpcodeImmediateType {
2+
3+
U32, // varuint32
4+
I32, // varuint32
5+
I64, // varint64
6+
BYTE,
7+
VECTOR_U32
8+
}

src/api/bytecode/ewasm/WasmBinaryParser.ts

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import { BytesReader } from "./BytesReader";
44
import { WasmSection, WasmTypeSectionPayload, WasmSectionPayload, WasmExportSectionPayload, WasmCodeSectionPayload } from "./WasmSection";
55
import { WasmSectionType, WasmType, WasmValueType, getWasmValueType, getExternalType } from "./wasmTypes";
66
import { FuncType } from "./FuncType";
7-
import { FunctionBody, FunctionLocal } from "./FunctionBody";
7+
import { FunctionBody, FunctionLocal, formatOpcodes } from "./FunctionBody";
8+
import { WasmOpcode, WasmOpcodeDefinition, WasmOpcodes, Immediate } from "./WasmOpcodes";
9+
import { OpcodeImmediateType } from "./OpcodeImmediateType";
810

911

1012
@injectable()
@@ -35,24 +37,34 @@ export class WasmBinaryParser {
3537
throw new Error(`WASM binary version=${version}, supported=${this.WASM_V1}`)
3638
}
3739

38-
const wasmSections: WasmSection[] = []
40+
const sections: WasmSection[] = []
3941
while(!reader.finished()) {
4042
const sectionId = reader.readBytesToNumber(1)
4143
const sectionType = WasmSectionType[sectionId.toString()]
4244
const payloadLength = reader.readVarUint32()
4345
const payloadData = reader.readBytes(payloadLength)
4446
const payloadHex = payloadData.toString('hex')
4547
const payload = this.parseSectionPayload(payloadData, sectionId)
46-
wasmSections.push({
48+
sections.push({
4749
sectionType,
48-
payloadLength,
49-
payloadData,
5050
payloadHex,
5151
payload
5252
})
5353
}
54-
console.log(JSON.stringify(wasmSections))
55-
return
54+
55+
// removeme
56+
const sec = sections.find(section => {
57+
return section.sectionType.toString() == WasmSectionType[WasmSectionType.Code.toString()]
58+
});
59+
const outp: WasmCodeSectionPayload = sec.payload as WasmCodeSectionPayload;
60+
// console.log(JSON.stringify(outp.functions[1]))
61+
const mapp = outp.functions[3].opcodes.map(p => `[0x${p.opcode.code.toString(16)}] ${p.opcode.name} ${p.immediates}`)
62+
// console.log(mapp)
63+
console.log(outp.functions[2].formattedOpcodes)
64+
// console.log(JSON.stringify(wasmSections))
65+
return {
66+
sections
67+
}
5668
}
5769

5870
parseSectionPayload(payload: Buffer, sectionId: number): WasmSectionPayload {
@@ -142,12 +154,54 @@ export class WasmBinaryParser {
142154
})
143155
localsCounter++
144156
}
145-
const functionInstructions = reader.readBytes(body.length - reader.getPointer())
146-
const bytecodeHex = functionInstructions.toString('hex')
157+
const bytecodeBuffer: Buffer = reader.readBytes(body.length - reader.getPointer())
158+
const opcodes: WasmOpcode[] = this.parseFunctionBytecode(bytecodeBuffer)
159+
const bytecodeHex: string = bytecodeBuffer.toString('hex')
160+
const formattedOpcodes = formatOpcodes(opcodes)
147161
return {
148162
bytecodeHex,
149-
locals
163+
locals,
164+
opcodes,
165+
formattedOpcodes
166+
}
167+
}
168+
169+
parseFunctionBytecode(bytecode: Buffer): WasmOpcode[] {
170+
const reader = new BytesReader(bytecode)
171+
const opcodes: WasmOpcode[] = []
172+
while(!reader.finished()) {
173+
const opcodeByte = reader.readBytesToNumber(1)
174+
const immediates: string[] = []
175+
const opcodeDefinition: WasmOpcodeDefinition = WasmOpcodes.getDefinition(opcodeByte)
176+
if(!opcodeDefinition) {
177+
throw new Error(`Opcode not implemented: ${opcodeByte} [${opcodeByte.toString(16)}]`)
178+
}
179+
for(const immediate of opcodeDefinition.immediates) {
180+
// TODO refactor
181+
if (immediate.type === OpcodeImmediateType.U32) {
182+
const immediateValue = reader.readVarUint32()
183+
const valueFormatted = immediateValue < 0? `-0x${(immediateValue * -1).toString(16)}`: `0x${immediateValue.toString(16)}`
184+
immediates.push(valueFormatted)
185+
} else if (immediate.type === OpcodeImmediateType.I32) {
186+
const immediateValue = reader.readVarInt32()
187+
const valueFormatted = immediateValue < 0? `-0x${(immediateValue * -1).toString(16)}`: `0x${immediateValue.toString(16)}`
188+
immediates.push(valueFormatted)
189+
} else if (immediate.type === OpcodeImmediateType.I64) {
190+
const immediateValue = reader.readVarInt64()
191+
const valueFormatted = immediateValue < 0? `-0x${(immediateValue * -1).toString(16)}`: `0x${immediateValue.toString(16)}`
192+
immediates.push(valueFormatted)
193+
} else if (immediate.type === OpcodeImmediateType.BYTE) {
194+
const immediateValue = reader.readBytesToNumber(1)
195+
const valueFormatted = immediateValue < 0? `-0x${(immediateValue * -1).toString(16)}`: `0x${immediateValue.toString(16)}`
196+
immediates.push(valueFormatted)
197+
}
198+
}
199+
opcodes.push({
200+
opcode: opcodeDefinition,
201+
immediates
202+
})
150203
}
204+
return opcodes
151205
}
152206

153207
parseFuncType(reader: BytesReader, index: number): FuncType {

0 commit comments

Comments
 (0)