#!/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>       : 24 * N bytes
# <name data>   : variable
#
# Each index entry has the format
# <symbol address> : 8 bytes
# <symbol size>    : 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.)

import re

sym_re = re.compile(r'([0-9a-fA-F]{16}) ([0-9a-fA-F]{16} )?[tTvVwW] (.*)')

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)."""

    syms = []
    for line in sys.stdin:
        match = sym_re.match(line)
        if not match: continue

        addr = int(match.group(1), base=16)
        size = int(match.group(2) or "0", base=16)
        name = match.group(3)

        if size == 0:
            if not "." in name:
                print(f"SYMBOL WARNING: zero size for symbol {name}")
            continue

        syms.append([addr, size, name, 0])

    return 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("@QQQ") * len(syms), 1)
    nul = b'\0'

    for s in syms:
        s[3] = outfile.tell()
        outfile.write(s[2].encode('utf-8'))
        outfile.write(nul)

    outfile.seek(index_pos)
    for s in syms:
        addr = s[0]
        size = s[1]
        pos = s[3]
        outfile.write(struct.pack("@QQQ", addr, size, pos))

    return len(syms)


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

    syms = 0
    size = 0
    with open(sys.argv[1], "wb") as outfile:
        syms = write_table(parse_syms(sys.stdin), outfile)
        outfile.seek(0, 2)
        size = outfile.tell()

    print(f"Wrote {syms} symbols ({size/1024:.1f} KiB) to {sys.argv[1]}.")
