summaryrefslogtreecommitdiffstats
path: root/util/wasm/preload/wasm_binary_tools.py
blob: 9a2150b964ba17d64496a0e156598e92241e73fa (plain)
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

#!/usr/bin/env python3
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

import sys
import struct


class WasmBinary:
    """For reference of binary format see Emscripten source code, especially library_dylink.js."""

    def __init__(self, filepath):
        self._offset = 0
        self._end = 0
        self._dependencies = []
        with open(filepath, 'rb') as file:
            self._binary = file.read()
            self._check_preamble()
            self._parse_subsections()

    def get_dependencies(self):
        return self._dependencies

    def _get_leb(self):
        ret = 0
        mul = 1
        while True:
            byte = self._binary[self._offset]
            self._offset += 1
            ret += (byte & 0x7f) * mul
            mul *= 0x80
            if not (byte & 0x80):
                break
        return ret

    def _get_string(self):
        length = self._get_leb()
        self._offset += length
        return self._binary[self._offset - length:self._offset].decode('utf-8')

    def _check_preamble(self):
        preamble = memoryview(self._binary)[:24]
        int32View = struct.unpack('<6I', preamble)
        assert int32View[0] == 0x6d736100, "magic number not found"
        assert self._binary[8] == 0, "dynlink section needs to be first"
        self._offset = 9
        section_size = self._get_leb()
        self._end = self._offset + section_size
        name = self._get_string()
        assert name == "dylink.0", "section dylink.0 not found"

    def _parse_subsections(self):
        WASM_DYLINK_NEEDED = 0x2

        while self._offset < self._end:
            subsection_type = self._binary[self._offset]
            self._offset += 1
            subsection_size = self._get_leb()

            if subsection_type == WASM_DYLINK_NEEDED:
                needed_dynlibs_count = self._get_leb()
                for _ in range(needed_dynlibs_count):
                    self._dependencies.append(self._get_string())
            else:
                self._offset += subsection_size  # we don't care about other sections for now


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python wasm_binary_tools.py <shared_object>")
        sys.exit(1)

    file_path = sys.argv[1]
    binary = WasmBinary(file_path)
    dependencies = binary.get_dependencies()
    for d in dependencies:
        print(d)