#!/usr/bin/env python3
#
# Generate the jsix style symbol table. The format in memory of this table
# is as follows:
#
# <num_entires> : 8 bytes
# <index>       : 16 * N bytes
# <name data>   : variable
#
# Each index entry has the format
# <symbol address> : 8 bytes
# <offset of name> : 8 bytes
#
# Name offsets are from the start of the symbol table as a whole. (ie,
# where <num_entries> is located.)

def parse_syms(infile):
    """Take the output of the `nm` command, and parse it into a tuple
    representing the symbols in the text segment of the binary. Returns
    a list of (address, symbol_name)."""

    from cxxfilt import demangle, InvalidName

    syms = []
    for line in sys.stdin:
        addr, t, mangled = line.split()
        if t not in "tTvVwW": continue

        try:
            name = demangle(mangled)
        except InvalidName:
            continue

        addr = int(addr, base=16)
        syms.append((addr, name))

    return sorted(syms)


def write_table(syms, outfile):
    """Write the given symbol table as generated by parse_syms()
    to the outfile, index first, and then name character data."""

    import struct

    outfile.write(struct.pack("@Q", len(syms)))
    index_pos = outfile.tell()

    outfile.seek(struct.calcsize("@QQ") * len(syms), 1)
    nul = b'\0'

    positions = {}
    for s in syms:
        addr, name = s
        positions[addr] = outfile.tell()

        data = name.encode('utf-8')
        outfile.write(name.encode('utf-8'))
        outfile.write(nul)

    outfile.seek(index_pos)
    for s in syms:
        addr = s[0]
        pos = positions[addr]
        outfile.write(struct.pack("@QQ", addr, pos))


if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <output>")
        sys.exit(1)

    outfile = open(sys.argv[1], "wb")
    write_table(parse_syms(sys.stdin), outfile)
