Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/wapython
Path: blob/main/core/dylink/src/global-offset-table.ts
1067 views
1
import FunctionTable from "./function-table";
2
import debug from "debug";
3
4
const log = debug("dylink:global-offset-table");
5
6
// Global Offset Table
7
export default class GlobalOffsetTable {
8
private GOT: { [key: string]: WebAssembly.Global } = {};
9
10
public readonly memMap: { [key: string]: WebAssembly.Global } = {};
11
public readonly funcMap: {
12
[key: string]: { index: number; set: (f: Function) => void };
13
} = {};
14
public readonly mem: { [key: string]: WebAssembly.Global };
15
public readonly func: { [key: string]: WebAssembly.Global };
16
private getMainInstanceExports: () => { [key: string]: any };
17
private functionTable: FunctionTable;
18
19
constructor(
20
getMainInstanceExports: () => { [key: string]: any },
21
functionTable: FunctionTable
22
) {
23
this.mem = new Proxy(this.GOT, { get: this.GOTMemHandler.bind(this) });
24
this.func = new Proxy(this.GOT, { get: this.GOTFuncHandler.bind(this) });
25
this.getMainInstanceExports = getMainInstanceExports;
26
this.functionTable = functionTable;
27
}
28
29
getState() {
30
const state = new Set<string>();
31
for (const key in this.GOT) {
32
state.add(key);
33
}
34
return state;
35
}
36
37
setState(state: Set<string>) {
38
for (const key in this.GOT) {
39
if (!state.has(key)) {
40
delete this.GOT[key];
41
delete this.memMap[key];
42
delete this.funcMap[key];
43
}
44
}
45
}
46
47
private GOTMemHandler(_, key: string) {
48
if (key in this.GOT) {
49
return Reflect.get(this.GOT, key);
50
}
51
/*
52
The spec has the following (garbled?) statement about what this is:
53
"However since exports are static, modules connect [sic -- cannot?]
54
export the final relocated addresses (i.e. they cannot add
55
__memory_base before exporting). Thus, the exported address is
56
before relocation; the loader, which knows __memory_base, can
57
then calculate the final relocated address."
58
59
In any case, what we need to do here is return the *memory address*
60
of the variable with name key. For example, if key='stdin', we
61
are returning the address of the stdin file descriptor (that integer).
62
*/
63
64
let rtn = this.GOT[key];
65
if (!rtn) {
66
const x = new WebAssembly.Global(
67
{
68
value: "i32",
69
mutable: true,
70
},
71
0
72
);
73
this.memMap[key] = x;
74
rtn = this.GOT[key] = x;
75
}
76
return rtn;
77
}
78
79
private GOTFuncHandler(_, key: string) {
80
if (key in this.GOT) {
81
return Reflect.get(this.GOT, key);
82
}
83
let rtn = this.GOT[key];
84
if (!rtn) {
85
// Dynamic module needs a *pointer* to the function with name "key".
86
// There are several possibilities:
87
//
88
// 1. This function is already in a our global function table, from
89
// the main module or another dynamic link library defining it. An
90
// example is the function strcmp from libc, which is often used as a pointer
91
// with qsort. In that case, we know the pointer and immediately
92
// set the value of the function pointer to that value -- it's important
93
// to use the *same* pointer in both the main module and the dynamic library,
94
// rather than making another one just for the dynamic library (which would
95
// waste space, and completely breaks functions like qsort that take a
96
// function pointer).
97
//
98
// 2. Another likely possibility is that this is a function that will get defined
99
// as a side effect of the dynamic link module being loaded. We don't know
100
// what address that function will get, so in that case we create an entry
101
// in funcMap, and later below we update the pointer created here.
102
//
103
// 3. A third possibility is that the requested function isn't in the
104
// function pointer table but it's made available via the Javascript
105
// environment. As far as I know, there is no way to make such a Javascript
106
// function available as a function pointer aside from creating a new compiled
107
// function in web assembly that calls that Javascript function, so this is
108
// a fatal error, and we have to modify libc.ts to make such a wrapper. This
109
// happened with geteuid at one point, which comes from node.js.
110
//
111
// 4. The function might be defined in another dynamic library that hasn't
112
// been loaded yet. We have NOT addressed this problem yet, and this must
113
// also be a fatal error.
114
//
115
let value;
116
const f = this.getMainInstanceExports()[`__WASM_EXPORT__${key}`];
117
if (f == null) {
118
// new function: have to do further work below to add this to table.
119
this.funcMap[key] = this.functionTable.setLater();
120
value = this.funcMap[key].index;
121
} else {
122
// existing function perhaps from libc, e.g., "strcmp".
123
value = (f as Function)();
124
}
125
log("GOTFuncHandler ", key, "-->", value);
126
// place in the table -- we make a note of where to put it,
127
// and actually place it later below after the import is done.
128
const ptr = new WebAssembly.Global(
129
{
130
value: "i32",
131
mutable: true,
132
},
133
value
134
);
135
rtn = this.GOT[key] = ptr;
136
}
137
return rtn;
138
}
139
}
140
141