Files
velzieandClaude Opus 5.5 c1b0051d02 gexcc: a module's run path, rewritten at install as an ELF's
gexcc-rpath prints or replaces a module's run path (dylink.0's runtime
path), in place and at the same size, as meson's depfixer does an ELF's
DT_RUNPATH at install. gexcc-objdump -p prints it as RUNPATH, so
dpkg-shlibdeps finds a library in a directory the run path names.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_c8c04c00da9a4525ab21b0b9f146c598
2026-10-26 15:58:29 -04:00

137 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""objdump for gexcc's output. A wasm side module (a shared library or
program built with GEXCC_SHARED=1) is described the way GNU objdump
describes an ELF shared object, for the tools that read that (Debian's
dh_makeshlibs and dpkg-shlibdeps, libtool):
-f the file format line (elf32-wasm32)
-p the dynamic section: NEEDED and RUNPATH (dylink.0) and SONAME (the
module name)
-T the dynamic symbols: exports defined, env imports *UND*
-w and -R are accepted (no relocation records), -x is -fpT
Anything else, or any other file, goes to llvm-objdump.
"""
import os, sys
def leb(w, p):
r = sh = 0
while True:
x = w[p]; p += 1
r |= (x & 0x7f) << sh; sh += 7
if not x & 0x80:
return r, p
def name(w, p):
n, p = leb(w, p)
return w[p:p + n].decode('utf-8', 'replace'), p + n
def describe(path):
"""(soname, needed, runpath, defined [(name, kind)], undefined [name])
of a side module, or None for anything else."""
try:
w = open(path, 'rb').read()
except OSError:
return None
if w[:4] != b'\0asm':
return None
soname, needed, runpath, defined, undefined, dylink = None, [], [], [], [], False
p = 8
while p < len(w):
sid = w[p]; n, q = leb(w, p + 1); end = q + n
if sid == 0:
nm, q = name(w, q)
if nm == 'dylink.0':
dylink = True
while q < end:
kind = w[q]; ln, q = leb(w, q + 1); sub_end = q + ln
if kind == 2: # WASM_DYLINK_NEEDED
cnt, r = leb(w, q)
for _ in range(cnt):
lib, r = name(w, r); needed.append(lib)
elif kind == 5: # WASM_DYLINK_RUNTIME_PATH
cnt, r = leb(w, q)
for _ in range(cnt):
d, r = name(w, r); runpath.append(d)
q = sub_end
elif nm == 'name' and w[q] == 0: # module name
_, r = leb(w, q + 1)
soname, _ = name(w, r)
elif sid == 2:
cnt, q = leb(w, q)
for _ in range(cnt):
m, q = name(w, q); f, q = name(w, q)
kind = w[q]; q += 1
if kind == 0:
_, q = leb(w, q)
if m == 'env' or m == 'GOT.func':
undefined.append(f)
elif kind == 1:
q += 1; fl, q = leb(w, q); _, q = leb(w, q)
if fl & 1:
_, q = leb(w, q)
elif kind == 2:
fl, q = leb(w, q); _, q = leb(w, q)
if fl & 1:
_, q = leb(w, q)
elif kind == 3:
q += 2
elif kind == 4:
q += 1; _, q = leb(w, q)
elif sid == 7:
cnt, q = leb(w, q)
for _ in range(cnt):
f, q = name(w, q)
kind = w[q]; q += 1
_, q = leb(w, q)
if kind in (0, 3) and not f.startswith('__'):
defined.append((f, kind))
p = end
if not dylink:
return None
return soname or os.path.basename(path), needed, runpath, defined, sorted(set(undefined))
def main():
args = sys.argv[1:]
flags, files, ok = set(), [], True
for a in args:
if a.startswith('--'):
ok = False
elif a.startswith('-'):
flags |= set(a[1:])
else:
files.append(a)
descs = [describe(f) for f in files]
if not ok or not files or not flags or flags - set('fpTwRx') or any(d is None for d in descs):
os.execvp('llvm-objdump', ['llvm-objdump'] + args)
if 'x' in flags:
flags |= set('fpT')
out = []
for f, (soname, needed, runpath, defined, undefined) in zip(files, descs):
out.append('\n%s: file format elf32-wasm32\n' % f)
if 'f' in flags:
out.append('architecture: wasm32, flags 0x00000150:\nHAS_SYMS, DYNAMIC, D_PAGED\nstart address 0x00000000\n')
if 'p' in flags:
out.append('Dynamic Section:')
out += [' NEEDED %s' % n for n in needed]
out.append(' SONAME %s' % soname)
if runpath:
out.append(' RUNPATH %s' % ':'.join(runpath))
out.append('')
if 'T' in flags:
out.append('DYNAMIC SYMBOL TABLE:')
for s in undefined:
out.append('00000000 DF *UND*\t00000000 Base %s' % s)
for s, kind in defined:
out.append('00000000 g %s .%s\t00000000 Base %s' % ('DF' if kind == 0 else 'DO',
'text' if kind == 0 else 'data', s))
out.append('')
sys.stdout.write('\n'.join(out) + '\n')
main()