Path: blob/main/core/dylink/src/global-offset-table.ts
1067 views
import FunctionTable from "./function-table";1import debug from "debug";23const log = debug("dylink:global-offset-table");45// Global Offset Table6export default class GlobalOffsetTable {7private GOT: { [key: string]: WebAssembly.Global } = {};89public readonly memMap: { [key: string]: WebAssembly.Global } = {};10public readonly funcMap: {11[key: string]: { index: number; set: (f: Function) => void };12} = {};13public readonly mem: { [key: string]: WebAssembly.Global };14public readonly func: { [key: string]: WebAssembly.Global };15private getMainInstanceExports: () => { [key: string]: any };16private functionTable: FunctionTable;1718constructor(19getMainInstanceExports: () => { [key: string]: any },20functionTable: FunctionTable21) {22this.mem = new Proxy(this.GOT, { get: this.GOTMemHandler.bind(this) });23this.func = new Proxy(this.GOT, { get: this.GOTFuncHandler.bind(this) });24this.getMainInstanceExports = getMainInstanceExports;25this.functionTable = functionTable;26}2728getState() {29const state = new Set<string>();30for (const key in this.GOT) {31state.add(key);32}33return state;34}3536setState(state: Set<string>) {37for (const key in this.GOT) {38if (!state.has(key)) {39delete this.GOT[key];40delete this.memMap[key];41delete this.funcMap[key];42}43}44}4546private GOTMemHandler(_, key: string) {47if (key in this.GOT) {48return Reflect.get(this.GOT, key);49}50/*51The spec has the following (garbled?) statement about what this is:52"However since exports are static, modules connect [sic -- cannot?]53export the final relocated addresses (i.e. they cannot add54__memory_base before exporting). Thus, the exported address is55before relocation; the loader, which knows __memory_base, can56then calculate the final relocated address."5758In any case, what we need to do here is return the *memory address*59of the variable with name key. For example, if key='stdin', we60are returning the address of the stdin file descriptor (that integer).61*/6263let rtn = this.GOT[key];64if (!rtn) {65const x = new WebAssembly.Global(66{67value: "i32",68mutable: true,69},70071);72this.memMap[key] = x;73rtn = this.GOT[key] = x;74}75return rtn;76}7778private GOTFuncHandler(_, key: string) {79if (key in this.GOT) {80return Reflect.get(this.GOT, key);81}82let rtn = this.GOT[key];83if (!rtn) {84// Dynamic module needs a *pointer* to the function with name "key".85// There are several possibilities:86//87// 1. This function is already in a our global function table, from88// the main module or another dynamic link library defining it. An89// example is the function strcmp from libc, which is often used as a pointer90// with qsort. In that case, we know the pointer and immediately91// set the value of the function pointer to that value -- it's important92// to use the *same* pointer in both the main module and the dynamic library,93// rather than making another one just for the dynamic library (which would94// waste space, and completely breaks functions like qsort that take a95// function pointer).96//97// 2. Another likely possibility is that this is a function that will get defined98// as a side effect of the dynamic link module being loaded. We don't know99// what address that function will get, so in that case we create an entry100// in funcMap, and later below we update the pointer created here.101//102// 3. A third possibility is that the requested function isn't in the103// function pointer table but it's made available via the Javascript104// environment. As far as I know, there is no way to make such a Javascript105// function available as a function pointer aside from creating a new compiled106// function in web assembly that calls that Javascript function, so this is107// a fatal error, and we have to modify libc.ts to make such a wrapper. This108// happened with geteuid at one point, which comes from node.js.109//110// 4. The function might be defined in another dynamic library that hasn't111// been loaded yet. We have NOT addressed this problem yet, and this must112// also be a fatal error.113//114let value;115const f = this.getMainInstanceExports()[`__WASM_EXPORT__${key}`];116if (f == null) {117// new function: have to do further work below to add this to table.118this.funcMap[key] = this.functionTable.setLater();119value = this.funcMap[key].index;120} else {121// existing function perhaps from libc, e.g., "strcmp".122value = (f as Function)();123}124log("GOTFuncHandler ", key, "-->", value);125// place in the table -- we make a note of where to put it,126// and actually place it later below after the import is done.127const ptr = new WebAssembly.Global(128{129value: "i32",130mutable: true,131},132value133);134rtn = this.GOT[key] = ptr;135}136return rtn;137}138}139140141