mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-05-24 19:54:22 -04:00
erpt: add ids (and autogeneration tool)
This commit is contained in:
parent
065485b971
commit
5062329979
5 changed files with 1919 additions and 0 deletions
404
utilities/erpt.py
Normal file
404
utilities/erpt.py
Normal file
|
@ -0,0 +1,404 @@
|
|||
#
|
||||
# Copyright (c) 2018-2020 Atmosphère-NX
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms and conditions of the GNU General Public License,
|
||||
# version 2, as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# erpt.py: Autogeneration tool for <stratosphere/erpt/erpt_ids.autogen.hpp>
|
||||
|
||||
import nxo64
|
||||
import sys, os, string
|
||||
from struct import unpack as up, pack as pk
|
||||
|
||||
LOAD_BASE = 0x7100000000
|
||||
|
||||
HEADER = '''/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
/* NOTE: This file is auto-generated. */
|
||||
/* Do not make edits to this file by hand. */
|
||||
|
||||
'''
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
iter_range = range
|
||||
int_types = (int,)
|
||||
ascii_string = lambda b: b.decode('ascii')
|
||||
bytes_to_list = lambda b: list(b)
|
||||
list_to_bytes = lambda l: bytes(l)
|
||||
else:
|
||||
iter_range = xrange
|
||||
int_types = (int, long)
|
||||
ascii_string = lambda b: str(b)
|
||||
bytes_to_list = lambda b: map(ord, b)
|
||||
list_to_bytes = lambda l: ''.join(map(chr, l))
|
||||
|
||||
(DT_NULL, DT_NEEDED, DT_PLTRELSZ, DT_PLTGOT, DT_HASH, DT_STRTAB, DT_SYMTAB, DT_RELA, DT_RELASZ,
|
||||
DT_RELAENT, DT_STRSZ, DT_SYMENT, DT_INIT, DT_FINI, DT_SONAME, DT_RPATH, DT_SYMBOLIC, DT_REL,
|
||||
DT_RELSZ, DT_RELENT, DT_PLTREL, DT_DEBUG, DT_TEXTREL, DT_JMPREL, DT_BIND_NOW, DT_INIT_ARRAY,
|
||||
DT_FINI_ARRAY, DT_INIT_ARRAYSZ, DT_FINI_ARRAYSZ, DT_RUNPATH, DT_FLAGS) = iter_range(31)
|
||||
DT_GNU_HASH = 0x6ffffef5
|
||||
DT_VERSYM = 0x6ffffff0
|
||||
DT_RELACOUNT = 0x6ffffff9
|
||||
DT_RELCOUNT = 0x6ffffffa
|
||||
DT_FLAGS_1 = 0x6ffffffb
|
||||
DT_VERDEF = 0x6ffffffc
|
||||
DT_VERDEFNUM = 0x6ffffffd
|
||||
|
||||
STT_NOTYPE = 0
|
||||
STT_OBJECT = 1
|
||||
STT_FUNC = 2
|
||||
STT_SECTION = 3
|
||||
|
||||
STB_LOCAL = 0
|
||||
STB_GLOBAL = 1
|
||||
STB_WEAK = 2
|
||||
|
||||
R_ARM_ABS32 = 2
|
||||
R_ARM_TLS_DESC = 13
|
||||
R_ARM_GLOB_DAT = 21
|
||||
R_ARM_JUMP_SLOT = 22
|
||||
R_ARM_RELATIVE = 23
|
||||
|
||||
R_AARCH64_ABS64 = 257
|
||||
R_AARCH64_GLOB_DAT = 1025
|
||||
R_AARCH64_JUMP_SLOT = 1026
|
||||
R_AARCH64_RELATIVE = 1027
|
||||
R_AARCH64_TLSDESC = 1031
|
||||
|
||||
CATEGORIES = {
|
||||
0 : 'Test',
|
||||
1 : 'ErrorInfo',
|
||||
2 : 'ConnectionStatusInfo',
|
||||
3 : 'NetworkInfo',
|
||||
4 : 'NXMacAddressInfo',
|
||||
5 : 'StealthNetworkInfo',
|
||||
6 : 'LimitHighCapacityInfo',
|
||||
7 : 'NATTypeInfo',
|
||||
8 : 'WirelessAPMacAddressInfo',
|
||||
9 : 'GlobalIPAddressInfo',
|
||||
10 : 'EnableWirelessInterfaceInfo',
|
||||
11 : 'EnableWifiInfo',
|
||||
12 : 'EnableBluetoothInfo',
|
||||
13 : 'EnableNFCInfo',
|
||||
14 : 'NintendoZoneSSIDListVersionInfo',
|
||||
15 : 'LANAdapterMacAddressInfo',
|
||||
16 : 'ApplicationInfo',
|
||||
17 : 'OccurrenceInfo',
|
||||
18 : 'ProductModelInfo',
|
||||
19 : 'CurrentLanguageInfo',
|
||||
20 : 'UseNetworkTimeProtocolInfo',
|
||||
21 : 'TimeZoneInfo',
|
||||
22 : 'ControllerFirmwareInfo',
|
||||
23 : 'VideoOutputInfo',
|
||||
24 : 'NANDFreeSpaceInfo',
|
||||
25 : 'SDCardFreeSpaceInfo',
|
||||
26 : 'ScreenBrightnessInfo',
|
||||
27 : 'AudioFormatInfo',
|
||||
28 : 'MuteOnHeadsetUnpluggedInfo',
|
||||
29 : 'NumUserRegisteredInfo',
|
||||
30 : 'DataDeletionInfo',
|
||||
31 : 'ControllerVibrationInfo',
|
||||
32 : 'LockScreenInfo',
|
||||
33 : 'InternalBatteryLotNumberInfo',
|
||||
34 : 'LeftControllerSerialNumberInfo',
|
||||
35 : 'RightControllerSerialNumberInfo',
|
||||
36 : 'NotificationInfo',
|
||||
37 : 'TVInfo',
|
||||
38 : 'SleepInfo',
|
||||
39 : 'ConnectionInfo',
|
||||
40 : 'NetworkErrorInfo',
|
||||
41 : 'FileAccessPathInfo',
|
||||
42 : 'GameCardCIDInfo',
|
||||
43 : 'NANDCIDInfo',
|
||||
44 : 'MicroSDCIDInfo',
|
||||
45 : 'NANDSpeedModeInfo',
|
||||
46 : 'MicroSDSpeedModeInfo',
|
||||
47 : 'GameCardSpeedModeInfo',
|
||||
48 : 'UserAccountInternalIDInfo',
|
||||
49 : 'NetworkServiceAccountInternalIDInfo',
|
||||
50 : 'NintendoAccountInternalIDInfo',
|
||||
51 : 'USB3AvailableInfo',
|
||||
52 : 'CallStackInfo',
|
||||
53 : 'SystemStartupLogInfo',
|
||||
54 : 'RegionSettingInfo',
|
||||
55 : 'NintendoZoneConnectedInfo',
|
||||
56 : 'ForceSleepInfo',
|
||||
57 : 'ChargerInfo',
|
||||
58 : 'RadioStrengthInfo',
|
||||
59 : 'ErrorInfoAuto',
|
||||
60 : 'AccessPointInfo',
|
||||
61 : 'ErrorInfoDefaults',
|
||||
62 : 'SystemPowerStateInfo',
|
||||
63 : 'PerformanceInfo',
|
||||
64 : 'ThrottlingInfo',
|
||||
65 : 'GameCardErrorInfo',
|
||||
66 : 'EdidInfo',
|
||||
67 : 'ThermalInfo',
|
||||
68 : 'CradleFirmwareInfo',
|
||||
69 : 'RunningApplicationInfo',
|
||||
70 : 'RunningAppletInfo',
|
||||
71 : 'FocusedAppletHistoryInfo',
|
||||
72 : 'CompositorInfo',
|
||||
73 : 'BatteryChargeInfo',
|
||||
74 : 'NANDExtendedCsd',
|
||||
75 : 'NANDPatrolInfo',
|
||||
76 : 'NANDErrorInfo',
|
||||
77 : 'NANDDriverLog',
|
||||
78 : 'SdCardSizeSpec',
|
||||
79 : 'SdCardErrorInfo',
|
||||
80 : 'SdCardDriverLog ',
|
||||
81 : 'FsProxyErrorInfo',
|
||||
82 : 'SystemAppletSceneInfo',
|
||||
83 : 'VideoInfo',
|
||||
84 : 'GpuErrorInfo',
|
||||
85 : 'PowerClockInfo',
|
||||
86 : 'AdspErrorInfo',
|
||||
87 : 'NvDispDeviceInfo',
|
||||
88 : 'NvDispDcWindowInfo',
|
||||
89 : 'NvDispDpModeInfo',
|
||||
90 : 'NvDispDpLinkSpec',
|
||||
91 : 'NvDispDpLinkStatus',
|
||||
92 : 'NvDispDpHdcpInfo',
|
||||
93 : 'NvDispDpAuxCecInfo',
|
||||
94 : 'NvDispDcInfo',
|
||||
95 : 'NvDispDsiInfo',
|
||||
96 : 'NvDispErrIDInfo',
|
||||
97 : 'SdCardMountInfo',
|
||||
98 : 'RetailInteractiveDisplayInfo',
|
||||
99 : 'CompositorStateInfo',
|
||||
100 : 'CompositorLayerInfo',
|
||||
101 : 'CompositorDisplayInfo',
|
||||
102 : 'CompositorHWCInfo',
|
||||
103 : 'MonitorCapability',
|
||||
104 : 'ErrorReportSharePermissionInfo',
|
||||
105 : 'MultimediaInfo',
|
||||
106 : 'ConnectedControllerInfo',
|
||||
107 : 'FsMemoryInfo',
|
||||
108 : 'UserClockContextInfo',
|
||||
109 : 'NetworkClockContextInfo',
|
||||
110 : 'AcpGeneralSettingsInfo',
|
||||
111 : 'AcpPlayLogSettingsInfo',
|
||||
112 : 'AcpAocSettingsInfo',
|
||||
113 : 'AcpBcatSettingsInfo',
|
||||
114 : 'AcpStorageSettingsInfo',
|
||||
115 : 'AcpRatingSettingsInfo',
|
||||
116 : 'MonitorSettings',
|
||||
117 : 'RebootlessSystemUpdateVersionInfo',
|
||||
118 : 'NifmConnectionTestInfo',
|
||||
119 : 'PcieLoggedStateInfo',
|
||||
120 : 'NetworkSecurityCertificateInfo',
|
||||
121 : 'AcpNeighborDetectionInfo',
|
||||
122 : 'GpuCrashInfo',
|
||||
123 : 'UsbStateInfo',
|
||||
124 : 'NvHostErrInfo',
|
||||
125 : 'RunningUlaInfo',
|
||||
}
|
||||
|
||||
FIELD_TYPES = {
|
||||
0 : 'FieldType_NumericU64',
|
||||
1 : 'FieldType_NumericU32',
|
||||
2 : 'FieldType_NumericI64',
|
||||
3 : 'FieldType_NumericI32',
|
||||
4 : 'FieldType_String',
|
||||
5 : 'FieldType_U8Array',
|
||||
6 : 'FieldType_U32Array',
|
||||
7 : 'FieldType_U64Array',
|
||||
8 : 'FieldType_I32Array',
|
||||
9 : 'FieldType_I64Array',
|
||||
10 : 'FieldType_Bool',
|
||||
11 : 'FieldType_NumericU16',
|
||||
12 : 'FieldType_NumericU8',
|
||||
13 : 'FieldType_NumericI16',
|
||||
14 : 'FieldType_NumericI8',
|
||||
15 : 'FieldType_I8Array',
|
||||
}
|
||||
|
||||
def get_full(nxo):
|
||||
full = nxo.text[0]
|
||||
if nxo.ro[2] >= len(full):
|
||||
full += b'\x00' * (nxo.ro[2] - len(full))
|
||||
else:
|
||||
full = full[:nxo.ro[2]]
|
||||
full += nxo.ro[0]
|
||||
if nxo.data[2] > len(full):
|
||||
full += b'\x00' * (nxo.data[2] - len(full))
|
||||
full += nxo.data[0]
|
||||
|
||||
undef_count = 0
|
||||
for s in nxo.symbols:
|
||||
if not s.shndx and s.name:
|
||||
undef_count += 1
|
||||
last_ea = max(LOAD_BASE + end for start, end, name, kind in nxo.sections)
|
||||
undef_entry_size = 8
|
||||
undef_ea = ((last_ea + 0xFFF) & ~0xFFF) + undef_entry_size # plus 8 so we don't end up on the "end" symbol
|
||||
|
||||
for i,s in enumerate(nxo.symbols):
|
||||
if not s.shndx and s.name:
|
||||
#idaapi.create_data(undef_ea, idc.FF_QWORD, 8, idaapi.BADADDR)
|
||||
#idaapi.force_name(undef_ea, s.name)
|
||||
s.resolved = undef_ea
|
||||
undef_ea += undef_entry_size
|
||||
elif i != 0:
|
||||
assert s.shndx
|
||||
s.resolved = LOAD_BASE + s.value
|
||||
if s.name:
|
||||
if s.type == STT_FUNC:
|
||||
print(hex(s.resolved), s.name)
|
||||
idaapi.add_entry(s.resolved, s.resolved, s.name, 0)
|
||||
else:
|
||||
idaapi.force_name(s.resolved, s.name)
|
||||
|
||||
else:
|
||||
# NULL symbol
|
||||
s.resolved = 0
|
||||
|
||||
def put_dword(z, target, val):
|
||||
return z[:target] + pk('<I', val) + z[target+4:]
|
||||
def put_qword(z, target, val):
|
||||
return z[:target] + pk('<Q', val) + z[target+8:]
|
||||
def get_dword(z, target):
|
||||
return up('<I', z[target:target+4])[0]
|
||||
|
||||
for offset, r_type, sym, addend in nxo.relocations:
|
||||
#print offset, r_type, sym, addend
|
||||
target = offset + LOAD_BASE
|
||||
if r_type in (R_ARM_GLOB_DAT, R_ARM_JUMP_SLOT, R_ARM_ABS32):
|
||||
if not sym:
|
||||
print('error: relocation at %X failed' % target)
|
||||
else:
|
||||
full = put_dword(full, target, sym.resolved)
|
||||
elif r_type == R_ARM_RELATIVE:
|
||||
full = put_dword(full, target, get_dword(full, target) + LOAD_BASE)
|
||||
elif r_type in (R_AARCH64_GLOB_DAT, R_AARCH64_JUMP_SLOT, R_AARCH64_ABS64):
|
||||
full = put_qword(full, target, sym.resolved + addend)
|
||||
elif r_type == R_AARCH64_RELATIVE:
|
||||
full = put_qword(full, target, LOAD_BASE + addend)
|
||||
else:
|
||||
print('TODO r_type %d' % (r_type,))
|
||||
return full
|
||||
|
||||
def locate_fields(full):
|
||||
start = ['TestU64', 'TestU32', 'TestI64', 'TestI32']
|
||||
inds = [LOAD_BASE + full.index('%s\x00' % s) for s in start]
|
||||
target = pk('<QQQQ', inds[0], inds[1], inds[2], inds[3])
|
||||
return full.index(target)
|
||||
|
||||
def read_string(full, ofs):
|
||||
s = ''
|
||||
if ofs >= len(full):
|
||||
return ''
|
||||
while full[ofs] != '\x00':
|
||||
s += full[ofs]
|
||||
ofs += 1
|
||||
if ofs >= len(full):
|
||||
return ''
|
||||
return s
|
||||
|
||||
def is_valid_field_name(s):
|
||||
ALLOWED = string.lowercase + string.uppercase + string.digits + '_'
|
||||
if not s:
|
||||
return False
|
||||
for c in s:
|
||||
if not c in ALLOWED:
|
||||
return False
|
||||
return True
|
||||
|
||||
def parse_fields(full, table):
|
||||
fields = []
|
||||
ofs = 0
|
||||
while True:
|
||||
val = up('<Q', full[table + ofs:table + ofs + 8])[0]
|
||||
if (val & 0xFFFFFFFF00000000) != LOAD_BASE:
|
||||
break
|
||||
s = read_string(full, val - LOAD_BASE)
|
||||
if not is_valid_field_name(s):
|
||||
break
|
||||
fields.append(s)
|
||||
ofs += 8
|
||||
return fields
|
||||
|
||||
def find_categories(full, num_fields):
|
||||
KNOWN = [0] * 10 + [1] * 2 + [0x3B] * 2
|
||||
ind = full.index(''.join(pk('<I', i) for i in KNOWN))
|
||||
return list(up('<'+'I'*num_fields, full[ind:ind+4*num_fields]))
|
||||
|
||||
def find_types(full, num_fields):
|
||||
KNOWN = range(10) + [4, 4, 2, 4]
|
||||
ind = full.index(''.join(pk('<I', i) for i in KNOWN))
|
||||
return list(up('<'+'I'*num_fields, full[ind:ind+4*num_fields]))
|
||||
|
||||
def cat_to_string(c):
|
||||
return CATEGORIES[c] if c in CATEGORIES else 'Category_Unknown%d' % c
|
||||
|
||||
def typ_to_string(t):
|
||||
return FIELD_TYPES[t] if t in FIELD_TYPES else 'FieldType_Unknown%d' % t
|
||||
|
||||
def main(argc, argv):
|
||||
if argc != 2:
|
||||
print 'Usage: %s erpt_nso' % argv[0]
|
||||
return 1
|
||||
f = open(argv[1], 'rb')
|
||||
nxo = nxo64.load_nxo(f)
|
||||
full = get_full(nxo)
|
||||
field_table = locate_fields(full)
|
||||
fields = parse_fields(full, field_table)
|
||||
NUM_FIELDS = len(fields)
|
||||
cats = find_categories(full, NUM_FIELDS)
|
||||
types = find_types(full, NUM_FIELDS)
|
||||
print 'Identified %d fields.' % NUM_FIELDS
|
||||
mf = max(len(s) for s in fields)
|
||||
mc = max(len(cat_to_string(c)) for c in cats)
|
||||
mt = max(len(typ_to_string(t)) for t in types)
|
||||
format_string = '- %%-%ds %%-%ds %%-%ds' % (mf+1, mc+1, mt)
|
||||
for i in xrange(NUM_FIELDS):
|
||||
f, c, t = fields[i], cat_to_string(cats[i]), typ_to_string(types[i])
|
||||
print format_string % (f+',', c+',', t)
|
||||
with open(argv[1]+'.hpp', 'w') as out:
|
||||
out.write(HEADER)
|
||||
out.write('#define AMS_ERPT_FOREACH_FIELD_TYPE(HANDLER) \\\n')
|
||||
for tp in sorted(list(set(types))):
|
||||
out.write((' HANDLER(%%-%ds %%-2d) \\\n' % (mt+1)) % (typ_to_string(tp)+',', tp))
|
||||
out.write('\n')
|
||||
out.write('#define AMS_ERPT_FOREACH_CATEGORY(HANDLER) \\\n')
|
||||
for ct in sorted(list(set(cats))):
|
||||
out.write((' HANDLER(%%-%ds %%-3d) \\\n' % (mc+1)) % (cat_to_string(ct)+',', ct))
|
||||
out.write('\n')
|
||||
out.write('#define AMS_ERPT_FOREACH_FIELD(HANDLER) \\\n')
|
||||
for i in xrange(NUM_FIELDS):
|
||||
f, c, t = fields[i], cats[i], types[i]
|
||||
out.write((' HANDLER(%%-%ds %%-4s %%-4s %%-2s) \\\n' % (mf+1)) % (f+',', '%d,'%i, '%d,'%c, '%d'%t))
|
||||
out.write('\n')
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
ret = main(len(sys.argv), sys.argv)
|
||||
except:
|
||||
ret = 1
|
||||
print 'exception'
|
||||
sys.exit(ret)
|
Loading…
Add table
Add a link
Reference in a new issue