1:1 source port of the engine and game code; Windows build and system layer; docs. No game files or SDK headers.
This commit is contained in:
parent
c3b032a45a
commit
0ecb47e9cb
3871 changed files with 91080 additions and 1640063 deletions
3
tools/port/emu_stubs/README.md
Normal file
3
tools/port/emu_stubs/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
One file per area (e.g. `math.py`, `stdio.py`). Each function is named exactly like the imported symbol
|
||||
(`_sinf`, `_vsnprintf`, `__ZdlPv`...), takes the `Game` and ends with `g._ret(value)` (or sets FP registers for
|
||||
float returns). Behave like 2009 Darwin libc on i386.
|
||||
64
tools/port/emu_stubs/math.py
Normal file
64
tools/port/emu_stubs/math.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
"""libm import stubs (float functions) for the emulator, behaving like 2009 Darwin i386 libm.
|
||||
|
||||
i386 float functions return their result in x87 ST0. A Python hook cannot push the x87 stack directly, so the
|
||||
stub stores the float in a scratch word and jumps to a tiny trampoline `flds [scratch]; ret` (the caller's return
|
||||
address is still on the stack, so the trampoline's `ret` returns to the caller, which pops ST0 as usual).
|
||||
|
||||
sinf/cosf: Darwin's libm of that era evaluates them in double precision and rounds to float, i.e. the result is
|
||||
the correctly rounded float of the double result. The double sin/cos of the host libm is used for that.
|
||||
Setting `g.h_libm_host = True` makes the stubs call the host's sinf/cosf instead (the libm a port runs on), so a
|
||||
differential test can separate libm rounding differences from differences in the ported code.
|
||||
Helpers are prefixed h_ so the stub loader (which registers callables starting with a single '_') skips them.
|
||||
"""
|
||||
import ctypes
|
||||
import math
|
||||
import struct
|
||||
|
||||
from unicorn.x86_const import UC_X86_REG_ESP, UC_X86_REG_EIP
|
||||
|
||||
|
||||
def h_f32(x):
|
||||
return struct.unpack('<f', struct.pack('<f', x))[0]
|
||||
|
||||
|
||||
def h_arg_f32(g, i):
|
||||
esp = g.uc.reg_read(UC_X86_REG_ESP)
|
||||
return struct.unpack('<f', bytes(g.uc.mem_read(esp + 4 + 4 * i, 4)))[0]
|
||||
|
||||
|
||||
def h_fld_trampoline(g):
|
||||
"""Returns (code, scratch): code = `flds [scratch]; ret`, placed on its own page, scratch on another page."""
|
||||
t = getattr(g, 'h_fld_tramp', None)
|
||||
if t is None:
|
||||
block = g.malloc(0x3000)
|
||||
code = (block + 0xfff) & ~0xfff
|
||||
scratch = code + 0x1000
|
||||
g.uc.mem_write(code, b'\xd9\x05' + struct.pack('<I', scratch) + b'\xc3')
|
||||
t = g.h_fld_tramp = (code, scratch)
|
||||
return t
|
||||
|
||||
|
||||
def h_ret_f32(g, value):
|
||||
code, scratch = h_fld_trampoline(g)
|
||||
g.uc.mem_write(scratch, struct.pack('<f', value))
|
||||
g.uc.reg_write(UC_X86_REG_EIP, code)
|
||||
|
||||
|
||||
h_libc = ctypes.CDLL(None)
|
||||
for _n in ('sinf', 'cosf'):
|
||||
getattr(h_libc, _n).restype = ctypes.c_float
|
||||
getattr(h_libc, _n).argtypes = [ctypes.c_float]
|
||||
|
||||
|
||||
def h_libm(g, name, model, x):
|
||||
if getattr(g, 'h_libm_host', False):
|
||||
return getattr(h_libc, name)(x)
|
||||
return model(x) if math.isfinite(x) else math.nan
|
||||
|
||||
|
||||
def _sinf(g):
|
||||
h_ret_f32(g, h_libm(g, 'sinf', math.sin, h_arg_f32(g, 0)))
|
||||
|
||||
|
||||
def _cosf(g):
|
||||
h_ret_f32(g, h_libm(g, 'cosf', math.cos, h_arg_f32(g, 0)))
|
||||
366
tools/port/emu_stubs/memfile.py
Normal file
366
tools/port/emu_stubs/memfile.py
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
"""Import stubs for the TMemoryFile / TMemDataSource differential test (tools/port/test_memfile.py).
|
||||
|
||||
Behaves like 2009 Darwin i386 (libSystem, libstdc++ of gcc 4.0):
|
||||
- _vsnprintf: C99 semantics (returns the full length, writes at most n-1 chars plus NUL), integer/string subset.
|
||||
- _pthread_mutex_lock/_unlock: return 0 and count the calls in g.mutex_log (the test checks lock/unlock pairs).
|
||||
- std::_Rb_tree_* free functions of libstdc++ (tree.cc), transcribed from the gcc 4.0 source.
|
||||
- Weak template instantiations that the binary imports through its jump table although it defines them itself
|
||||
(e.g. std::_Rb_tree<str, pair<const str,str>>::erase(const str&)) are bound to the binary's own copy, as dyld
|
||||
coalesces them to the executable's definition.
|
||||
|
||||
emu.py's loader skips names that start with '__' (all C++ imports), so the C++ ones are registered into
|
||||
EXTRA_STUBS directly at load time (see h_register at the bottom).
|
||||
"""
|
||||
import re
|
||||
import struct
|
||||
|
||||
from unicorn.x86_const import UC_X86_REG_EIP
|
||||
|
||||
RED, BLACK = 0, 1
|
||||
|
||||
|
||||
# ---- libc ------------------------------------------------------------------------------------------------------
|
||||
def h_format(g, fmt, ap):
|
||||
out = bytearray()
|
||||
i = 0
|
||||
while i < len(fmt):
|
||||
c = fmt[i]
|
||||
if c != 0x25:
|
||||
out.append(c); i += 1; continue
|
||||
m = re.match(rb'%([-+ #0]*)(\d*)(?:\.(\d+))?(hh|h|ll|l|z|j|t)?([diouxXcs%])', fmt[i:])
|
||||
if not m:
|
||||
raise RuntimeError(f'_vsnprintf: unsupported format {fmt[i:]!r}')
|
||||
flags, width, prec, length, conv = m.groups()
|
||||
i += m.end()
|
||||
if conv == b'%':
|
||||
out += b'%'; continue
|
||||
if length == b'll':
|
||||
v = g.u32(ap) | (g.u32(ap + 4) << 32); ap += 8; bits = 64
|
||||
else:
|
||||
v = g.u32(ap); ap += 4; bits = 32
|
||||
if conv in (b'd', b'i'):
|
||||
if v & (1 << (bits - 1)):
|
||||
v -= 1 << bits
|
||||
body = str(abs(v)).encode()
|
||||
sign = b'-' if v < 0 else (b'+' if b'+' in flags else (b' ' if b' ' in flags else b''))
|
||||
elif conv == b'u':
|
||||
body, sign = str(v).encode(), b''
|
||||
elif conv == b'o':
|
||||
body, sign = format(v, 'o').encode(), b''
|
||||
elif conv in (b'x', b'X'):
|
||||
body, sign = format(v, 'x' if conv == b'x' else 'X').encode(), b''
|
||||
elif conv == b'c':
|
||||
body, sign = bytes([v & 0xff]), b''
|
||||
else: # s
|
||||
body, sign = (g.read_cstr(v) if v else b'(null)'), b''
|
||||
if prec is not None:
|
||||
body = body[:int(prec)]
|
||||
if conv not in (b's', b'c') and prec is not None:
|
||||
body = body.rjust(int(prec), b'0')
|
||||
w = int(width) if width else 0
|
||||
pad = w - len(sign) - len(body)
|
||||
if pad > 0:
|
||||
if b'-' in flags:
|
||||
body = sign + body + b' ' * pad; sign = b''
|
||||
elif b'0' in flags and prec is None and conv not in (b's', b'c'):
|
||||
body = sign + b'0' * pad + body; sign = b''
|
||||
else:
|
||||
body = b' ' * pad + sign + body; sign = b''
|
||||
out += sign + body
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def _vsnprintf(g):
|
||||
buf, n, fmt, ap = g._args(4)
|
||||
text = h_format(g, g.read_cstr(fmt), ap)
|
||||
if n:
|
||||
part = text[:n - 1]
|
||||
g.uc.mem_write(buf, part + b'\0')
|
||||
g._ret(len(text))
|
||||
|
||||
|
||||
def _pthread_mutex_lock(g):
|
||||
(m,) = g._args(1)
|
||||
g.__dict__.setdefault('mutex_log', []).append(('lock', m))
|
||||
g._ret(0)
|
||||
|
||||
|
||||
def _pthread_mutex_unlock(g):
|
||||
(m,) = g._args(1)
|
||||
g.__dict__.setdefault('mutex_log', []).append(('unlock', m))
|
||||
g._ret(0)
|
||||
|
||||
|
||||
# ---- libstdc++ tree.cc (gcc 4.0) -----------------------------------------------------------------------------
|
||||
# _Rb_tree_node_base: +0 color, +4 parent, +8 left, +0xc right.
|
||||
def h_col(g, x): return g.u32(x)
|
||||
def h_par(g, x): return g.u32(x + 4)
|
||||
def h_left(g, x): return g.u32(x + 8)
|
||||
def h_right(g, x): return g.u32(x + 12)
|
||||
def h_scol(g, x, v): g.w32(x, v)
|
||||
def h_spar(g, x, v): g.w32(x + 4, v)
|
||||
def h_sleft(g, x, v): g.w32(x + 8, v)
|
||||
def h_sright(g, x, v): g.w32(x + 12, v)
|
||||
|
||||
|
||||
def h_rotate_left(g, x, root_slot):
|
||||
y = h_right(g, x)
|
||||
h_sright(g, x, h_left(g, y))
|
||||
if h_left(g, y):
|
||||
h_spar(g, h_left(g, y), x)
|
||||
h_spar(g, y, h_par(g, x))
|
||||
if x == g.u32(root_slot):
|
||||
g.w32(root_slot, y)
|
||||
elif x == h_left(g, h_par(g, x)):
|
||||
h_sleft(g, h_par(g, x), y)
|
||||
else:
|
||||
h_sright(g, h_par(g, x), y)
|
||||
h_sleft(g, y, x)
|
||||
h_spar(g, x, y)
|
||||
|
||||
|
||||
def h_rotate_right(g, x, root_slot):
|
||||
y = h_left(g, x)
|
||||
h_sleft(g, x, h_right(g, y))
|
||||
if h_right(g, y):
|
||||
h_spar(g, h_right(g, y), x)
|
||||
h_spar(g, y, h_par(g, x))
|
||||
if x == g.u32(root_slot):
|
||||
g.w32(root_slot, y)
|
||||
elif x == h_right(g, h_par(g, x)):
|
||||
h_sright(g, h_par(g, x), y)
|
||||
else:
|
||||
h_sleft(g, h_par(g, x), y)
|
||||
h_sright(g, y, x)
|
||||
h_spar(g, x, y)
|
||||
|
||||
|
||||
def h_increment(g, x):
|
||||
if h_right(g, x):
|
||||
x = h_right(g, x)
|
||||
while h_left(g, x):
|
||||
x = h_left(g, x)
|
||||
else:
|
||||
y = h_par(g, x)
|
||||
while x == h_right(g, y):
|
||||
x = y
|
||||
y = h_par(g, y)
|
||||
if h_right(g, x) != y:
|
||||
x = y
|
||||
return x
|
||||
|
||||
|
||||
def h_decrement(g, x):
|
||||
if h_col(g, x) == RED and h_par(g, h_par(g, x)) == x:
|
||||
x = h_right(g, x)
|
||||
elif h_left(g, x):
|
||||
y = h_left(g, x)
|
||||
while h_right(g, y):
|
||||
y = h_right(g, y)
|
||||
x = y
|
||||
else:
|
||||
y = h_par(g, x)
|
||||
while x == h_left(g, y):
|
||||
x = y
|
||||
y = h_par(g, y)
|
||||
x = y
|
||||
return x
|
||||
|
||||
|
||||
def rb_increment(g):
|
||||
(x,) = g._args(1); g._ret(h_increment(g, x))
|
||||
|
||||
|
||||
def rb_decrement(g):
|
||||
(x,) = g._args(1); g._ret(h_decrement(g, x))
|
||||
|
||||
|
||||
def rb_insert_and_rebalance(g):
|
||||
insert_left, x, p, header = g._args(4)
|
||||
insert_left &= 0xff
|
||||
root_slot = header + 4
|
||||
h_spar(g, x, p); h_sleft(g, x, 0); h_sright(g, x, 0); h_scol(g, x, RED)
|
||||
if insert_left:
|
||||
h_sleft(g, p, x)
|
||||
if p == header:
|
||||
g.w32(header + 4, x)
|
||||
g.w32(header + 12, x)
|
||||
elif p == g.u32(header + 8):
|
||||
g.w32(header + 8, x)
|
||||
else:
|
||||
h_sright(g, p, x)
|
||||
if p == g.u32(header + 12):
|
||||
g.w32(header + 12, x)
|
||||
while x != g.u32(root_slot) and h_col(g, h_par(g, x)) == RED:
|
||||
xpp = h_par(g, h_par(g, x))
|
||||
if h_par(g, x) == h_left(g, xpp):
|
||||
y = h_right(g, xpp)
|
||||
if y and h_col(g, y) == RED:
|
||||
h_scol(g, h_par(g, x), BLACK); h_scol(g, y, BLACK); h_scol(g, xpp, RED)
|
||||
x = xpp
|
||||
else:
|
||||
if x == h_right(g, h_par(g, x)):
|
||||
x = h_par(g, x)
|
||||
h_rotate_left(g, x, root_slot)
|
||||
h_scol(g, h_par(g, x), BLACK); h_scol(g, xpp, RED)
|
||||
h_rotate_right(g, xpp, root_slot)
|
||||
else:
|
||||
y = h_left(g, xpp)
|
||||
if y and h_col(g, y) == RED:
|
||||
h_scol(g, h_par(g, x), BLACK); h_scol(g, y, BLACK); h_scol(g, xpp, RED)
|
||||
x = xpp
|
||||
else:
|
||||
if x == h_left(g, h_par(g, x)):
|
||||
x = h_par(g, x)
|
||||
h_rotate_right(g, x, root_slot)
|
||||
h_scol(g, h_par(g, x), BLACK); h_scol(g, xpp, RED)
|
||||
h_rotate_left(g, xpp, root_slot)
|
||||
h_scol(g, g.u32(root_slot), BLACK)
|
||||
g._ret(0)
|
||||
|
||||
|
||||
def rb_rebalance_for_erase(g):
|
||||
z, header = g._args(2)
|
||||
root_slot, left_slot, right_slot = header + 4, header + 8, header + 12
|
||||
y = z
|
||||
x = 0
|
||||
x_parent = 0
|
||||
if h_left(g, y) == 0:
|
||||
x = h_right(g, y)
|
||||
elif h_right(g, y) == 0:
|
||||
x = h_left(g, y)
|
||||
else:
|
||||
y = h_right(g, y)
|
||||
while h_left(g, y):
|
||||
y = h_left(g, y)
|
||||
x = h_right(g, y)
|
||||
if y != z:
|
||||
h_spar(g, h_left(g, z), y)
|
||||
h_sleft(g, y, h_left(g, z))
|
||||
if y != h_right(g, z):
|
||||
x_parent = h_par(g, y)
|
||||
if x:
|
||||
h_spar(g, x, h_par(g, y))
|
||||
h_sleft(g, h_par(g, y), x)
|
||||
h_sright(g, y, h_right(g, z))
|
||||
h_spar(g, h_right(g, z), y)
|
||||
else:
|
||||
x_parent = y
|
||||
if g.u32(root_slot) == z:
|
||||
g.w32(root_slot, y)
|
||||
elif h_left(g, h_par(g, z)) == z:
|
||||
h_sleft(g, h_par(g, z), y)
|
||||
else:
|
||||
h_sright(g, h_par(g, z), y)
|
||||
h_spar(g, y, h_par(g, z))
|
||||
cy, cz = h_col(g, y), h_col(g, z)
|
||||
h_scol(g, y, cz); h_scol(g, z, cy)
|
||||
y = z
|
||||
else:
|
||||
x_parent = h_par(g, y)
|
||||
if x:
|
||||
h_spar(g, x, h_par(g, y))
|
||||
if g.u32(root_slot) == z:
|
||||
g.w32(root_slot, x)
|
||||
elif h_left(g, h_par(g, z)) == z:
|
||||
h_sleft(g, h_par(g, z), x)
|
||||
else:
|
||||
h_sright(g, h_par(g, z), x)
|
||||
if g.u32(left_slot) == z:
|
||||
if h_right(g, z) == 0:
|
||||
g.w32(left_slot, h_par(g, z))
|
||||
else:
|
||||
m = x
|
||||
while h_left(g, m):
|
||||
m = h_left(g, m)
|
||||
g.w32(left_slot, m)
|
||||
if g.u32(right_slot) == z:
|
||||
if h_left(g, z) == 0:
|
||||
g.w32(right_slot, h_par(g, z))
|
||||
else:
|
||||
m = x
|
||||
while h_right(g, m):
|
||||
m = h_right(g, m)
|
||||
g.w32(right_slot, m)
|
||||
if h_col(g, y) != RED:
|
||||
while x != g.u32(root_slot) and (x == 0 or h_col(g, x) == BLACK):
|
||||
if x == h_left(g, x_parent):
|
||||
w = h_right(g, x_parent)
|
||||
if h_col(g, w) == RED:
|
||||
h_scol(g, w, BLACK); h_scol(g, x_parent, RED)
|
||||
h_rotate_left(g, x_parent, root_slot)
|
||||
w = h_right(g, x_parent)
|
||||
if (h_left(g, w) == 0 or h_col(g, h_left(g, w)) == BLACK) and \
|
||||
(h_right(g, w) == 0 or h_col(g, h_right(g, w)) == BLACK):
|
||||
h_scol(g, w, RED)
|
||||
x = x_parent
|
||||
x_parent = h_par(g, x_parent)
|
||||
else:
|
||||
if h_right(g, w) == 0 or h_col(g, h_right(g, w)) == BLACK:
|
||||
h_scol(g, h_left(g, w), BLACK); h_scol(g, w, RED)
|
||||
h_rotate_right(g, w, root_slot)
|
||||
w = h_right(g, x_parent)
|
||||
h_scol(g, w, h_col(g, x_parent)); h_scol(g, x_parent, BLACK)
|
||||
if h_right(g, w):
|
||||
h_scol(g, h_right(g, w), BLACK)
|
||||
h_rotate_left(g, x_parent, root_slot)
|
||||
break
|
||||
else:
|
||||
w = h_left(g, x_parent)
|
||||
if h_col(g, w) == RED:
|
||||
h_scol(g, w, BLACK); h_scol(g, x_parent, RED)
|
||||
h_rotate_right(g, x_parent, root_slot)
|
||||
w = h_left(g, x_parent)
|
||||
if (h_right(g, w) == 0 or h_col(g, h_right(g, w)) == BLACK) and \
|
||||
(h_left(g, w) == 0 or h_col(g, h_left(g, w)) == BLACK):
|
||||
h_scol(g, w, RED)
|
||||
x = x_parent
|
||||
x_parent = h_par(g, x_parent)
|
||||
else:
|
||||
if h_left(g, w) == 0 or h_col(g, h_left(g, w)) == BLACK:
|
||||
h_scol(g, h_right(g, w), BLACK); h_scol(g, w, RED)
|
||||
h_rotate_left(g, w, root_slot)
|
||||
w = h_left(g, x_parent)
|
||||
h_scol(g, w, h_col(g, x_parent)); h_scol(g, x_parent, BLACK)
|
||||
if h_left(g, w):
|
||||
h_scol(g, h_left(g, w), BLACK)
|
||||
h_rotate_right(g, x_parent, root_slot)
|
||||
break
|
||||
if x:
|
||||
h_scol(g, x, BLACK)
|
||||
g._ret(y)
|
||||
|
||||
|
||||
def h_forward_to_binary(name):
|
||||
"""A weak symbol the executable imports from itself: jump to its own definition (dyld coalescing)."""
|
||||
def stub(g):
|
||||
g.uc.reg_write(UC_X86_REG_EIP, g.symbols[name])
|
||||
return stub
|
||||
|
||||
|
||||
h_MAP = 'I3strSt4pairIKS0_S0_ESt10_Select1stIS3_ESt4lessIS0_ESaIS3_EE'
|
||||
CXX_STUBS = {
|
||||
'__ZSt18_Rb_tree_incrementPSt18_Rb_tree_node_base': rb_increment,
|
||||
'__ZSt18_Rb_tree_incrementPKSt18_Rb_tree_node_base': rb_increment,
|
||||
'__ZSt18_Rb_tree_decrementPSt18_Rb_tree_node_base': rb_decrement,
|
||||
'__ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_': rb_insert_and_rebalance,
|
||||
'__ZSt28_Rb_tree_rebalance_for_erasePSt18_Rb_tree_node_baseRS_': rb_rebalance_for_erase,
|
||||
}
|
||||
for h_n in ['__ZNSt8_Rb_tree' + h_MAP + '5eraseERS2_',
|
||||
'__ZNSt8_Rb_tree' + h_MAP + '5eraseESt17_Rb_tree_iteratorIS3_ESB_',
|
||||
'__ZNSt8_Rb_tree' + h_MAP + '11upper_boundERS2_',
|
||||
'__ZNSt8_Rb_tree' + h_MAP + '4findERS2_']:
|
||||
CXX_STUBS[h_n] = h_forward_to_binary(h_n)
|
||||
|
||||
|
||||
def h_register():
|
||||
import sys
|
||||
f = sys._getframe(1)
|
||||
while f is not None:
|
||||
if 'EXTRA_STUBS' in f.f_globals and '_load_extra_stubs' in f.f_globals:
|
||||
f.f_globals['EXTRA_STUBS'].update(CXX_STUBS)
|
||||
return
|
||||
f = f.f_back
|
||||
|
||||
|
||||
h_register()
|
||||
440
tools/port/emu_stubs/xml.py
Normal file
440
tools/port/emu_stubs/xml.py
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
"""Import stubs for the simplexml / stringtable differential test (tools/port/test_xml.py).
|
||||
|
||||
Behaviour follows 2009 Darwin libc / libstdc++ (gcc 4.0) on i386:
|
||||
- ctype in the "C" locale (isspace is true only for 9..13 and 32; negative values other than EOF are not space),
|
||||
- strchr also finds the terminating NUL when searching for 0, strncmp returns the unsigned byte difference,
|
||||
- printf/scanf: the conversions used by the SDK (%d %s %c %u %x ...), sscanf returns EOF on an input failure before
|
||||
the first conversion,
|
||||
- std::list node hook/unhook and the red-black tree helpers are ports of libstdc++'s list.cc / tree.cc.
|
||||
|
||||
All stubs are registered into emu.EXTRA_STUBS at the bottom of this file with setdefault, so an equivalent stub that
|
||||
another area's file registered first is kept (emu.py's own loader would let the last file win, and it skips names
|
||||
starting with "__" such as the libstdc++ "__Z..." symbols). The functions therefore carry an "x" prefix.
|
||||
"""
|
||||
import re
|
||||
import struct
|
||||
|
||||
from unicorn.x86_const import UC_X86_REG_ESP
|
||||
|
||||
|
||||
def s32(v):
|
||||
v &= 0xffffffff
|
||||
return v - (1 << 32) if v & 0x80000000 else v
|
||||
|
||||
|
||||
# ---- ctype / string ---------------------------------------------------------------------------------------
|
||||
|
||||
def x_isspace(g):
|
||||
(c,) = g._args(1)
|
||||
c = s32(c)
|
||||
# _CTYPE_S = 0x4000 in Darwin's rune table; only ASCII entries are set in the C locale.
|
||||
g._ret(0x4000 if c in (9, 10, 11, 12, 13, 32) else 0)
|
||||
|
||||
|
||||
def x_strchr(g):
|
||||
s, c = g._args(2)
|
||||
c &= 0xff
|
||||
i = 0
|
||||
while True:
|
||||
b = g.uc.mem_read(s + i, 1)[0]
|
||||
if b == c:
|
||||
g._ret(s + i)
|
||||
return
|
||||
if b == 0:
|
||||
g._ret(0)
|
||||
return
|
||||
i += 1
|
||||
|
||||
|
||||
def x_strncmp(g):
|
||||
a, b, n = g._args(3)
|
||||
for i in range(n):
|
||||
x = g.uc.mem_read(a + i, 1)[0]
|
||||
y = g.uc.mem_read(b + i, 1)[0]
|
||||
if x != y:
|
||||
g._ret(x - y)
|
||||
return
|
||||
if x == 0:
|
||||
break
|
||||
g._ret(0)
|
||||
|
||||
|
||||
def x_strstr(g):
|
||||
h, n = g._args(2)
|
||||
hay = g.read_cstr(h)
|
||||
needle = g.read_cstr(n)
|
||||
i = hay.find(needle)
|
||||
g._ret(h + i if i >= 0 else 0)
|
||||
|
||||
|
||||
# ---- printf / scanf ---------------------------------------------------------------------------------------
|
||||
|
||||
# FreeBSD-derived vfprintf (Darwin): an unknown conversion character is printed as itself, no argument used.
|
||||
_SPEC = re.compile(rb"%([-+ #0']*)(\*|\d+)?(?:\.(\*|\d*))?(hh|h|ll|l|q|L|j|z|t)?(.)", re.S)
|
||||
|
||||
|
||||
def format_c(g, fmt, args):
|
||||
"""printf-style formatting of the bytes `fmt`; `args` yields 32-bit stack words (i386 va_list)."""
|
||||
out = bytearray()
|
||||
pos = 0
|
||||
while True:
|
||||
m = _SPEC.search(fmt, pos)
|
||||
if not m:
|
||||
out += fmt[pos:]
|
||||
return bytes(out)
|
||||
out += fmt[pos:m.start()]
|
||||
pos = m.end()
|
||||
flags, width, prec, length, conv = m.groups()
|
||||
flags = flags.decode().replace("'", '')
|
||||
if conv == b'%':
|
||||
out += b'%'
|
||||
continue
|
||||
if width == b'*':
|
||||
width = s32(next(args))
|
||||
if width < 0:
|
||||
flags += '-'
|
||||
width = -width
|
||||
else:
|
||||
width = int(width) if width else None
|
||||
if prec == b'*':
|
||||
prec = s32(next(args))
|
||||
prec = prec if prec >= 0 else None
|
||||
elif prec is not None:
|
||||
prec = int(prec) if prec else 0
|
||||
if conv in b'di':
|
||||
v = next(args)
|
||||
if length in (b'll', b'q', b'j'):
|
||||
v |= next(args) << 32
|
||||
v = v - (1 << 64) if v & (1 << 63) else v
|
||||
else:
|
||||
v = s32(v)
|
||||
spec = '%' + flags + (str(width) if width else '') + ('.%d' % prec if prec is not None else '') + 'd'
|
||||
out += (spec % v).encode()
|
||||
elif conv in b'ouxX':
|
||||
v = next(args)
|
||||
if length in (b'll', b'q', b'j'):
|
||||
v |= next(args) << 32
|
||||
spec = '%' + flags + (str(width) if width else '') + ('.%d' % prec if prec is not None else '') + \
|
||||
(conv.decode() if conv != b'u' else 'd')
|
||||
out += (spec % v).encode()
|
||||
elif conv == b'c':
|
||||
s = bytes([next(args) & 0xff])
|
||||
out += s.rjust(width or 0) if '-' not in flags else s.ljust(width or 0)
|
||||
elif conv == b's':
|
||||
p = next(args)
|
||||
s = g.read_cstr(p) if p else b'(null)'
|
||||
if prec is not None:
|
||||
s = s[:prec]
|
||||
out += s.rjust(width or 0) if '-' not in flags else s.ljust(width or 0)
|
||||
elif conv == b'p':
|
||||
out += b'0x%x' % next(args)
|
||||
elif conv == b'n':
|
||||
next(args)
|
||||
else:
|
||||
out += conv
|
||||
|
||||
|
||||
def arg_words(g, ptr):
|
||||
while True:
|
||||
yield g.u32(ptr)
|
||||
ptr += 4
|
||||
|
||||
|
||||
def x_vsnprintf(g):
|
||||
buf, size, fmt, va = g._args(4)
|
||||
s = format_c(g, g.read_cstr(fmt), arg_words(g, va))
|
||||
if size:
|
||||
n = min(len(s), size - 1)
|
||||
g.uc.mem_write(buf, s[:n] + b'\0')
|
||||
g._ret(len(s))
|
||||
|
||||
|
||||
def x_vasprintf(g):
|
||||
ret, fmt, va = g._args(3)
|
||||
s = format_c(g, g.read_cstr(fmt), arg_words(g, va))
|
||||
p = g.malloc(len(s) + 1)
|
||||
g.uc.mem_write(p, s + b'\0')
|
||||
g.w32(ret, p)
|
||||
g._ret(len(s))
|
||||
|
||||
|
||||
def x_sscanf(g):
|
||||
src, fmt = g._args(2)
|
||||
esp_args = arg_words(g, g.uc.reg_read(UC_X86_REG_ESP) + 12)
|
||||
s = g.read_cstr(src)
|
||||
f = g.read_cstr(fmt)
|
||||
si = 0
|
||||
fi = 0
|
||||
count = 0
|
||||
ws = b' \t\n\v\f\r'
|
||||
|
||||
def result(input_failure):
|
||||
return -1 if (input_failure and count == 0) else count
|
||||
|
||||
while fi < len(f):
|
||||
fc = f[fi:fi + 1]
|
||||
if fc in ws:
|
||||
while si < len(s) and s[si:si + 1] in ws:
|
||||
si += 1
|
||||
fi += 1
|
||||
continue
|
||||
if fc != b'%':
|
||||
if si >= len(s):
|
||||
g._ret(result(True)); return
|
||||
if s[si:si + 1] != fc:
|
||||
g._ret(result(False)); return
|
||||
si += 1
|
||||
fi += 1
|
||||
continue
|
||||
m = re.match(rb'%(\*)?(\d+)?(hh|h|l|ll)?([dis%])', f[fi:])
|
||||
if not m:
|
||||
raise ValueError('unsupported scanf format %r' % f)
|
||||
fi += m.end()
|
||||
suppress, width, _length, conv = m.groups()
|
||||
if conv == b'%':
|
||||
while si < len(s) and s[si:si + 1] in ws:
|
||||
si += 1
|
||||
if si >= len(s):
|
||||
g._ret(result(True)); return
|
||||
if s[si:si + 1] != b'%':
|
||||
g._ret(result(False)); return
|
||||
si += 1
|
||||
continue
|
||||
while si < len(s) and s[si:si + 1] in ws:
|
||||
si += 1
|
||||
if si >= len(s):
|
||||
g._ret(result(True)); return
|
||||
width = int(width) if width else 1 << 30
|
||||
if conv == b's':
|
||||
start = si
|
||||
while si < len(s) and si - start < width and s[si:si + 1] not in ws:
|
||||
si += 1
|
||||
if not suppress:
|
||||
p = next(esp_args)
|
||||
g.uc.mem_write(p, s[start:si] + b'\0')
|
||||
count += 1
|
||||
continue
|
||||
start = si
|
||||
if si < len(s) and si - start < width and s[si:si + 1] in b'+-':
|
||||
si += 1
|
||||
dstart = si
|
||||
while si < len(s) and si - start < width and 48 <= s[si] <= 57:
|
||||
si += 1
|
||||
if si == dstart:
|
||||
g._ret(result(False)); return
|
||||
if not suppress:
|
||||
p = next(esp_args)
|
||||
g.uc.mem_write(p, struct.pack('<i', s32(int(s[start:si]))))
|
||||
count += 1
|
||||
g._ret(count)
|
||||
|
||||
|
||||
# ---- WriteDbg's ostream output (recorded on the Game object as g.dbg_log) -----------------------------------
|
||||
|
||||
def __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(g):
|
||||
os_, s = g._args(2)
|
||||
if not hasattr(g, 'dbg_log'):
|
||||
g.dbg_log = []
|
||||
g.dbg_log.append(g.read_cstr(s))
|
||||
g._ret(os_)
|
||||
|
||||
|
||||
def __ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(g):
|
||||
(os_,) = g._args(1)
|
||||
g._ret(os_)
|
||||
|
||||
|
||||
# ---- libstdc++ list.cc ------------------------------------------------------------------------------------
|
||||
|
||||
def __ZNSt15_List_node_base4hookEPS_(g):
|
||||
node, pos = g._args(2)
|
||||
prev = g.u32(pos + 4)
|
||||
g.w32(node, pos)
|
||||
g.w32(node + 4, prev)
|
||||
g.w32(prev, node)
|
||||
g.w32(pos + 4, node)
|
||||
g._ret(0)
|
||||
|
||||
|
||||
def __ZNSt15_List_node_base6unhookEv(g):
|
||||
(node,) = g._args(1)
|
||||
nxt = g.u32(node)
|
||||
prev = g.u32(node + 4)
|
||||
g.w32(prev, nxt)
|
||||
g.w32(nxt + 4, prev)
|
||||
g._ret(0)
|
||||
|
||||
|
||||
# ---- libstdc++ tree.cc (node: color +0 (red 0, black 1), parent +4, left +8, right +0xc) ---------------------
|
||||
|
||||
RED, BLACK = 0, 1
|
||||
|
||||
|
||||
def rb_col(g, x): return g.u32(x)
|
||||
def rb_par(g, x): return g.u32(x + 4)
|
||||
def rb_lft(g, x): return g.u32(x + 8)
|
||||
def rb_rgt(g, x): return g.u32(x + 12)
|
||||
|
||||
|
||||
def rb_increment(g, x):
|
||||
if rb_rgt(g, x):
|
||||
x = rb_rgt(g, x)
|
||||
while rb_lft(g, x):
|
||||
x = rb_lft(g, x)
|
||||
else:
|
||||
y = rb_par(g, x)
|
||||
while x == rb_rgt(g, y):
|
||||
x = y
|
||||
y = rb_par(g, y)
|
||||
if rb_rgt(g, x) != y:
|
||||
x = y
|
||||
return x
|
||||
|
||||
|
||||
def rb_decrement(g, x):
|
||||
if rb_col(g, x) == RED and rb_par(g, rb_par(g, x)) == x:
|
||||
x = rb_rgt(g, x)
|
||||
elif rb_lft(g, x):
|
||||
y = rb_lft(g, x)
|
||||
while rb_rgt(g, y):
|
||||
y = rb_rgt(g, y)
|
||||
x = y
|
||||
else:
|
||||
y = rb_par(g, x)
|
||||
while x == rb_lft(g, y):
|
||||
x = y
|
||||
y = rb_par(g, y)
|
||||
x = y
|
||||
return x
|
||||
|
||||
|
||||
def __ZSt18_Rb_tree_incrementPSt18_Rb_tree_node_base(g):
|
||||
(x,) = g._args(1); g._ret(rb_increment(g, x))
|
||||
|
||||
|
||||
def __ZSt18_Rb_tree_incrementPKSt18_Rb_tree_node_base(g):
|
||||
(x,) = g._args(1); g._ret(rb_increment(g, x))
|
||||
|
||||
|
||||
def __ZSt18_Rb_tree_decrementPSt18_Rb_tree_node_base(g):
|
||||
(x,) = g._args(1); g._ret(rb_decrement(g, x))
|
||||
|
||||
|
||||
def rb_rotate_left(g, x, header):
|
||||
y = rb_rgt(g, x)
|
||||
g.w32(x + 12, rb_lft(g, y))
|
||||
if rb_lft(g, y):
|
||||
g.w32(rb_lft(g, y) + 4, x)
|
||||
g.w32(y + 4, rb_par(g, x))
|
||||
if x == g.u32(header + 4):
|
||||
g.w32(header + 4, y)
|
||||
elif x == rb_lft(g, rb_par(g, x)):
|
||||
g.w32(rb_par(g, x) + 8, y)
|
||||
else:
|
||||
g.w32(rb_par(g, x) + 12, y)
|
||||
g.w32(y + 8, x)
|
||||
g.w32(x + 4, y)
|
||||
|
||||
|
||||
def rb_rotate_right(g, x, header):
|
||||
y = rb_lft(g, x)
|
||||
g.w32(x + 8, rb_rgt(g, y))
|
||||
if rb_rgt(g, y):
|
||||
g.w32(rb_rgt(g, y) + 4, x)
|
||||
g.w32(y + 4, rb_par(g, x))
|
||||
if x == g.u32(header + 4):
|
||||
g.w32(header + 4, y)
|
||||
elif x == rb_rgt(g, rb_par(g, x)):
|
||||
g.w32(rb_par(g, x) + 12, y)
|
||||
else:
|
||||
g.w32(rb_par(g, x) + 8, y)
|
||||
g.w32(y + 12, x)
|
||||
g.w32(x + 4, y)
|
||||
|
||||
|
||||
def __ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_(g):
|
||||
insert_left, x, p, header = g._args(4)
|
||||
insert_left &= 0xff
|
||||
g.w32(x + 4, p)
|
||||
g.w32(x + 8, 0)
|
||||
g.w32(x + 12, 0)
|
||||
g.w32(x, RED)
|
||||
if insert_left:
|
||||
g.w32(p + 8, x)
|
||||
if p == header:
|
||||
g.w32(header + 4, x)
|
||||
g.w32(header + 12, x)
|
||||
elif p == g.u32(header + 8):
|
||||
g.w32(header + 8, x)
|
||||
else:
|
||||
g.w32(p + 12, x)
|
||||
if p == g.u32(header + 12):
|
||||
g.w32(header + 12, x)
|
||||
while x != g.u32(header + 4) and rb_col(g, rb_par(g, x)) == RED:
|
||||
xpp = rb_par(g, rb_par(g, x))
|
||||
if rb_par(g, x) == rb_lft(g, xpp):
|
||||
y = rb_rgt(g, xpp)
|
||||
if y and rb_col(g, y) == RED:
|
||||
g.w32(rb_par(g, x), BLACK)
|
||||
g.w32(y, BLACK)
|
||||
g.w32(xpp, RED)
|
||||
x = xpp
|
||||
else:
|
||||
if x == rb_rgt(g, rb_par(g, x)):
|
||||
x = rb_par(g, x)
|
||||
rb_rotate_left(g, x, header)
|
||||
g.w32(rb_par(g, x), BLACK)
|
||||
g.w32(xpp, RED)
|
||||
rb_rotate_right(g, xpp, header)
|
||||
else:
|
||||
y = rb_lft(g, xpp)
|
||||
if y and rb_col(g, y) == RED:
|
||||
g.w32(rb_par(g, x), BLACK)
|
||||
g.w32(y, BLACK)
|
||||
g.w32(xpp, RED)
|
||||
x = xpp
|
||||
else:
|
||||
if x == rb_lft(g, rb_par(g, x)):
|
||||
x = rb_par(g, x)
|
||||
rb_rotate_right(g, x, header)
|
||||
g.w32(rb_par(g, x), BLACK)
|
||||
g.w32(xpp, RED)
|
||||
rb_rotate_left(g, xpp, header)
|
||||
g.w32(g.u32(header + 4), BLACK)
|
||||
g._ret(0)
|
||||
|
||||
|
||||
# ---- coalesced (weak) template instantiations --------------------------------------------------------------
|
||||
# The game calls these std::map<str,str> members through __jump_table stubs although it defines them itself; dyld
|
||||
# binds a coalesced symbol to the main executable's definition, so the stub continues at the local code.
|
||||
COALESCED_LOCAL = [
|
||||
'__ZNSt8_Rb_treeI3strSt4pairIKS0_S0_ESt10_Select1stIS3_ESt4lessIS0_ESaIS3_EE4findERS2_',
|
||||
'__ZNSt8_Rb_treeI3strSt4pairIKS0_S0_ESt10_Select1stIS3_ESt4lessIS0_ESaIS3_EE11upper_boundERS2_',
|
||||
'__ZNSt8_Rb_treeI3strSt4pairIKS0_S0_ESt10_Select1stIS3_ESt4lessIS0_ESaIS3_EE5eraseESt17_Rb_tree_iteratorIS3_ESB_',
|
||||
'__ZNSt8_Rb_treeI3strSt4pairIKS0_S0_ESt10_Select1stIS3_ESt4lessIS0_ESaIS3_EE5eraseERS2_',
|
||||
]
|
||||
|
||||
|
||||
def make_local_jump(name):
|
||||
def stub(g):
|
||||
from unicorn.x86_const import UC_X86_REG_EIP
|
||||
g.uc.reg_write(UC_X86_REG_EIP, g.symbols[name])
|
||||
return stub
|
||||
|
||||
|
||||
# ---- registration of the "__Z..." names (skipped by emu.py's loader) ---------------------------------------
|
||||
|
||||
def register_cxx():
|
||||
import sys
|
||||
names = {k[1:]: v for k, v in globals().items() if k.startswith('x_') and callable(v)}
|
||||
names.update({k: v for k, v in globals().items() if k.startswith('__Z') and callable(v)})
|
||||
for n in COALESCED_LOCAL:
|
||||
names[n] = make_local_jump(n)
|
||||
for mod in list(sys.modules.values()):
|
||||
stubs = getattr(mod, 'EXTRA_STUBS', None)
|
||||
if isinstance(stubs, dict):
|
||||
for k, v in names.items():
|
||||
stubs.setdefault(k, v)
|
||||
|
||||
|
||||
register_cxx()
|
||||
Loading…
Add table
Add a link
Reference in a new issue