#!/usr/bin/env python3

objdump = 'x86_64-w64-mingw32-objdump'
nm = 'x86_64-w64-mingw32-nm'
addr2line = 'x86_64-w64-mingw32-addr2line'
symbol_prefix = ''

import subprocess
import sys

if len(sys.argv) <= 1:
    print(sys.argv[0] + " exe")
    sys.exit(0)

exe = sys.argv[1]
result = subprocess.run([nm, '-g', '--defined-only', exe], stdout=subprocess.PIPE)

symbols = {}
addresses = {}
for line in result.stdout.decode('utf-8').split('\n'):
    cols = line.split(' ')
    if len(cols) == 3:
        sym = cols[2]
        addr = cols[0]
        symbols[sym] = addr
        addr = addr.lstrip('0').lower()
        if addr in addresses:
            # Duplicate symbols pointing at the same address; prefer __imp_*
            if sym.startswith('__imp'):
                addresses[addr] = sym
        else:
            addresses[addr] = sym

start_sym_name = symbol_prefix + '__RUNTIME_PSEUDO_RELOC_LIST__'
end_sym_name = symbol_prefix + '__RUNTIME_PSEUDO_RELOC_LIST_END__'

if start_sym_name not in symbols or end_sym_name not in symbols:
    print("No pseudo reloc symbols")
    sys.exit(1)

base = None
result = subprocess.run([objdump, '-p', exe], stdout=subprocess.PIPE)
for line in result.stdout.decode('utf-8').split('\n'):
    if line.startswith('ImageBase'):
        base = line.split('\t')[-1]
        break

if base == None:
    print("No ImageBase found")
    sys.exit(1)
base = int(base, 16)

pseudo_relocs_start = int(symbols[start_sym_name], 16)
pseudo_relocs_end = int(symbols[end_sym_name], 16)

# Assuming the modern v2 style pseudo reloc format; skip past 12 byte header.
pseudo_relocs_start = pseudo_relocs_start + 12

def flip(s):
    out = ''
    while len(s) >= 2:
        out = s[0:2] + out
        s = s[2:]
    return out

def get_pseudo_reloc(exe, pos):
    result = subprocess.run([objdump, '-s', exe, '--start-address=%#x' % pos, '--stop-address=%#x' % (pos + 12)], stdout=subprocess.PIPE)
    got_header = False
    for line in result.stdout.decode('utf-8').split('\n'):
        if line.startswith('Contents of section'):
            got_header = True
        elif got_header:
            cols = line.strip().split(' ')
            if len(cols) >= 4:
                return (flip(cols[1]), flip(cols[2]), flip(cols[3]))
            else:
                return None
    return None

def resolve(exe, addr):
    result = subprocess.run([addr2line, '-e', exe, addr], stdout=subprocess.PIPE)
    lines = result.stdout.decode('utf-8').split('\n')
    if len(lines) >= 1 and not '?' in lines[0]:
        return lines[0]
    return None

if pseudo_relocs_start >= pseudo_relocs_end:
    print("No pseudo relocs")
    sys.exit(0)

pos = pseudo_relocs_start
while pos + 12 <= pseudo_relocs_end:
    reloc = get_pseudo_reloc(exe, pos)
    sym_addr = int(reloc[0], 16) + base
    sym_addr_str = "%x" % sym_addr
    reloc_addr = int(reloc[1], 16) + base
    reloc_addr_str = "%#x" % reloc_addr
    size = int(reloc[2], 16) % 256
    sym = sym_addr_str
    if sym_addr_str in addresses:
        sym = addresses[sym_addr_str]
    loc = resolve(exe, reloc_addr_str)
    if loc:
        loc = ", referenced at " + loc
    else:
        loc = ""
    print("Relocation at " + reloc_addr_str + " targeting " + sym + ", " + str(size) + " bits" + loc)
    pos = pos + 12

