-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
wasm_offset_converter.js
168 lines (150 loc) · 4.97 KB
/
wasm_offset_converter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* @license
* Copyright 2019 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
/** @constructor */
function WasmOffsetConverter(wasmBytes, wasmModule) {
// This class parses a WASM binary file, and constructs a mapping from
// function indices to the start of their code in the binary file, as well
// as parsing the name section to allow conversion of offsets to function names.
//
// The main purpose of this module is to enable the conversion of function
// index and offset from start of function to an offset into the WASM binary.
// This is needed to look up the WASM source map as well as generate
// consistent program counter representations given v8's non-standard
// WASM stack trace format.
//
// v8 bug: https://crbug.com/v8/9172
//
// This code is also used to check if the candidate source map offset is
// actually part of the same function as the offset we are looking for,
// as well as providing the function names for a given offset.
// current byte offset into the WASM binary, as we parse it
// the first section starts at offset 8.
var offset = 8;
// the index of the next function we see in the binary
var funcidx = 0;
// map from function index to byte offset in WASM binary
this.offset_map = {};
this.func_starts = [];
// map from function index to names in WASM binary
this.name_map = {};
// number of imported functions this module has
this.import_functions = 0;
// the buffer unsignedLEB128 will read from.
var buffer = wasmBytes;
function unsignedLEB128() {
// consumes an unsigned LEB128 integer starting at `offset`.
// changes `offset` to immediately after the integer
var result = 0;
var shift = 0;
do {
var byte = buffer[offset++];
result += (byte & 0x7F) << shift;
shift += 7;
} while (byte & 0x80);
return result;
}
function skipLimits() {
var flags = unsignedLEB128();
unsignedLEB128(); // initial size
var hasMax = (flags & 1) != 0;
if (hasMax) {
unsignedLEB128();
}
}
binary_parse:
while (offset < buffer.length) {
var start = offset;
var type = buffer[offset++];
var end = unsignedLEB128() + offset;
switch (type) {
case 2: // import section
// we need to find all function imports and increment funcidx for each one
// since functions defined in the module are numbered after all imports
var count = unsignedLEB128();
while (count-- > 0) {
// skip module
offset = unsignedLEB128() + offset;
// skip name
offset = unsignedLEB128() + offset;
var kind = buffer[offset++];
switch (kind) {
case 0: // function import
++funcidx;
unsignedLEB128(); // skip function type
break;
case 1: // table import
unsignedLEB128(); // skip elem type
skipLimits();
break;
case 2: // memory import
skipLimits();
break;
case 3: // global import
offset += 2; // skip type id byte and mutability byte
break;
case 4: // tag import
++offset; // skip attribute
unsignedLEB128(); // skip tag type
break;
#if ASSERTIONS
default: throw 'bad import kind: ' + kind;
#endif
}
}
this.import_functions = funcidx;
break;
case 10: // code section
var count = unsignedLEB128();
while (count-- > 0) {
var size = unsignedLEB128();
this.offset_map[funcidx++] = offset;
this.func_starts.push(offset);
offset += size;
}
break binary_parse;
}
offset = end;
}
var sections = WebAssembly.Module.customSections(wasmModule, "name");
for (var i = 0; i < sections.length; ++i) {
buffer = new Uint8Array(sections[i]);
if (buffer[0] != 1) // not a function name section
continue;
offset = 1;
unsignedLEB128(); // skip byte count
var count = unsignedLEB128();
while (count-- > 0) {
var index = unsignedLEB128();
var length = unsignedLEB128();
this.name_map[index] = UTF8ArrayToString(buffer, offset, length);
offset += length;
}
}
}
WasmOffsetConverter.prototype.convert = function (funcidx, offset) {
return this.offset_map[funcidx] + offset;
}
WasmOffsetConverter.prototype.getIndex = function (offset) {
var lo = 0;
var hi = this.func_starts.length;
var mid;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
if (this.func_starts[mid] > offset) {
hi = mid;
} else {
lo = mid + 1;
}
}
return lo + this.import_functions - 1;
}
WasmOffsetConverter.prototype.isSameFunc = function (offset1, offset2) {
return this.getIndex(offset1) == this.getIndex(offset2);
}
WasmOffsetConverter.prototype.getName = function (offset) {
var index = this.getIndex(offset);
return this.name_map[index] || ('wasm-function[' + index + ']');
}