69 lines
3.5 KiB
Python
69 lines
3.5 KiB
Python
import argparse
|
|
import base64
|
|
import hashlib
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
|
|
ROOT = Path(__file__).resolve().parents[3]
|
|
RUNTIME = ROOT / 'core/libs/prx/libc/src/exception/Exports.cpp'
|
|
TEST = Path(__file__).with_name('ExceptionRuntime.cpp')
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--prx', type=Path)
|
|
arguments = parser.parse_args()
|
|
|
|
|
|
def nid(symbol):
|
|
digest = hashlib.sha1(symbol.encode() + bytes.fromhex('518d64a635ded8c1e6b039b1c3e55230')).digest()
|
|
return base64.b64encode(digest[:8][::-1]).decode().rstrip('=').replace('/', '-')
|
|
|
|
|
|
def run(*args):
|
|
subprocess.run([str(arg) for arg in args], check=True)
|
|
|
|
|
|
with tempfile.TemporaryDirectory(prefix='anyps5-exceptions-') as directory:
|
|
work = Path(directory)
|
|
for optimization in ('-O0', '-O2'):
|
|
runtime = work / 'runtime.o'
|
|
test = work / 'test.o'
|
|
executable = work / 'test'
|
|
compiler = os.environ.get('CXX', 'c++')
|
|
run(compiler, '-std=c++20', optimization, '-g', '-fPIC', '-c', RUNTIME, '-o', runtime)
|
|
symbols = subprocess.check_output(['nm', '--defined-only', str(runtime)], text=True)
|
|
exports = {line.split()[-1] for line in symbols.splitlines() if line.split()}
|
|
unresolved = subprocess.check_output(['nm', '-u', str(runtime)], text=True)
|
|
forbidden = ('__cxa_throw', '__cxa_begin_catch', '__cxa_end_catch', '__cxa_rethrow',
|
|
'__gxx_personality_v0', '_Unwind_Resume', '_Unwind_RaiseException', '_ZSt9terminatev')
|
|
assert not any(line.split()[-1] in forbidden for line in unresolved.splitlines()), unresolved
|
|
run(compiler, '-std=c++20', optimization, '-g', '-c', TEST, '-o', test)
|
|
unresolved = subprocess.check_output(['nm', '-u', str(test)], text=True)
|
|
mapping = []
|
|
for line in unresolved.splitlines():
|
|
symbol = line.split()[-1]
|
|
if symbol + '_nid_postfix' in exports:
|
|
mapping += ['--redefine-sym', symbol + '=' + symbol + '_nid_postfix']
|
|
run('objcopy', *mapping, test)
|
|
run(compiler, '-g', runtime, test, '-pthread', '-Wl,--eh-frame-hdr', '-o', executable)
|
|
run(executable)
|
|
print(compiler, optimization, 'standalone passed', flush=True)
|
|
if arguments.prx:
|
|
prx = arguments.prx.resolve()
|
|
dynamic = subprocess.check_output(['readelf', '--dyn-syms', '--wide', str(prx)], text=True)
|
|
prx_exports = {parts[7] for line in dynamic.splitlines()
|
|
if len(parts := line.split()) >= 8 and parts[6] != 'UND'}
|
|
expected = {nid(symbol.removesuffix('_nid_postfix')) for symbol in exports if symbol.endswith('_nid_postfix')}
|
|
assert expected <= prx_exports, sorted(expected - prx_exports)
|
|
run(compiler, '-std=c++20', optimization, '-g', '-c', TEST, '-o', test)
|
|
unresolved = subprocess.check_output(['nm', '-u', str(test)], text=True)
|
|
mapping = []
|
|
for line in unresolved.splitlines():
|
|
symbol = line.split()[-1]
|
|
if symbol in exports and symbol.endswith('_nid_postfix') or symbol + '_nid_postfix' in exports:
|
|
mapping += ['--redefine-sym', symbol + '=' + nid(symbol.removesuffix('_nid_postfix'))]
|
|
run('objcopy', *mapping, test)
|
|
run(compiler, '-g', test, prx, '-pthread', '-Wl,--eh-frame-hdr', '-Wl,-rpath,' + str(prx.parent), '-o', executable)
|
|
run(executable)
|
|
print(compiler, optimization, 'NID PRX passed', flush=True)
|