diff --git a/.github/ISSUE_TEMPLATE/executable-import-issue.md b/.github/ISSUE_TEMPLATE/executable-import-issue.md new file mode 100644 index 0000000..5a13377 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/executable-import-issue.md @@ -0,0 +1,23 @@ +--- +name: Executable import issue +about: Report a problem encountered while importing a PlayStation®Vita executable. +title: "[Import] " +labels: bug +assignees: '' + +--- + +**Describe the bug** +Add any information you find may be relevant. + +**Exception call stack** +If an exception happens, include the call stack here. The call stack will be displayed in either the `Details>>` extended panel (import failure) or in the `Additional Information` of the `Import Results Summary`. Include all the text in those panels here. +``` +CALL STACK GOES HERE +``` + +**ELF source** +Please indicate the origin of the ELF file that causes this problem. If possible, include it along with the report. For system modules, indicate the exact firmware it was extracted from and in which path the module is located. + +**Additional information** +Creation date of the extension (this can be found in `File>Install Extensions`): 20XX-XX-XX \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d530ed7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +.gradle/* +.vscode/* +build/* + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +.settings +.antProperties.xml +.project +.classpath +bin/help +bin/ +dist/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8bb81e5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,32 @@ +The Clear BSD License + +Copyright (c) 2023 CreepNT +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted (subject to the limitations in the disclaimer +below) provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY +THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..521a9ff --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ + +# VitaLoader Redux +PlayStation®Vita ELF-PRX loader for Ghidra + +## Features +Redux can be used in place of the ELF loader provided by Ghidra to load exectuables in ELF-PRX format targetting the PlayStation®Vita platform. **This loader does NOT support standard ELF executables - only use it for ELFs in PRX format.** + +- Loads ELF files with SCE types (`ET_SCE_EXEC`, `ET_SCE_RELEXEC`, `ET_SCE_PSP2RELEXEC`) and standard types (`ET_REL`, `ET_EXEC`, `ET_CORE` ***in PRX format***) +- Locates and marks up all module entrypoints + - `module_start` + - `module_stop` + - `module_exit` + - `module_bootstart` + - `module_suspend` + - `module_proc_create` + - `module_proc_exit` + - `module_proc_kill` +- Locates, marks up and parses `NONAME` exports + - `SceModuleInfo` + - `SceProcessParam` and subfields + - `SceLibcParam` including Malloc Replacement + - Module thread parameters + - DTrace probes + - SDK version (displayed in `About Program` window) +- Locates and marks up all imports and exports + - Imports are separated based on the module from which they are imported + - Allows automatic renaming of symbols using NID databases + +### New features +#### NID Analyzer +Naming of imports and exports using a NID database is no longer performed at import time. Use the new `NID Resolution` analyzer instead. Analysis can be performed multiple times with different databases. + +The database used for analysis can be changed in the analyzer's settings in `Analysis > Auto Analyze ''`. The built-in databases are located in `%USERPROFILE%\.ghidra\\Extensions\VitaLoaderRedux\data\databases` and can be freely modified. +- `DefaultNIDDatabase.yaml` is used by default by the analyzer (`BuiltinDatabase`) +- `SecondaryNIDDatabase.yaml` can be selected by choosing `BuiltinSecondaryDatabase` +- An arbitrary database file can be used by choosing `ExternalDatabase` + +To apply NIDs from multiple databases successively, disable the `Clear old names` setting. + +#### Variable import relocation +Variable imports are now supported and handled properly ! This also applies to function-as-variable imports. A special memory block is created to "store" all imported variables. The relocations associated to them are applied + +The varimport memory block can be customized at import time by clicking on the `Options...` button in the Import dialog. + +Due to the way relocation is performed, certain code patterns will confuse the decompiler. For example, C code that should read as +```c +if (&sceWeaklyImportedFunction != NULL) { + sceWeaklyImportedFunction(); +} +``` +will transform info something similar to +```c +if (true) { + sceWeaklyImportedFunction(); +} +``` +i.e. the condition will always evaluate to 1. + +The assembly will now however hold a reference to the function thunk. This can be used to understand what the correct disassembly is. **Users should always be wary of `if (true)` and `if (false)` tests as they usually hide a subtlety the decompiler is unable to recover.** Note that the affected code patterns are seen only in a few modules (e.g. `SceDisplay`) - this should not be an issue for most reverse engineering tasks. + +#### Utility scripts +Can be found in the *Script Manager* under the `Vita` category. +- `MapRAMForNSKBL.py` + - Adds LPDDR2TOP in the memory map and merge it with NSKBL (`nskbl.bin`) + - Fixes missing references to `.bss` section and other stuff +- `AddHardwareDevices.py` + - Adds several hardware devices in the memory map + - Useful for reverse engineering of code running without MMU on (SKBL, NSKBL, CMeP binaries) + +#### MeP-c5 support +- Original idea from [ghidra-mep](https://github.com/xyzz/ghidra-mep) by xyz + - **Written from scratch** + - *`ghidra-mep` used as reference (along with Ghidra "samples") for tricky points* +- Implements most of the MeP-c4 instruction set + - Coprocessor-modulo instructions are not implemented + - MeP-c5 instructions are not implemented (except `PREF`) + - The `CACHE` instruction is implemented + - **This fixes `halt_baddata()` in some CMeP binaries!** +- IVC2 coprocessor (i.e. Venezia core) is not implemented + +## Installation +Download the [latest release](https://github.com/CreepNT/VitaLoaderRedux/releases/latest) for the Ghidra version you use. +Open Ghidra, select `File` > `Install Extensions...`, click on the green `+` and select the `.zip` you just downloaded. +Restart Ghidra, as asked by a dialog that should appear. + +## Updating +Open Ghidra, select `File` > `Install Extensions...` and untick the checkbox next to `VitaLoaderRedux`. +Close Ghidra and follow the [install instructions](#Installation) again. + +## Building +[Install Gradle](https://gradle.org/install/) then run `gradle` in a command prompt. Make sure to pass `-PGHIDRA_INSTALL_DIR=` if the environement variable `GHIDRA_INSTALL_DIR` isn't set. + +**Building the extension for a version of Ghidra earlier than 10.3 is not supported.** + +## Bug reports +Please report any error encountered with Redux in the [Issues Tracker](https://github.com/CreepNT/VitaLoaderRedux/issues). + +Before submitting any bug report, update to the latest version of the extension. Make sure you are importing an ELF file **in PRX format** - regular ELF files are **not** supported. + +If you are not able to load a file (`ARM ELF-PRX for PlayStation®Vita` is not displayed in the `Executable Type` list), ***please verify that your executable is not malformed***. + +### Known bugs +None. + +## Future plans +The following features might be implemented in Redux: +- Add missing structures (e.g. smaller `SceLibcParam`) +- Symbol parsing +- Object file support (`.o` files) +- Unwind tables parsing + - *if it might be useful for C++ binaries reversing, which I doubt* +- Full MeP-c5 implementation +- Venezia (MeP + IVC2) support + +## Credits +- **“PlayStation” is a registered trademark or trademark of Sony Interactive Entertainment Inc.** +- astrelsky and all contributors - [GhidraOrbis](https://github.com/astrelsky/GhidraOrbis) +- xerpi and all contributors - [GhidraVitaLoader script](https://github.com/xerpi/GhidraVitaLoader) +- xyz - [ghidra-mep](https://github.com/xyzz/ghidra-mep) +- EsotericSoftware - [YamlBeans](https://github.com/EsotericSoftware/yamlbeans) +Special thanks for pre-release testing and various input: + - CelesteBlue + - GrapheneCt + - Macdu + - M Ibrahim + - Princess-of-Sleeping + - rem + - sarcastic_cat + - everyone else I forgot (sorry 😅) + +## License +This repository is covered by the [Clear BSD License](/LICENSE), except the third-party libraries in the [lib/](/lib/) directory which are covered by the licenses listed in [lib/LICENSES](/lib/LICENSES). \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..fad8369 --- /dev/null +++ b/build.gradle @@ -0,0 +1,62 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Builds a Ghidra Extension for a given Ghidra installation. +// +// An absolute path to the Ghidra installation directory must be supplied either by setting the +// GHIDRA_INSTALL_DIR environment variable or Gradle project property: +// +// > export GHIDRA_INSTALL_DIR= +// > gradle +// +// or +// +// > gradle -PGHIDRA_INSTALL_DIR= +// +// Gradle should be invoked from the directory of the project to build. Please see the +// application.gradle.version property in /Ghidra/application.properties +// for the correction version of Gradle to use for the Ghidra installation you specify. + +//----------------------START "DO NOT MODIFY" SECTION------------------------------ +def ghidraInstallDir + +if (project.hasProperty("GHIDRA_INSTALL_DIR")) { + ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") +} else if (System.env.GHIDRA_INSTALL_DIR) { + ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR +} + +if (ghidraInstallDir) { + apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" +} +else { + throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") +} +//----------------------END "DO NOT MODIFY" SECTION------------------------------- + +repositories { + // Declare dependency repositories here. This is not needed if dependencies are manually + // dropped into the lib/ directory. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html for more info. + // Ex: mavenCentral() +} + +dependencies { + // Any external dependencies added here will automatically be copied to the lib/ directory when + // this extension is built. +} + +// Exclude additional files from the built extension +// Ex: buildExtension.exclude '.idea/**' diff --git a/data/ExtensionPoint.manifest b/data/ExtensionPoint.manifest new file mode 100644 index 0000000..6e80887 --- /dev/null +++ b/data/ExtensionPoint.manifest @@ -0,0 +1 @@ +ImportExportProperty \ No newline at end of file diff --git a/data/VitaFunctionsThatDoNotReturn.txt b/data/VitaFunctionsThatDoNotReturn.txt new file mode 100644 index 0000000..42fb9ff --- /dev/null +++ b/data/VitaFunctionsThatDoNotReturn.txt @@ -0,0 +1,24 @@ +# Vita functions which do not return +# Write the function's real name followed by the systematic name(s) +# (names should not start with '_' since these are stripped during checking) + +stack_chk_fail +SceSysclibForDriver_B997493D +SceLibKernel_37691BF8 +SceLibSsp_39AD080B + +sceKernelExitProcessForUser +SceProcessmgr_C053DC6B + +sceKernelStopped +SceDebugForKernel_F1F0C365 + +# panic_on_kernel_exception (used by Excpmgr) +SceDebugForKernel_082B8D6A +SceDebugForKernel_CCABDD98 + +sceKernelPanic +SceDebugForDriver_391B5B74 + +sceKernelPrintfPanic +SceDebugForDriver_00CCE39C \ No newline at end of file diff --git a/data/buildLanguage.xml b/data/buildLanguage.xml new file mode 100644 index 0000000..23e39b6 --- /dev/null +++ b/data/buildLanguage.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/databases/DefaultNIDDatabase.yaml b/data/databases/DefaultNIDDatabase.yaml new file mode 100644 index 0000000..df3dc38 --- /dev/null +++ b/data/databases/DefaultNIDDatabase.yaml @@ -0,0 +1,246 @@ +#Version 3 scheme: +# +# kernel: false becomes syscall: true +# modules->nid becomes modules->fingerprint +# +# +#This file aims to be sorted by NID, but may not be for time constraints. +# +version: 3 +firmware: 3.60 #even though we have NIDs for 0.9xx-only functions :D +modules: + ####################################################################### + # SECURE STATE (TZS) MODULES # + # # + # Note that the _TZS suffix is not present in module name, but is # + # required here to avoid a clash with the Non-Secure module # + ####################################################################### +# SceCdram: +# fingerprint: 0x1EE352F6 +# SceDriverTzs: +# fingerprint: 0xC4AF0386 + SceExcpmgr_TZS: + fingerprint: 0xDA718079 + libraries: + SceExcpmgrForTZS: + nid: 0x8F526F35 + functions: + sceKernel_GetExcpmgrContext: 0x07A5790B + sceKernelReleaseExceptionHandler: 0x166C9362 + sceKernelReleaseDefaultExceptionHandler: 0x6282E52C + sceKernelRegisterDefaultExceptionHandler: 0xA0434735 + sceKernelRegisterMonitorEntry: 0xAC297406 + sceKernelRegisterExceptionHandler: 0xDD4C680D +# SceKernelBusError_TZS: +# fingerprint: 0xAA8F8DA3 + SceKernelDmacMgr_TZS: + fingerprint: 0x1EE5B206 + libraries: + SceDmacmgrForTZS: + nid: 0xD180F06A + functions: + SceDmacmgrForTZS_184CF142: 0x184CF142 + SceKernelIntrMgr_TZS: + fingerprint: 0xD47DA083 + libraries: + SceIntrmgrForTZS: + nid: 0xEC3056FE + functions: + sceKernelGenerateSoftIntr: 0x28BBA975 + sceKernelDisableIntr: 0x4F39B381 + sceKernelIsIntrContext: 0x636F4549 + sceKernelRegisterIntrHandler: 0x6B84DA8F + sceKernelReleaseIntrHandler: 0x75A0F189 + sceKernelSetIntrPriority: 0x9168E78E + sceKernelResumeIntr: 0x92DE2E92 + sceKernelEnableIntr: 0x98E38390 + sceKernelSetIntrTargetCpu: 0xAA3C4787 + sceKernelSuspendIntr: 0xBFBEAB5C + sceKernelUsleep: 0xC0908EA9 + sceKernelRegisterMonitorCallHandler: 0xC188114F + sceKernelGetIntrTargetCpu: 0xF3B92D98 + sceKernelGetIntrPriority: 0xFEAC9841 + SceSblSmsched_TZS: + fingerprint: 0x5B25A8DA + libraries: + SceSblSmSchedForTZS: + nid: 0x5A2E5C09 + functions: + sceSblSmSchedGetStatus: 0x073A984E + #SceSblSmSchedForTZS_A4BF6A78 may be sceSblSmSchedReleaseIntrHandler + sceSblSmSchedWait: 0xC9D2138B + sceSblSmSchedKill: 0xDD5CDAE1 + sceSblSmSchedStop: 0xE72F2886 +# SceSysconTzs: +# fingerprint: 0x2F238BC5 + SceSysmem_TZS: + fingerprint: 0x8266B998 + libraries: + SceSysmemForTZS: + nid: 0x08680060 + functions: + sceKernelGetKernelPartition: 0xE074C866 + sceKernelAllocMemBlock: 0x0C510B46 + sceKernelSysrootGetKermitRevision: 0x1345678 + sceKernelDeleteHeap: 0x624454BC + sceKernelCreateHeap: 0x56A16D84 + sceKernelAllocHeapMemoryWithOption: 0xEA712806 + sceKernelAllocHeapMemory: 0x473871D8 + sceKernelFreeHeapMemory: 0x42FB5B12 + sceKernelAllocUncacheHeapMemoryWithOption: 0x6A3FBAF0 + sceKernelFreeUncacheHeapMemory: 0x2F18E288 + sceKernelNameHeapDelete: 0xF459D09D + sceKernelNameHeapInsert: 0x42AD34AB + sceKernelSetSuspendIntrFunc: 0xB4306D21 + sceKernelSetResumeIntrFunc: 0x418111B0 + sceKernelAddressSpaceUnmap: 0x31273DC7 + sceKernelGetMemBlockBase: 0xE1DEDFF4 + sceKernelFreeMemBlock: 0x658EACE3 + sceKernelAllocMemBlockForKernel: 0x402EB970 + sceKernelAllocPartitionMemBlock: 0x0028E26C + sceKernelAllocPartitionStackMemBlock: 0x69022B7F + sceKernelVAtoPA: 0x1DEADF6C + sceKernelVARangeToPAVector: 0x9D43E416 + sceKernelSetSuspendIntrFunc: 0xB4306D21 + sceKernelSetResumeIntrFunc: 0x418111B0 + SceDipswForTZS: + nid: 0x9DBF584C + functions: + sceKernelCheckDipsw: 0xA98FC2FD + SceUartForTZS: + nid: 0xA1D740D7 + functions: + sceUartWrite: 0x3AFD5E71 + SceDebugForTZS: + nid: 0x028FFADB + functions: + sceKernelDebugModuleStart2: 0x32E3D4B9 + sceKernelRegisterKprintfHandler: 0xD36F27BA + sceKernelDebugPutchar: 0xD36F27BA + sceKernelStopped: 0xCB502FD1 + sceKernelAssert: 0x2F9B8AA8 + sceKernelAssertLevel: 0x618F89E6 + sceKernelGetAssertLevel: 0xF851BA4F + sceKernelPrintDebugLog: 0x85BF33E1 + _sceKernelPrintDebugLog: 0x4FF6536C + sceKernelPrintf: 0x1655E0E3 + sceKernelPrintfLevel: 0xC70CBB58 + sceKernelPrintfCore0: 0x08F8AD70 + sceKernelPrintfLevelCore0: 0xD4E6A0C0 + sceKernelSetMinimumLogLevel: 0x898E417E + _sceKernelWriteDebugLog: 0x95E1A80B + SceSysclibForTZS: + nid: 0xC839BB78 + functions: + __stack_chk_fail: 0xE5457B97 + strncpy: 0x361850BB + memcpy: 0xBE3AF2EE + memset: 0x956DB750 + SceSysrootForTZS: + nid: 0xFEFF641D + functions: + sceKernelSysrootBacktrace: 0xF26A5E68 + sceKernelSysrootPrintBacktrace: 0x463EA380 + sceKernelSysrootShowCallerInfo: 0x2F3D9414 + sceKernelSysrootGetModulePrivate: 0xC9DD8EB3 + sceKernelSysrootSetModulePrivate: 0xEA572BD9 + sceKernelSysrootGetSharedMemory: 0x1C96096F + sceKernelSysrootSetSharedMemory: 0x45EEB2E0 + sceKernelSysrootAlloc: 0x1A9E1D28 + sceKernelSysrootGetKblParam: 0x29C1049E + sceKernelSysrootGetErnieWakeupFactor: 0x8E871C6D + sceKernelSysrootGetKermitRevision: 0xDB3DAB76 + sceKernelSysrootGetVbaseMonitorVector: 0xD4E6D8E8 + sceKernelSysrootGetVbaseResetVector: 0x2460ADFA + sceKernelSysrootGetKernelMMUContext: 0xA8B7DAF9 + sceKernelSysrootCorelockLock: 0xD653194A + sceKernelSysrootCorelockUnlock: 0x2F5FD9A8 + sceKernelIsColdBoot: 0x0567B4B9 + sceKernelIsSecureState: 0xEFA54B46 + SceQafMgrForTZS: + nid: 0x637069DD + functions: + sceSblQafManagerIsAllowKernelDebug: 0x54D22D74 + ScePmMgrForTZS: + nid: 0x27F1AFD8 + functions: + sceSblPmMgrGetProductMode: 0xCF554E69 + SceSblAIMgrForTZS: + nid: 0x60D19047 + functions: + sceSblAIMgrIsCEX: 0x536B38F4 + SceCpuForTZS: + nid: 0xC516B23E + functions: + atomicCompareAndSet8: 0xEFD6F289 + sceKernelCpuId: 0x44C423D3 + sceKernelGetCpsr: 0x324727D1 + sceKernelGetSpsr: 0x72CA4F7A + sceKernelGetVmaccessRange: 0x98BF47D3 + sceKernelSetUndModeStack: 0xDF17E4A3 + sceKernelSetAbtModeStack: 0xF832C341 + sceKernelSetFIQModeStack: 0x49AD8B60 + sceKernelSetIRQModeStack: 0xD9013440 + sceKernelSetSvcModeStack: 0xFB1D3114 + sceKernelSetMonModeStack: 0xC2A428F3 + sceKernelPleFlushRequest: 0xC4137AED + sceKernelDomainTextMemcpy: 0x39FCFCC2 + sceKernelDcacheCleanRange: 0x190D96D5 + sceKernelDcacheInvalidateRange: 0xCDD46655 + sceKernelDcacheCleanInvalidateAll: 0x7CCE9480 + sceKernelL1DcacheCleanRange: 0x17A88E69 + sceKernelL1DcacheCleanInvalidateAll: 0x0A15B41C + sceKernelL1IcacheInvalidateRange: 0xB421FAFD + sceKernelL1IcacheInvalidateEntireAllCore: 0xA5965CBF + sceKernelSendEvent: 0xF42F079B + sceKernelWaitForEvent: 0x40DEC1B6 + sceKernelSpinlockLowLock: 0x71FD9AB5 + sceKernelSpinlockLowUnlock: 0xCD98416C + sceKernelSpinlockLowLockCpuSuspendIntr: 0xD67A4356 + sceKernelSpinlockLowUnlockCpuResumeIntr: 0xB8F00FBE + ##################################################################### + # NON-SECURE STATE MODULES # + ##################################################################### + NSKBL: + libraries: + SceKblForKernel: + nid: 0xD0FC2991 + functions: + sceKernelPrintf: 0x13A5ABEF + sceKernelSysrootProcessmgrStart2: 0x161D6FCC + sceKernelSysrootThreadMgrStartAfterProcess: 0x1DB28F02 + sceSDrfpStart: 0x230456F3 + sceSdStandaloneExit: 0x261F2747 + sceSDbgSdioStart: 0x29A8524D + sceKernelSysrootCorelockUnlock: 0x314AA770 + sceSblAimgrIsDEX: 0x5945F065 + sceKernelBootLoadModules: 0x6D7A1F18 + sceKernelPrintfLevel: 0x752E7EEC + sceAuthMgrExit: 0x79241ACF + sceKernelSysrootCorelockLock: 0x807B4437 + sceSblAimgrIsVITA: 0x838466E9 + sceSblAimgrIsCEX: 0x8A416887 + sceSblAimgrIsTest: 0x943E7537 + sceKernelBootBootModules: 0x9A92436E + sceKernelGetDebugPutchar: 0x9B868276 + sceSblAimgrIsDolce: 0xA7BD4417 + BootModules: 0xA7D60F71 + sceSDfMgrStart: 0xAA8005E4 + sceKernelCpuId: 0xB506A10E + sceSblAimgrIsTool: 0xB6C9ACF1 + sceSblAimgrIsGenuineDolce: 0xB6D00D6D + sceKernelGetDebugLevel: 0xC011935A + sceSblAimgrIsDiag: 0xC3DDDE15 + sceKernelSysrootIofilemgrStart: 0xC7B77991 + sceKernelCheckDipsw: 0xC8F4DE71 + sceSblQafManagerIsAllowKernelDebug: 0xCE94F329 + sceSdStandaloneInit: 0xF7AF8690 + LoadModules: 0xFAE33FDD + SceLibKernel: + libraries: + SceLibKernel: + nid: 0xCAE9ACE6 + functions: + __stack_chk_fail: 0x37691BF8 + variables: + __stack_chk_guard: 0x93B8AA67 \ No newline at end of file diff --git a/data/databases/LibraryToModuleDatabase.yaml b/data/databases/LibraryToModuleDatabase.yaml new file mode 100644 index 0000000..c0a4093 --- /dev/null +++ b/data/databases/LibraryToModuleDatabase.yaml @@ -0,0 +1,1107 @@ +######### +# NSKBL # +######### + +nskbl.bin: + - SceKblForKernel + +############## +# NS modules # +############## + +acmgr.skprx: + - SceSblACMgr + - SceSblACMgrForDriver + - SceSblACMgrForKernel + +applier.skprx: + - SceSblApplierForDriver + +appmgr.skprx: + - SceAppMgr + - SceAppMgrForDriver + - SceSharedFb + - SceSharedFbForDriver + +audio.skprx: + - SceAudio + - SceAudioForDriver + +audioin.skprx: + - SceAudioIn + - SceAudioInForDriver + +authmgr.skprx: + - SceSblAuthMgrForDriver + - SceSblAuthMgrForKernel + +avcodec.skprx: + - SceAvcodec + - SceAvcodecForDriver + +#In old firmwares: av_config.skprx +avconfig.skprx: + - SceAVConfig + - SceAVConfigForDriver + +bbmc.skprx: + - SceBbmc + - SceBbmcForDriver + +bootimage.skprx: + - SceKernelBootimage + +#BSOD exports only one function +#The rest is exported by deci4p_userp.skprx +#bsod.skprx: +# - SceDeci4pUserp + +bt.skprx: + - SceBt + - SceBtForDriver + +buserror.skprx: + - SceBusErrorForKernel + +camera.skprx: + - SceCamera + - SceCameraForDriver + +clockgen.skprx: + - SceClockgenForDriver + +#Or codec_cx.skprx +codec.skprx: + - SceCodec + - SceCodecForDriver + +compat.skprx: + - SceCompat + - SceCompatForDriver + - SceCompatForVsh + +coredump.skprx: + - SceCoredump + - SceCoredumpNounlink + - SceCoredumpForDriver + +ctrl.skprx: + - SceCtrl + - SceCtrlForDriver + +cui_updater.skprx: + - SceCuiUpdater + +dbgsdio.skprx: + - SceDbgSdioForDriver + +dbgusb.skprx: + - SceDbgUsbForDriver + +deci4p_cpup.skprx: + - SceDeci4pCpupForDriver + +deci4p_dbgp.skprx: + - SceDeci4pDbgp + - SceDeci4pDbgpForDriver + +deci4p_dfmgr.skprx: + - SceDeci4pDfMgrForDebugger + +deci4p_dtracep.skprx: + - SceDTrace + +deci4p_loadp.skprx: + - SceDeci4pLoadp + +deci4p_sdbgp.skprx: + - SceDeci4pSdbgpTest + - SceDeci4pSdbgpForKernel + +deci4p_deci2p.skprx: + - SceDeci2pForDriver + +deci4p_sdfmgr.skprx: + - SceDeci4pSDfMgrForKernel + +deci4p_userp.skprx: + - SceDeci4pUserp + +diagbridge.skprx: + - SceDiagBridge + +display.skprx: + - SceDisplay + - SceDisplayForDriver + +dmacmgr.skprx: + - SceDmacmgr + - SceDmacmgrForDriver + +error.skprx: + - SceError + - SceErrorForDriver + +excpmgr.skprx: + - SceExcpmgrForKernel + +fios2.skprx: + - SceFios2Kernel + - SceFios2KernelForDriver + - SceFios2Kernel02 + +first_image_writer.skprx: + - SceSblFirstImageWriter + - SceSblFirstImageWriterForDriver + +#Moved to post_ss_mgr.skprx +#fwloader.skprx: +# - SceSblFwLoaderForDriver + +gcauthmgr.skprx: + - SceSblGcAuthMgr + - SceSblGcAuthMgrSclkForDriver + - SceSblGcAuthMgrPkgForDriver + - SceSblGcAuthMgrDrmBBForDriver + - SceSblGcAuthMgrPcactForDriver + - SceSblGcAuthMgrGcAuthForDriver + - SceSblGcAuthMgrPsmactForDriver + - SceSblGcAuthMgrAdhocBBForDriver + - SceSblGcAuthMgrMlnpsnlForDriver + - SceSblGcAuthMgrMsSaveBBForDriver + +gps.skprx: + - SceGps + - SceGpsForDriver + +gpu_es1.skprx: + - SceGpuEs1ForDriver + - SceGpuEs1ForUser + +gpu_es2.skprx: + - SceGpuEs2ForDriver + - SceGpuEs2ForUser + +gpu_es3.skprx: + - SceGpuEs3ForDriver + - SceGpuEs3ForUser + +gpu_es4.skprx: + - SceGpuEs4ForDriver + - SceGpuEs4ForUser + +hdmi.skprx: + - SceHdmiForDriver + +hid.skprx: + - SceHid + - SceHidForDriver + +hpremote.skprx: + - SceHpremoteForDriver + +idstorage.skprx: + - SceIdStorage + - SceIdStorageForDriver + +intrmgr.skprx: + - SceIntrmgrForDriver + - SceIntrmgrForKernel + +iofilemgr.skprx: + - SceIofilemgr + - SceIofilemgrForDriver + +krm.skprx: + - SceKrm + +lcd.skprx: + - SceLcdForDriver + +led.skprx: + - SceLedForDriver + +lowio.skprx: + - SceCdramForDriver + - SceCsiForDriver + - SceDsiForDriver + - SceGrabForDriver + - SceGpioForDriver + - SceI2cForDriver + - SceIftuForDriver + - ScePervasiveForDriver + - ScePwmForDriver + +magicgate.skprx: + - SceMagicGateForDriver + +marlin_hci.skprx: + - SceMarlinHci + - SceMarlinHciForDriver + +mgkeymgr.skprx: + - SceSblMgKeyMgrForDriver + +mgvideo.skprx: + - SceMgVideo + - SceMgVideoForMiniApp + +modulemgr.skprx: + - SceModulemgr + - SceModulemgrForDriver + - SceModulemgrForKernel + - SceBacktrace + - SceBacktraceForDriver + +motion.skprx: + - SceMotionDev + - SceMotionDevForDriver + +msif.skprx: + - SceMsifForDriver + +mtpif.skprx: + - SceMtpIf + - SceMtpIfForDriver + +net_ps.skprx: + - SceNetPsForDriver + - SceNetPsForSyscalls + +ngs.skprx: + - SceNgsInternal + - SceNgsForDriver + +npdrm.skprx: + - SceNpDrm + - SceNpDrmForDriver + - SceNpDrmPackage + - ScePsmDrm + - ScePsmDrmForDriver + +oled.skprx: + - SceOledForDriver + +pfsmgr.skprx: + - ScePfsMgrForKernel + - ScePfsFacadeForKernel + +post_ss_mgr.skprx: + - SceSblPostSsMgrForDriver + - SceZlibForDriver + - SceSblFwLoaderForDriver + - SceSblPmMgr + - SceSblPmMgr + - SceSblRtcMgr + - SceSblLicMgr + - SceSblUtMgr + - SceSblSpsfoMgr + +power.skprx: + - ScePower + - ScePowerForDriver + - SceLedForDriver + +processmgr.skprx: + - SceProcessmgr + - SceProcessmgrForDriver + - SceProcessmgrForKernel + +regmgr.skprx: + - SceRegMgr + - SceRegMgrForDebugger + - SceRegMgrForDriver + - SceRegMgrForGame + - SceRegMgrForSDK + - SceRegMgrForTool + - SceRegMgrService + - SceRegMgrServiceForDriver + +rtc.skprx: + - SceRtc + - SceRtcForDriver + +samantha.skprx: + - SceSamanthaForDriver + +sdbgsdio.skprx: + - SceSDbgSdioForKernel + +sdif.skprx: + - SceSdifForDriver + +sm_comm.skprx: + - SceSblSsSmComm + - SceSblSsSmCommForKernel + +smsc_proxy.skprx: + - SceSblSmSchedProxyForKernel + +ss_mgr.skprx: + - SceSblSsMgr + - SceSblSsMgrForDriver + - SceSblSsMgrForKernel + - SceSblAimgr + - SceSblDmac5Mgr + - SceSblQafMgr + - SceSblRng + +ssproxy.skprx: + - SceSSProxyForKernel + +stdio.skprx: + - SceStdio + - SceStdioForDriver + +sublcd.skprx: + - SceSublcdForDriver + +syscon.skprx: + - SceSysconForDriver + +syslibtrace.skprx: + - SceNidsymtblForDriver + - SceSysLibTrace + - SceSyslibtraceForKernel + +sysmem.skprx: + - SceSysmem + - SceSysmemForKernel + - SceSysmemForDriver + - SceSysmemForDebugger + - SceDebugForDriver + - SceDebugForKernel + - SceDipsw + - SceDipswForDriver + - SceUartForKernel + - SceCpu + - SceCpuForKernel + - SceCpuForDriver + - SceSysclibForKernel + - SceSysclibForDriver + - SceSysrootForKernel + - SceSysrootForDriver + - SceKernelUtilsForDriver +#Moved to ss_mgr.skprx in 1.80. +# - SceZlibForDriver + - SceKernelSuspendForDriver + - SceQafMgrForDriver + - ScePmMgrForDriver + - SceSblAIMgrForDriver + - SceProcEventForDriver + - SceDebugLed + - SceDebugLedForDriver + - SceDebugForKernel + +sysmodule.skprx: + - SceSysmodule + +systimer.skprx: + - SceSystimerForDriver + +threadmgr.skprx: + - SceThreadmgr + - SceThreadmgrCoredumpTime + - SceThreadmgrForDriver + - SceThreadmgrForKernel + +touch.skprx: + - SceTouch + - SceTouchForDriver + +udcd.skprx: + - SceUdcd + - SceUdcdForDriver + +ulobjmgr.skprx: + - SceUlobjMgr + - SceUlobjMgrForDriver + +umass.skprx: + - SceUsbMassForDriver + +update_mgr.skprx: + - SceSblUpdateMgrForDriver + - SceSblUpdateMgrForKernel + - SceSblSsUpdateMgr + - SceSblSsUpdateMgrAdditional + +usbaudio.skprx: + - SceUsbAudioIn + - SceUsbAudioForDriver + +usbd.skprx: + - SceUsbdForDriver + - SceUsbdForUser + +usbmtp.skprx: + - SceUsbMtpForDriver + +usbserial.skprx: + - SceUsbSerial + - SceUsbSerialForDriver + +usbserv.skprx: + - SceUsbServ + - SceUsbServForDriver + +usbstor.skprx: + - SceUsbstorForDriver + - SceUsbstor + +usbstorvstor.skprx: + - SceUsbstorVStor + - SceUsbstorVStorForDriver + +usb_ether_rtl.skprx: + - SceUsbEtherRtlForDriver + +usb_ether_smsc.skprx: + - SceUsbEtherSmscForDriver + +vnz_wrapper.skprx: + - SceCodecEngineWrapper + - SceCodecEngineWrapperForDebugger + - SceCodecEngineWrapperForDriver + - SceVeneziaWrapperForDriver + +vshbridge.skprx: + - SceDrmBridge + - SceVshBridge + - SceVshBridgeAdditional + +wlanbt.skprx: + - SceWlan + - SceWlanBtForDriver + +wlanbt_robin.skprx: + - SceWlanBtRobinForDriver + + # -- User modules -- # + +##### +# os0:us/ +##### +avcodec_us.suprx: + - SceAudiodecUser + - SceAudioencUser + - SceJpegUser + - SceJpegEncUser + - SceVideodecUser + - SceVideoencUser + - SceVideodecAsyncUser + - SceVideodecRecoveryPointUser + - SceVideodecLowDelayUser + - SceCodecEngineUser + +driver_us.suprx: + - SceRtcUser + - SceDisplayUser + - SceMotion + - SceAppMgrUser + - SceErrorUser + - SceFios2User + - SceDrmBridgeUser + +libgpu_es4.suprx: + - SceGpuEs4User + - SceGpuEs4UserForVsh + +libgxm_es4.suprx: + - SceGxm + - SceGxmInternal + - SceGxmInternalForGles + - SceGxmInternalForReplay + - SceGxmInternalForTest + - SceGxmInternalForVsh + +libkernel.suprx: + - SceLibKernel + - SceLibRng + - SceLibGcc + - SceLibSsp + - SceRtabi + - SceKernelForVM + - SceKernelForMono + +##### +# vs0:data/external/webcode/ +##### +jx_web_filtering.suprx: + - SceWebFiltering + - WebPlugin + +ScePsp2Compat.suprx: + - ScePsp2Compat + +SceWebKitModule.suprx: + - SceWebKit + +vita_jsextobj.suprx: + - SceLibVitaJSExtObj + +##### +# vs0:sys/external/ +##### +activity_db.suprx: + - SceActivityDb + +adhoc_matching.suprx: + - SceNetAdhocMatching + +apputil.suprx: + - SceAppUtil + - SceAppUtilAddcontForce + - SceAppUtilAppEventUserDefined + - SceAppUtilBook + - SceAppUtilCache + - SceAppUtilDevice + - SceAppUtilLaunchApp + - SceAppUtilNpSignin + - SceAppUtilPsm + - SceAppUtilUmass + - SceAppUtilWebBrowserCBLimited + +apputil_ext.suprx: + - SceAppUtilExt + - SceAppUtilExtMarlinIptv + - SceAppUtilExtPlayReady + - SceAppUtilExtPsNow + +audiocodec.suprx: + - SceAudiocodec + +avcdec_for_player.suprx: + - SceAvcdecForPlayer + +bgapputil.suprx: + - SceBgAppUtil + +bXCe.suprx: + - ScebXCe + +common_gui_dialog.suprx: + - SceCommonGuiDialog + +dbrecovery_utility.suprx: + - SceDbrecoveryUtility + +dbutil.suprx: + - SceDbutil + +incoming_dialog.suprx: + - SceIncomingDialog + +ini_file_processor.suprx: + - SceIniFileProcessor + +libatrac.suprx: + - SceAtrac + +libc.suprx: + - SceLibc + - SceLibm + - SceLibstdcxx + +libcdlg.suprx: + - SceCommonDialog + - SceNpWebApiCommonDialog + +libcdlg_main.suprx: + - SceCommonDialogMain + +libclipboard.suprx: + - SceClipboard + +libcodecengine_perf.suprx: + - SceCodecEnginePerf + +libdbg.suprx: + - SceDbg + +libhttp.suprx: + - SceHttp + +libfiber.suprx: + - SceFiber + - SceUlobjDbg + +libfios2.suprx: + - SceFios2 + +libg729.suprx: + - SceG729 + +libgameupdate.suprx: + - SceGameUpdate + +libhandwriting.suprx: + - SceHandwriting + +libhttp.suprx: + - SceHttp + +libime.suprx: + - SceIme + - SceImeVsh + +libipmi_nongame.suprx: + - SceIpmiNonGameApp + +liblocation.suprx: + - SceLibLocation + +liblocation_extension.suprx: + - SCeLibLocationExtension + +liblocation_factory.suprx: + - SceLibLocationFactory + +liblocation_internal.suprx: + - SceLibLocationInternal + +libmln.suprx: + - SceMarlin + +libmlnapplib.suprx: + - mlnapplib + +libmlndownloader.suprx: + - mlndl + +libnaac.suprx: + - SceAacenc + - SceAacencInternal + +libnet.suprx: + - SceNet + - SceNetInternal + +libnetctl.suprx: + - SceNetCtl + +libngs.suprx: + - SceNgs + +libpaf.suprx: + - ScePafAutoTestTty + - ScePafCommon + - ScePafGraphics + - ScePafLowlayer + - ScePafMisc + - ScePafResource + - ScePafStdc + - ScePafThread + - ScePafToplevel + - ScePafWidget + +libpaf_web_map_view.suprx: + - ScePafWebMapView + +libperf.suprx: + - ScePerf + - ScePerfInternal + +libpgf.suprx: + - ScePgf + +libpvf.suprx: + - ScePvf + +#There probably exists es1-es3 too +librazorcapture_es4.suprx: + - SceRazorCapture + +librazorhud_es4.suprx: + - SceRazorHud + - SceRazorHudInternalForReplay + +librudp.suprx: + - SceLibRudp + +libsas.suprx: + - SceSas + +libsceavplayer.suprx: + - SceAvPlayer + +libSceBeisobmf.suprx: + - SceBeisobmf + +libSceBemp2sys.suprx: + - SceBemp2sys + +libSceCompanionUtil.suprx: + - SceCompanionUtil + +libSceDtcpIp.suprx: + - SceDtcpIp + +libSceFt2.suprx: + - SceFt2 + +libscejpegarm.suprx: + - SceJpegArm + +libscejpegencarm.suprx: + - SceJpegEncArm + +libSceJson.suprx: + - SceJson + +libscemp4.suprx: + - SceMp4 + +libSceMp4Rec.suprx: + - SceLibMp4Recorder + +libSceMusicExport.suprx: + - SceMusicExport + +libSceNearDialogUtil.suprx: + - SceNearDialogUtil + +libSceNearUtil.suprx: + - SceNearUtil + +libScePhotoExport.suprx: + - ScePhotoExport + +libScePromoterUtil.suprx: + - ScePromoterUtil + +libSceScreenShot.suprx: + - SceScreenShot + +libSceShutterSound.suprx: + - SceShutterSound + +libSceSqlite.suprx: + - SceSqlite + +libSceTelephonyUtil.suprx: + - SceTelephonyUtil + +libSceTeleportClient.suprx: + - SceTeleportClient + +libSceTeleportServer.suprx: + - SceTeleportServer + +libSceVideoExport.suprx: + - SceVideoExport + - SceVideoExportEmpr + - SceVideoExportData + +libSceVideoSearchEmpr.suprx: + - SceVideoSearchEmpr + +libSceXml.suprx: + - SceLibXml + +libshellsvc.suprx: + - SceIpmi + - SceShellSvc + - SceShellUtil + - SceShellUtilLaunchApp + - SceShellUtilUketorne + - SceSvcCtrl + +libssl.suprx: + - SceSsl + - SceSslInternal + +libsulpha.suprx: + - SceSulpha + +libsystemgesture.suprx: + - SceSystemGesture + +libult.suprx: + - SceUlt + +libvoice.suprx: + - SceVoice + +libvoiceqos.suprx: + - SceVoiceQoS + +livearea_util.suprx: + - SceLiveAreaUtil + - SceLiveAreaUtilBgApp + +mail_api_for_local_libc.suprx: + - mail_api_for_local_libc + +near_profile.suprx: + - SceNearProfile + +notification_util.suprx: + - SceNotificationUtil + - SceNotificationUtilBgApp + - SceNotificationUtilProcess + +np_activity.suprx: + - SceNpActivityNet + +np_activity_sdk.suprx: + - SceNpActivity + +np_basic.suprx: + - SceNpBasic + +np_commerce2.suprx: + - SceNpCommerce2 + +np_common.suprx: + - SceNpCommon + +np_common_ps4.suprx: + - SceNpCommonPs4 + +np_friend_privacylevel.suprx: + - SceNpFriendPrivacyLevel + +np_kdc.suprx: + - SceNpKdc + +np_manager.suprx: + - SceNpManager + - SceNpManagerOAuth + - SceNpManagerSP + - SceNpManagerTicket + +np_matching2.suprx: + - SceNpMatching2 + +np_message.suprx: + - SceNpMessage + +np_message_contacts.suprx: + - SceNpMessageContacts + +np_message_dialog_impl.suprx: + - SceNpMessageDualogPlugin + +np_party.suprx: + - SceNpPartyGameUtil + +np_ranking.suprx: + - SceNpScore + +np_signaling.suprx: + - SceNpSignaling + +np_sns_facebook.suprx: + - SceNpSnsFacebook + +np_trophy.suprx: + - SceNpTrophy + +np_tus.suprx: + - SceNpTus + +np_utility.suprx: + - SceNpUtility + - SceNpUtilityAvatarN + +np_webapi.suprx: + - SceNpWebApi + +party_member_list.suprx: + - ScePartyMemberListPlugin + +psmkdc.suprx: + - SceDrmPsmKdc + +pspnet_adhoc.suprx: + - ScePspnetAdhoc + +sqlite.suprx: + - SceSqliteVsh + +trigger_util.suprx: + - SceTriggerUtil + +web_ui_plugin.suprx: + - SceWebUIPlugin + +##### +# vs0:vsh/common/ +##### +app_settings.suprx: + - SceAppSettings + +av_content_handler.suprx: + - SceHostAvch + +backup_restore.suprx: + - SceBackupRestore + +content_operation.suprx: + - SceContentOperation + +dbrecovery_plugin.suprx: + - SceDbRecovery + +dbsetup.suprx: + - SceDbSetup + +libBEAVCorePlayer.suprx: + - SceBEAVCorePlayer + +libFflMp4.suprx: + - SceLibFflMp4 + +libical.suprx: + - libical + +libicalss.suprx: + - libicalss + +libmarlin.suprx: + - SceMarlin + +libmarlin_pb.suprx: + - SceMarlinPb + +#Exported by vs0:sys/external/libmlndownloader.suprx +#libmarlindownloader.suprx: +# - mlndl + +libmtp.suprx: + - SceLibMtp + +libmtphttp.suprx: + - SceLibMtpHttp + +libmtphttp_wrapper.suprx: + - SceLibMtpHttpWrapper + +libSenvuabsFFsdk.suprx: + - SceLibFllVuMp4 + +libvideoprofiler.suprx: + - SceVideoProfiler + +mail_api_for_local.suprx: + - mail_api_for_local + +mtp_client.suprx: + - SceMtpClient + +mtpr3.suprx: + - SceMtpr3 + +np_grief_report.suprx: + - SceNpGriefReport + +##### +# vs0:vsh/common/mms/ +##### +AACPromoter.suprx: + - SceAACPromoter + +bmp_promoter.suprx: + - SceBmpPromoter + +gif_promoter.suprx: + - SceGifPromoter + +jpeg_promoter.suprx: + - SceJpegPromoter + +meta_gen.suprx: + - SceMetaGen + +Mp3Promoter.suprx: + - SceMp3Promoter + +MsvPromoter.suprx: + - SceMsvPromoter + +png_promoter.suprx: + - ScePngPromoter + +RiffPromoter.suprx: + - SceRiffPromoter + +SensMe.suprx: + - SceSenseMe + +tiff_promoter.suprx: + - SceTiffPromoter + +##### +# vs0:vsh/shell/ +##### +liblocation_dolce_provider.suprx: + - SceLibLocationDolceProvide + +liblocation_permission.suprx: + - SceLibLocationPermission + +liblocation_provider.suprx: + - SceLibLocationProvider + +livespace_db.suprx: + - SceLsdb + +shell.self: + - SceShell + +##### +# vs0:app/NPXS10001/ +##### +np_party_app.suprx: + - SceNpPartyAppUtil + +##### +# vs0:app/NPXS10013/ +##### +gaikai-player.suprx: #An identical version exists in NPXS10098 + - gaikai_player + +libSceSecondScreen.suprx: + - SceSecondScreen + +##### +# vs0:app/NPXS10015/ +##### +system_settings_core.suprx: + - SceSystemSettingsCore + + +##### +# vs0:app/NPXS10065/ +##### +grief_report_dialog.suprx: + - SceGriefReportDialog + +##### +# vs0:app/NPXS10072/ +##### +email_engine.suprx: + - SceEmailEngine + +############### +# TZS modules # +############### + +SceExcpmgr.elf: + - SceExcpmgrForTZS + +SceKernelIntrMgr.elf: + - SceIntrmgrForTZS + +SceSysmem.elf: + - SceSysmemForTZS + - SceDipswForTZS + - SceUartForTZS + - SceDebugForTZS + - SceCpuForTZS + - SceSysclibForTZS + - SceSysrootForTZS + - SceQafMgrForTZS + - ScePmMgrForTZS + - SceSblAIMgrForTZS \ No newline at end of file diff --git a/data/databases/SecondaryNIDDatabase.yaml b/data/databases/SecondaryNIDDatabase.yaml new file mode 100644 index 0000000..9f842fd --- /dev/null +++ b/data/databases/SecondaryNIDDatabase.yaml @@ -0,0 +1,48 @@ +version: 3 +firmware: 3.60 #even though we have NIDs for 0.9xx-only functions :D +modules: + ####################################################################### + # SECURE STATE (TZS) MODULES # + # # + # Note that the _TZS suffix is not present in module name, but is # + # required here to avoid a clash with the Non-Secure module # + ####################################################################### + + ##################################################################### + # NON-SECURE STATE MODULES # + ##################################################################### + NSKBL: + libraries: + SceKblForKernel: + nid: 0xD0FC2991 + functions: + sceKernelPrintf: 0x13A5ABEF + sceKernelSysrootProcessmgrStart2: 0x161D6FCC + sceKernelSysrootThreadMgrStartAfterProcess: 0x1DB28F02 + sceSDrfpStart: 0x230456F3 + sceSdStandaloneExit: 0x261F2747 + sceSDbgSdioStart: 0x29A8524D + sceKernelSysrootCorelockUnlock: 0x314AA770 + sceSblAimgrIsDEX: 0x5945F065 + sceKernelBootLoadModules: 0x6D7A1F18 + sceKernelPrintfLevel: 0x752E7EEC + sceAuthMgrExit: 0x79241ACF + sceKernelSysrootCorelockLock: 0x807B4437 + sceSblAimgrIsVITA: 0x838466E9 + sceSblAimgrIsCEX: 0x8A416887 + sceSblAimgrIsTest: 0x943E7537 + sceKernelBootBootModules: 0x9A92436E + sceKernelGetDebugPutchar: 0x9B868276 + sceSblAimgrIsDolce: 0xA7BD4417 + BootModules: 0xA7D60F71 + sceSDfMgrStart: 0xAA8005E4 + sceKernelCpuId: 0xB506A10E + sceSblAimgrIsTool: 0xB6C9ACF1 + sceSblAimgrIsGenuineDolce: 0xB6D00D6D + sceKernelGetDebugLevel: 0xC011935A + sceSblAimgrIsDiag: 0xC3DDDE15 + sceKernelSysrootIofilemgrStart: 0xC7B77991 + sceKernelCheckDipsw: 0xC8F4DE71 + sceSblQafManagerIsAllowKernelDebug: 0xCE94F329 + sceSdStandaloneInit: 0xF7AF8690 + LoadModules: 0xFAE33FDD diff --git a/data/languages/MeP-c5/CMeP.sla b/data/languages/MeP-c5/CMeP.sla new file mode 100644 index 0000000..52ba811 --- /dev/null +++ b/data/languages/MeP-c5/CMeP.sla @@ -0,0 +1,26625 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/languages/MeP-c5/CMeP.slaspec b/data/languages/MeP-c5/CMeP.slaspec new file mode 100644 index 0000000..2d8e839 --- /dev/null +++ b/data/languages/MeP-c5/CMeP.slaspec @@ -0,0 +1,19 @@ +@define ENDIAN "little" + +define endian=$(ENDIAN); +define alignment=2; + +@define DIV_OPT 1 +@define MUL_OPT 1 +@define BIT_OPT 1 +@define SAT_OPT 1 +@define CLP_OPT 1 +@define MIN_OPT 1 +@define AVE_OPT 1 +@define ABS_OPT 1 +@define LDZ_OPT 1 +@define DBG_OPT 1 + +@define TIMER_CHANNELS 2 + +@include "MeP-c5.sinc" diff --git a/data/languages/MeP-c5/MeP-c5.cspec b/data/languages/MeP-c5/MeP-c5.cspec new file mode 100644 index 0000000..50f49a1 --- /dev/null +++ b/data/languages/MeP-c5/MeP-c5.cspec @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/languages/MeP-c5/MeP-c5.ldefs b/data/languages/MeP-c5/MeP-c5.ldefs new file mode 100644 index 0000000..8712202 --- /dev/null +++ b/data/languages/MeP-c5/MeP-c5.ldefs @@ -0,0 +1,29 @@ + + + + + + + Toshiba MeP-c5 - CMeP implementation + + + + + Toshiba MeP-c5 - Venezia implementation (IVC2 coprocessor) + + + diff --git a/data/languages/MeP-c5/MeP-c5.opinion b/data/languages/MeP-c5/MeP-c5.opinion new file mode 100644 index 0000000..a456035 --- /dev/null +++ b/data/languages/MeP-c5/MeP-c5.opinion @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/data/languages/MeP-c5/MeP-c5.pspec b/data/languages/MeP-c5/MeP-c5.pspec new file mode 100644 index 0000000..8910281 --- /dev/null +++ b/data/languages/MeP-c5/MeP-c5.pspec @@ -0,0 +1,7 @@ + + + + + + + diff --git a/data/languages/MeP-c5/MeP-c5.sinc b/data/languages/MeP-c5/MeP-c5.sinc new file mode 100644 index 0000000..bdc6b2d --- /dev/null +++ b/data/languages/MeP-c5/MeP-c5.sinc @@ -0,0 +1,1781 @@ +# Main slaspec must define the following things: +# * endianess +# * alignment +# * the ENDIAN macro (either "little" or "big") +# * the TIMER_CHANNELS (between 1 and 4) +# +# Main slaspec may optionally define the following macros as 1 to enable MeP core options: +# +# VL32_OPT: The MeP core is extended with a 32-bit VLIW coprocessor <--| Only one of these +# VL64_OPT: The MeP core is extended with a 64-bit VLIW coprocessor <--| options may be enabled +# +# MUL_OPT: The 32-bit Multiply instruction option is ON. +# DIV_OPT: The 32-bit divide instruction option is ON. +# BIT_OPT: The bit manipulation instruction option is ON. +# LDZ_OPT: The leading zero option is ON. +# ABS_OPT: The absolute difference instruction option is ON. +# AVE_OPT: The average instruction option is ON. +# MIN_OPT: The MIN/MAX instruction option is ON. +# CLP_OPT: The clip instruction option is ON. +# SAT_OPT: The saturation instruction option is ON. +# DBG_OPT: The debug support function is ON. +# PTO_OPT: The program trace option is ON. #useless? +# HWE_OPT: The hardware engine option is ON. #useless? +# UCI_OPT: The User Custom Instruction option is ON. +# DSP_OPT: The DSP option is ON. +# COP_OPT: The coprocessor option is ON. +# +# +# If the VL32_OPT or VL64_OPT or COP_OPT macro is defined as 1, the following macros +# can be defined as 1 to alter the behaviour of the Core instruction set: +# +# LARGE_CP_REGNUM: The coprocessor has more than 16 general-purpose registers +# If defined as 1, thirty-two 64-bit CP GPRs will be created +# instead of 16 by default. +# +# NO_DEFAULT_CP_INSTR: Disables the built-in implementation of the CP instruction +# This macro must be defined as 1 if you implement the CP +# instruction in your slaspec. +# +# +# If the UCI_OPT macro is defined as 1, the following macro can be defined as 1: +# +# NO_DEFAULT_UCI_INSTR: Disables the built-in implementation of the UCI instruction. +# This macro must be defined as 1 if you implement the UCI +# instruction in your slaspec. +# +# +# If the DSP_OPT macro is defined as 1, the following macro can be defined as 1: +# +# NO_DEFAULT_DSP_INSTR: Disables the built-in implementation of the DSP instruction. +# This macro must be defined as 1 if you implement the +# DSP, DSP0 and DSP1 instructions in your slaspec. +# +# +#TODO: MeP-c5 instructions +# + +#Ensure all non-provided macros are defined as 0. +@include "Options.sinc" + +define space ram type=ram_space size=4 wordsize=1 default; +define space register type=register_space size=4; + +#Ideally, Control registers and Control Bus Space would be in their own register_space, +#but Ghidra does not support having more than one for whatever reason... (not even documented!?) +#define space creg type=register_space size=1 wordsize=4; #True pointer size is 5 bits +#define space cbus type=register_space size=2 wordsize=4; + +#General-purpose registers +define register offset=0x0 size=4 [ + r0 r1 r2 r3 r4 r5 r6 r7 + r8 r9 r10 r11 r12 tp gp sp +]; + +#Control registers + +@define CREG_OFFSET 0x10000 +@define CREG_MODULO_OFFSET 0x10030 +@define CREG_DEBUG_OFFSET 0x10060 + +define register offset=$(CREG_OFFSET) size=4 [ + pc lp sar _ rpb rpe rpc hi + lo _ _ _ _ _ _ _ + psw id tmp epc exc cfg _ npc + _ _ opt rcfg ccfg _ _ _ +]; + +@if COP_OPT == "1" +define register offset=$(CREG_MODULO_OFFSET) size=4 [ + mb0 me0 mb1 me1 +]; +@endif + +@if DBG_OPT == "1" +define register offset=$(CREG_DEBUG_OFFSET) size=4 [ + dbg depc +]; +@endif + +#BUG: Ghidra assumes contextreg to be 64-bit. +#Temporarily move these context bits to contextreg. + +#define context psw +# OM=(12,12) #Operation Mode (0 if MeP in Core mode, 1 if MeP in VLIW mode) +#; + +#define context rpe +# ELR=(0,0) #EndLess Repeat flag +#; + +#Co-processor registers +#Assume registers are 64-bit regardless of actual size (can be 32) + +@define CP_OFFSET 0x18000 +@define CP_CREG_OFFSET 0x18200 #For usage by coprocessor implementations + +define register offset=$(CP_OFFSET) size=8 [ + c0 c1 c2 c3 c4 c5 c6 c7 + c8 c9 c10 c11 c12 c13 c14 c15 +@ifdef LARGE_CP_REGNUM + c16 c17 c18 c19 c20 c21 c22 c23 + c24 c25 c26 c27 c28 c29 c30 c31 +@endif + cp_flag +]; + +# Context register +@define CONTEXTREG_OFFSET 0x19FF8 +define register offset=$(CONTEXTREG_OFFSET) size=8 contextreg; +define context contextreg + ELR=(0,0) noflow + OM=(12,12) + + CP_INST_MODE=(20,27) noflow + + #Number of instructions remaining, including the + #one marked, in REPEAT/EREPEAT block (either 2 or 1) + REPEAT_REMAINING=(28,29) noflow + + #Target of a REPEAT/EREPEAT jump + REPEAT_START=(32,63) noflow +; + +########################################### +# Control Bus registers # +########################################### + +# +# NOTE: all offsets and sizes have to +# be specified in bytes - hence, the +# offset for all registers has been set +# to their CBUS address multiplied by 4. +# + +@define CBUS_OFFSET 0x20000 +@define CBUS_TIMER_CH0 0x21000 +@define CBUS_TIMER_CH1 0x21040 +@define CBUS_TIMER_CH2 0x21080 +@define CBUS_TIMER_CH3 0x210C0 +@define CBUS_TIMER_CH4 0x21100 + +#INTC Interrupt Controller +define register offset=$(CBUS_OFFSET) size=4 [ + IVR ISR IER IET ILR0 ILR1 ILR2 ILR3 +]; + +#Timer/Counter region +define register offset=$(CBUS_TIMER_CH0) size=4 [ + CNT0 CMP0 TEN0 TCR0 TIS0 TCD0 +]; + +@if (TIMER_CHANNELS == "2") || (TIMER_CHANNELS == "3") || (TIMER_CHANNELS == "4") + define register offset=$(CBUS_TIMER_CH1) size=4 [ + CNT1 CMP1 TEN1 TCR1 TIS1 TCD1 + ]; +@endif + +@if (TIMER_CHANNELS == "3") || (TIMER_CHANNELS == "4") + define register offset=$(CBUS_TIMER_CH2) size=4 [ + CNT2 CMP2 TEN2 TCR2 TIS2 TCD2 + ]; +@endif + +@if (TIMER_CHANNELS == "4") + define register offset=$(CBUS_TIMER_CH1) size=4 [ + CNT3 CMP3 TEN3 TCR3 TIS3 TCD3 + ]; +@endif + + +#16-bit instruction, +#or first part of 32-bit instructions +define token instr (16) + major=(12,15) + sub=(0,3) + + Rn07=(8,10) + CRnExt=(8,11) #For c16-c31 + CRn=(8,11) + Rn=(8,11) + Rm=(4,7) + Rl=(0,3) + + cccc=(8,11) + + op0000=(0,0) + op0001=(0,1) + op0002=(0,2) + op0103=(1,3) + op0407=(4,7) + op0611=(6,11) + op0707=(7,7) + op0811=(8,11) + op1011=(10,11) + op1111=(11,11) + + imm0006=(0,6) + imm0007=(0,7) + imm0106=(1,6) + imm0107=(1,7) #bad?! + imm0206=(2,6) + imm0207=(2,7) #bad?! + imm0307=(3,7) + imm0405=(4,5) + imm0407=(4,7) + imm0410=(4,10) + imm0411=(4,11) + imm0809=(8,9) + imm0810=(8,10) + imm0811=(8,11) + + simm0107=(1,7) signed + simm0207=(2,7) signed + simm0111=(1,11) signed +; + +#Second part of 32-bit instructions +define token instr2(16) + wop0000=(0,0) + wop0001=(0,1) + wop0002=(0,2) + wop0003=(0,3) + wop0203=(2,3) + wop0303=(3,3) + wop0307=(3,7) + wop0311=(3,11) + wop0411=(4,11) + wop0811=(8,11) + wop0815=(8,15) + wop1215=(12,15) + + + wimm0307=(3,7) + + wimm16=(0,15) + + wsimm0007=(0,7) signed + wsimm0107=(1,7) signed + wsimm0207=(2,7) signed + wsimm0307=(3,7) signed + wsimm16=(0,15) signed + + wdisp16=(0,15) signed +; + +#Additional 32-bit word for 64-bit VLIW operation mode +define token vliw64_instr32(32) + v64_imm32=(0,31) +; + +attach variables + [ Rn Rm Rl ] + [ r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 tp gp sp ]; + +attach variables CRn + [ c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 ]; + +attach variables CRnExt + [ c16 c17 c18 c19 c20 c21 c22 c23 c24 c25 c26 c27 c28 c29 c30 c31 ]; + +attach variables Rn07 + [ r0 r1 r2 r3 r4 r5 r6 r7 ]; + +#--------------------------# +# Macros # +#--------------------------# + +macro SET_hi_lo(qword) { + hi = qword(4); + lo = qword:4; +} + +#Is this good enough? +#Should we use a TMode-like pseudoregister? +macro SET_OM(mode) { + mode = (mode & 1:4); + psw = (psw | (mode << 12)); +} + +macro perform_return(reg) { +@ifdef HAS_VLIW + if (reg:1 & 1:1) + goto ; +@endif + return [reg & ~1:4]; + +@ifdef HAS_VLIW + + SET_OM(1); +@if VL32_OPT == "1" + return [reg & ~3:4]; +@else #VL64_OPT + return [reg & ~7:4]; +@endif +@endif +} + +#--------------------------# +# Tables # +#--------------------------# + +BxxZTarget: target is simm0107 [ target = inst_start + (simm0107 << 1); ] { + export *:4 target; +} + +Bdisp12Target: target is simm0111 [ target = inst_start + (simm0111 << 1); ] { + export *:4 target; +} + +BSRTarget: target is major=13 & sub=9 & op1111=1 & imm0410; wdisp16 [ + target = inst_start + ((wdisp16 << 8) | (imm0410 << 1)); +] { + export *:4 target; +} + +BcondTarget: target is major=14; wdisp16 [ target = inst_start + (wdisp16 << 1); ] { + export *:4 target; +} + +@if COP_OPT == "1" +BCPcondTarget: target is major=13 & op0811=0b1000; wdisp16 [ target = inst_start + (wdisp16 << 1); ] { + export *:4 target; +} +@endif + +@if __HAS_VLIW == "1" && defined(__VLIW_ALIGN) + BSRVTarget: target is major=13 & sub=11 & op1111=1 & imm0410; wdisp16 [ + target = (inst_start + ( (wdisp16 << 8) | (imm0410 << 1) ) ) & ~ $(__VLIW_ALIGN); + OM = 1; + globalset(target, OM); + ] { + export *:4 target; + } +@endif + +RmMem: "("^Rm^")" is Rm { + export Rm; +} + +@if COP_OPT == "1" +RmMemInc: "("^Rm^"+)" is Rm { + export Rm; +} + +RmDisp16: wdisp16^"("^Rm^")" is Rm; wdisp16 { + local addr:4 = Rm + wdisp16; + export addr; +} +@endif + +SpDisp7: disp7^"("^sp^")" is imm0206 & sp [ disp7 = imm0206 << 2; ] { + local addr:4 = (sp + disp7) & ~3; + export *:4 addr; +} + +TpDisp7Word: disp7^"("^tp^")" is imm0206 & tp [ disp7 = imm0206 << 2; ] { + local addr:4 = (tp + disp7) & ~3; + export *:4 addr; +} + +TpDisp7Half: disp7^"("^tp^")" is imm0106 & tp [ disp7 = imm0106 << 1; ] { + local addr:4 = (tp + disp7) & ~1; + export *:2 addr; +} + +TpDisp7Byte: imm0006^"("^tp^")" is imm0006 & tp { + local addr:4 = tp + imm0006; + export *:1 addr; +} + +RmDisp16Word: wdisp16^"("^Rm^")" is Rm; wdisp16 { + local addr:4 = (Rm + wdisp16) & ~3; + export *:4 addr; +} + +RmDisp16Half: wdisp16^"("^Rm^")" is Rm; wdisp16 { + local addr:4 = (Rm + wdisp16) & ~1; + export *:2 addr; +} + +RmDisp16Byte: wdisp16^"("^Rm^")" is Rm; wdisp16 { + local addr:4 = Rm + wdisp16; + export *:1 addr; +} + +CBusReg: (wimm16) is wimm16 { + local dst:4 = $(CBUS_OFFSET) + 4 * wimm16; + export *[register]:4 dst; +} + +RepeatBlockStart: is REPEAT_START { + export *[ram]:4 REPEAT_START; +} + +#--------------------------# +# MeP Core Instruction Set # +#--------------------------# + +with :OM=0 { +#Generic instruction patterns (for REPEAT/EREPEAT) must come before any instruction + + #Penultimate instruction of REPEAT/EREPEAT block + #This instruction is the one REPEAT/EREPEAT opcode points to. + :^instruction is REPEAT_REMAINING=2 & instruction [ + #Markup next instruction for proper handling + REPEAT_REMAINING = 1; + globalset(inst_next, REPEAT_REMAINING); + globalset(inst_next, REPEAT_START); + globalset(inst_next, ELR); + + #Remove attribute for correct decoding during build + REPEAT_REMAINING = 0; + ] { + build instruction; + } + + #Ultimate instruction of REPEAT block. + #This instruction performs branching based on the value of rpc. + :^instruction is REPEAT_REMAINING=1 & ELR=0 & instruction & RepeatBlockStart [ + REPEAT_REMAINING = 0; + ] { + build instruction; + rpc = rpc - 1; + if (rpc == 0) + goto ; + + goto RepeatBlockStart; + + } + + #Ultimate instruction of EREPEAT block. Branches to repeat block start. + :^instruction is REPEAT_REMAINING=1 & ELR=1 & instruction & RepeatBlockStart [ + REPEAT_REMAINING = 0; + ] { + build instruction; + goto RepeatBlockStart; + } + + #-----------------# + # Major Opcode 0 # + #-----------------# + + with :major=0 { + :MOV Rn,Rm is sub=0 & Rn & Rm { #0000_nnnn_mmmm_0000 + Rn = Rm; + } + + # + # MeP-c4 Instruction Set Manual: + # + #The instruction "mov $0, $0" works as a NOP (no operation). + #Hazards are not detected in relation to operand $0. + #No write is performed to R0 ($0). + # + :NOP is sub=0 & Rn=0 & Rm=0 {} + + :NEG Rn,Rm is sub=1 & Rn & Rm { #0000_nnnn_mmmm_0001 + Rn = ~Rm; + } + + :SLT3 r0,Rn,Rm is sub=2 & r0 & Rn & Rm { #0000_nnnn_mmmm_0010 + r0 = zext(Rn s< Rm); + } + + :SLTU3 r0,Rn,Rm is sub=3 & r0 & Rn & Rm { #0000_nnnn_mmmm_0011 + r0 = zext(Rn < Rm); + } + + :SUB Rn,Rm is sub=4 & Rn & Rm { #0000_nnnn_mmmm_0100 + Rn = Rn - Rm; + } + + :SBVCK3 r0,Rn,Rm is sub=5 & Rn & Rm & r0 { #0000_nnnn_mmmm_0101 + #TODO: is this correct? + r0 = zext(sborrow(Rn, Rm)); + } + + :ADVCK3 r0,Rn,Rm is sub=7 & Rn & Rm & r0 { #0000_nnnn_mmmm_0111 + #TODO: is this correct? + r0 = zext(scarry(Rn, Rm)); + } + + :SB Rn,RmMem is sub=8 & Rn & RmMem { #0000_nnnn_mmmm_1000 + *:1 RmMem = Rn:1; + } + + :SH Rn,RmMem is sub=9 & Rn & RmMem { #0000_nnnn_mmmm_1001 + *:2 RmMem = Rn:2; + } + + :SW Rn,RmMem is sub=10 & Rn & RmMem { #0000_nnnn_mmmm_1010 + *:4 RmMem = Rn; + } + + :LBU Rn,RmMem is sub=11 & Rn & RmMem { #0000_nnnn_mmmm_1011 + Rn = *:1 RmMem; + } + + :LB Rn,RmMem is sub=12 & Rn & RmMem { #0000_nnnn_mmmm_1100 + Rn = sext(*:1 RmMem); + } + + :LH Rn,RmMem is sub=13 & Rn & RmMem { #0000_nnnn_mmmm_1101 + Rn = sext(*:2 RmMem); + } + + :LW Rn,RmMem is sub=14 & Rn & RmMem { #0000_nnnn_mmmm_1110 + Rn = *:4 RmMem; + } + + :LHU Rn,RmMem is sub=15 & Rn & RmMem { #0000_nnnn_mmmm_1111 + Rn = *:2 RmMem; + } + } + + #----------------# + # Major Opcode 1 # + #----------------# + + with :major=1 { + :OR Rn,Rm is sub=0 & Rn & Rm { #0001_nnnn_mmmm_0000 + Rn = (Rn | Rm); + } + + :AND Rn,Rm is sub=1 & Rn & Rm { #0001_nnnn_mmmm_0001 + Rn = (Rn & Rm); + } + + :XOR Rn,Rm is sub=2 & Rn & Rm { #0001_nnnn_mmmm_0010 + Rn = (Rn ^ Rm); + } + + :NOR Rn,Rm is sub=3 & Rn & Rm { #0001_nnnn_mmmm_0011 + Rn = ~(Rn | Rm); + } + +@if MUL_OPT == "1" + :MUL Rn,Rm is sub=4 & Rn & Rm { #0001_nnnn_mmmm_0100 + local prod:8 = sext(Rn:4) * sext(Rm:4); + SET_hi_lo(prod); + } + + :MULU Rn,Rm is sub=5 & Rn & Rm { #0001_nnnn_mmmm_0101 + local prod:8 = zext(Rn:4) * zext(Rm:4); + SET_hi_lo(prod); + } + + :MULR Rn,Rm is sub=6 & Rn & Rm { #0001_nnnn_mmmm_0110 + local prod:8 = sext(Rn:4) * sext(Rm:4); + SET_hi_lo(prod); + Rn = prod:4; + } + + :MULRU Rn,Rm is sub=7 & Rn & Rm { #0001_nnnn_mmmm_0111 + local prod:8 = zext(Rn:4) * zext(Rm:4); + SET_hi_lo(prod); + Rn = prod:4; + } +@endif + +@if DIV_OPT == "1" + :DIV Rn,Rm is sub=8 & Rn & Rm { #0001_nnnn_mmmm_1000 + local q:4 = Rn s/ Rm; + local r:4 = Rn s% Rm; + lo = q; + hi = r; + } + + :DIVU Rn,Rm is sub=9 & Rn & Rm { #0001_nnnn_mmmm_1001 + local q:4 = Rn / Rm; + local r:4 = Rn % Rm; + lo = q; + hi = r; + } +@endif + + :SSARB imm0809^"("^Rm^")" is sub=12 & op1011=0 & imm0809 & Rm { #0001_00dd_mmmm_1100 +@if ENDIAN == "little" + sar = zext(imm0809 + Rm[0,2] * 8); +@else + sar = 32:4 - zext(imm0809 + Rm[0,2] * 8); +@endif + } + + with :sub=13 { + :EXTB Rn is Rm=0b0000 & Rn { #0001_nnnn_0000_1101 + Rn = sext(Rn:1); + } + + :EXTH Rn is Rm=0b0010 & Rn { #0001_nnnn_0010_1101 + Rn = sext(Rn:2); + } + + :EXTUB Rn is Rm=0b1000 & Rn { #0001_nnnn_1000_1101 + Rn = zext(Rn:1); + } + + :EXTUH Rn is Rm=0b1010 & Rn { #0001_nnnn_1010_1101 + Rn = zext(Rn:2); + } + } + + #0001_0000_mmmm_1110 + :JMP Rm is sub=14 & Rn=0 & Rm +@if __HAS_VLIW == "1" + [ OM = (Rm & 1); globalset(Rm, OM); ] +@endif + { + goto [Rm]; + } + + #Special case: jumps to $1 and $11 are used to return from functions + :JMP Rm is sub=14 & Rn=0 & Rm & (Rm=1 | Rm=11) [ OM = (Rm & 1); ] { + return [Rm]; + } + + with :sub=15 { + :JSR Rm is Rn=0 & Rm { #0001_0000_mmmm_1111 + lp = inst_next; + call [Rm]; + } + +@if __HAS_VLIW == "1" + :JSRV Rm is Rn=0b1000 & Rm [ OM = 1; ] { #0001_1000_mmmm_1111 +@if VL32_OPT == "1" + local dst:4 = Rm & ~3:4; +@else #VL64_OPT + local dst:4 = Rm & ~7:4; +@endif + lp = (inst_next | 1); + call [dst]; + } +@endif + } + } + + #----------------# + # Major Opcode 2 # + #----------------# + + with :major=2 { +@if BIT_OPT == "1" + with :op1111=0 { + :BSETM RmMem,imm0810 is sub=0 & RmMem & imm0810 { #0010_0iii_mmmm_0000 + local bit:1 = (1 << imm0810); + *:1 RmMem = (*:1 RmMem | bit); + } + + :BCLRM RmMem,imm0810 is sub=1 & RmMem & imm0810 { #0010_0iii_mmmm_0001 + local mask:1 = ~(1 << imm0810); + *:1 RmMem = (*:1 RmMem & mask); + } + + :BNOTM RmMem,imm0810 is sub=2 & RmMem & imm0810 { #0010_0iii_mmmm_0010 + local bit:1 = (1 << imm0810); + *:1 RmMem = (*:1 RmMem ^ bit); + } + + :BTSTM r0,RmMem,imm0810 is sub=3 & r0 & RmMem & imm0810 { #0010_0iii_mmmm_0011 + local bit:1 = (1 << imm0810); + local mem:1 = *:1 RmMem; + r0 = zext(bit & mem); + } + } + + :TAS Rn,RmMem is sub=4 & Rn & RmMem { #0010_nnnn_mmmm_0100 + Rn = zext(*:1 RmMem); + *:1 RmMem = 1; + } +@endif + + :SL1AD3 r0,Rn,Rm is sub=6 & r0 & Rn & Rm { #0010_nnnn_mmmm_0110 + r0 = (Rn << 1) + Rm; + } + + :SL2AD3 r0,Rn,Rm is sub=7 & r0 & Rn & Rm { #0010_nnnn_mmmm_0111 + r0 = (Rn << 2) + Rm; + } + + :SRL Rn,Rm is sub=12 & Rn & Rm { #0010_nnnn_mmmm_1100 + Rn = (Rn >> Rm[0,5]); + } + + :SRA Rn,Rm is sub=13 & Rn & Rm { #0010_nnnn_mmmm_1101 + Rn = (Rn s>> Rm[0,5]); + } + + :SLL Rn,Rm is sub=14 & Rn & Rm { #0010_nnnn_mmmm_1110 + Rn = (Rn << (Rm & 0x1F)); + } + + :FSFT Rn,Rm is sub=15 & Rn & Rm { #0010_nnnn_mmmm_1111 + local res:8 = (zext(Rn) << 32) | zext(Rm); + res = (res << sar[0,6]); + Rn = res[32,32]; + } + } + + #----------------# + # Major Opcode 3 # + #----------------# + +@if COP_OPT == "1" + with :major=3 { + :SWCPI CRn,RmMemInc is sub=0 & CRn & RmMemInc { #0011_nnnn_mmmm_0000 + local dst:4 = RmMemInc & ~3; + *:4 dst = CRn:4; + RmMemInc = RmMemInc + 4; + } + + :LWCPI CRn,RmMemInc is sub=1 & CRn & RmMemInc { #0011_nnnn_mmmm_0001 + #IMPLEMENTATION NOTE + #On a coprocessor with registers wider than 32-bits, + #the result of the LWCP operation is implementation defined. + # + #This implementation sets the bottom 32-bits of the target register from + #memory while keeping the top 32-bits of the target register untouched. + # + local src:4 = RmMemInc & ~3; + CRn[0,32] = *:4 src; + RmMemInc = RmMemInc + 4; + } + + :SMCPI CRn,RmMemInc is sub=2 & CRn & RmMemInc { #0011_nnnn_mmmm_0010 + local dst:4 = RmMemInc & ~7; + *:8 dst = CRn:8; + RmMemInc = RmMemInc + 8; + } + + :LMCPI CRn,RmMemInc is sub=3 & CRn & RmMemInc { #0011_nnnn_mmmm_0011 + local src:4 = RmMemInc & ~7; + CRn = *:8 src; + RmMemInc = RmMemInc + 8; + } + + :SWCP CRn,RmMem is sub=8 & CRn & RmMem { #0011_nnnn_mmmm_1000 + local dst:4 = RmMem & ~3; + *:4 dst = CRn:4; + } + + :LWCP CRn,RmMem is sub=9 & CRn & RmMem { #0011_nnnn_mmmm_1001 + #IMPLEMENTATION NOTE: SEE LWCPI + local src:4 = RmMem & ~3; + CRn[0,32] = *:4 src; + } + + :SMCP CRn,RmMem is sub=10 & CRn & RmMem { #0011_nnnn_mmmm_1010 + local dst:4 = RmMem & ~7; + *:8 dst = CRn:8; + } + + :LMCP CRn,RmMem is sub=11 & CRn & RmMem { #0011_nnnn_mmmm_1011 + local src:4 = RmMem & ~7; + CRn = *:8 src; + } + } +@endif + + #----------------# + # Major Opcode 4 # + #----------------# + + with :major=4 { + with :op0707=0 { + #0100_nnnn_0iii_ii00 (imm7=iii_ii||00) + :ADD3 Rn,sp,imm7 is op0001=0b00 & Rn & imm0207 & sp [ imm7 = imm0207 << 2; ] { + Rn = sp + imm7; + } + + #0100_nnnn_0ddd_dd10 (disp7=ddd_dd||00) + :SW Rn,SpDisp7 is op0001=0b10 & Rn & SpDisp7 { + SpDisp7 = Rn; + } + + #0100_nnnn_0ddd_dd11 + :LW Rn,SpDisp7 is op0001=0b11 & Rn & SpDisp7 { + Rn = SpDisp7; + } + + } + + with :op0707=1 { + with :op1111=0 { + #0100_0nnn_1ddd_dd10 + :SW Rn07,TpDisp7Word is op0001=0b10 & Rn07 & TpDisp7Word { + TpDisp7Word = Rn07; + } + + #0100_0nnn_1ddd_dd11 + :LW Rn07,TpDisp7Word is op0001=0b11 & Rn07 & TpDisp7Word { + Rn07 = TpDisp7Word; + } + } + + #0100_1nnn_1ddd_dddd (disp7=ddd_dddd) + :LBU Rn07,TpDisp7Byte is op1111=1 & Rn07 & TpDisp7Byte { + Rn07 = zext(TpDisp7Byte); + } + } + } + + #----------------# + # Major Opcode 5 # + #----------------# + + :MOV Rn,imm0007 is major=5 & Rn & imm0007 { + Rn = imm0007:4; + } + + #----------------# + # Major Opcode 6 # + #----------------# + + with :major=6 { + #0110_nnnn_iiii_ii00 + :ADD Rn,simm0207 is op0001=0b00 & Rn & simm0207 { + Rn = Rn + simm0207; + } + + #0110_nnnn_iiii_i001 (imm5=iiii_i) + :SLT3 r0,Rn,imm0307 is op0002=0b001 & r0 & Rn & imm0307 { + r0 = zext(Rn s< imm0307); + } + + #0110_nnnn_iiii_i010 + :SRL Rn,imm0307 is op0002=0b010 & Rn & imm0307 { + Rn = (Rn >> imm0307); + } + + #0110_nnnn_iiii_i011 + :SRA Rn,imm0307 is op0002=0b011 & Rn & imm0307 { + Rn = (Rn s>> imm0307); + } + + #0110_nnnn_iiii_i101 + :SLTU3 r0,Rn,imm0307 is op0002=0b101 & r0 & Rn & imm0307 { + r0 = zext(Rn < zext(imm0307:1)); + } + + #0110_nnnn_iiii_i110 + :SLL Rn,imm0307 is op0002=0b110 & Rn & imm0307 { + Rn = (Rn << imm0307); + } + + #0110_nnnn_iiii_i111 + :SLL3 r0,Rn,imm0307 is op0002=0b111 & r0 & Rn & imm0307 { + r0 = (Rn << imm0307); + } + } + + #----------------# + # Major Opcode 7 # + #----------------# + + with :major=7 { + with :sub=0b0000 & Rn=0 { + define pcodeop DisableInterrupt; + :DI is Rm=0 { #0111_0000_0000_0000 + DisableInterrupt(); + } + + define pcodeop EnableInterrupt; + :EI is Rm=1 { #0111_0000_0001_0000 + EnableInterrupt(); + } + } + + with :sub=0b0001 & Rn=0 { + define pcodeop SynchronizeWithMemory; + :SYNCM is Rm=1 { #0111_0000_0001_0001 + SynchronizeWithMemory(); + } + +@if COP_OPT == "1" + define pcodeop SynchronizeWithCoprocessor; + :SYNCCP is Rm=2 { #0111_0000_0010_0001 + SynchronizeWithCoprocessor(); + } +@endif + } + + with :sub=0b0010 & Rn=0 { + :RET is Rm=0b0000 [ OM = (lp & 1); globalset(lp, OM); ] { #0111_0000_0000_0010 + perform_return(lp); + } + + :RETI is Rm=0b0001 [ OM = (epc & 1); globalset(epc, OM); ] { #0111_0000_0001_0010 + #TODO: NMI handling etc + perform_return(epc); + } + + define pcodeop Halt; + :HALT is Rm=0b0010 { #0111_0000_0010_0010 + Halt(); + } + + define pcodeop BreakException; + :BREAK is Rm=0b0011 { #0111_0000_0011_0010 + BreakException(); + } + + define pcodeop Sleep; + :SLEEP is Rm=0b0110 { #0111_0000_0110_0010 + Sleep(); + } + } + +@if DBG_OPT == "1" + with :sub=0b0011 & Rn=0 { + :DRET is Rm=0b0001 { #0111_0000_0001_0011 + perform_return(depc); + } + + define pcodeop DebugBreak; + :DBREAK is Rm=0b0011 { #0111_0000_0011_0011 + DebugBreak(); + } + } +@endif + + #TODO: when is CACHE enabled? + #Only if the core has a CACHE option? Do we care? + with :sub=0b0100 { + #0111_iiii_mmmm_0100 + + #No clue why, but putting imm0811 in the pattern list results + #in a seemingly nonsensical "Constraining currently undefined operand" error + + define pcodeop DataCacheHitWriteback; + :CACHE "WB",RmMem is imm0811=0 & RmMem { + DataCacheHitWriteback(RmMem); + } + + define pcodeop DataCacheIndexWriteback; + :CACHE "IDX_WB",RmMem is imm0811=1 & RmMem unimpl + + define pcodeop DataCacheHitWritebackInvalidate; + :CACHE "WB_INV",RmMem is imm0811=2 & RmMem { + DataCacheHitWritebackInvalidate(RmMem); + } + + define pcodeop DataCacheIndexWritebackInvalidate; + :CACHE "IDX_WB_INV",RmMem is imm0811=3 & RmMem unimpl + + define pcodeop DataCacheOperation; + :CACHE imm0811,RmMem is imm0811 & RmMem { + DataCacheOperation(imm0811:1, RmMem); + } + } + + with :sub=0b0101 { + define pcodeop CachePrefetch; + :PREF imm0811,RmMem is imm0811 & RmMem { + CachePrefetch(imm0811:1, RmMem); + } + } + + define pcodeop SystemCall; + :SWI imm0405 is sub=0b0110 & op0611=0 & imm0405 { #0111_0000_00ii_0110 + r0 = SystemCall(r4); + } + +#Man-made horrors beyond human comprehension. +@include "STCLDC.sinc" + } + + #----------------# + # Major Opcode 8 # + #----------------# + + with :major=8 { + #1000_0nnn_0ddd_dddd (disp7 = ddd_dddd) + :SB Rn07,TpDisp7Byte is op0707=0 & op1111=0 & Rn07 & TpDisp7Byte { + TpDisp7Byte = Rn07:1; + } + + #1000_0nnn_1ddd_ddd0 (disp7 = ddd_ddd||0) + :SH Rn07,TpDisp7Half is op0000=0 & op0707=1 & op1111=0 & Rn07 & TpDisp7Half { + TpDisp7Half = Rn07:2; + } + + #1000_1nnn_0ddd_dddd + :LB Rn07,TpDisp7Byte is op0707=0 & op1111=1 & Rn07 & TpDisp7Byte { + Rn07 = sext(TpDisp7Byte); + } + + #1000_1nnn_1ddd_ddd0 + :LH Rn07,TpDisp7Half is op0000=0 & op0707=1 & op1111=1 & Rn07 & TpDisp7Half { + Rn07 = sext(TpDisp7Half); + } + + #1000_1nnn_1ddd_ddd1 + :LHU Rn07,TpDisp7Half is op0000=1 & op0707=1 & op1111=1 & Rn07 & TpDisp7Half { + Rn07 = zext(TpDisp7Half); + } + } + + #----------------# + # Major Opcode 9 # + #----------------# + + :ADD3 Rl,Rn,Rm is major=9 & Rl & Rn & Rm { + Rl = Rn + Rm; + } + + #-----------------# + # Major Opcode 10 # + #-----------------# + + with :major=10 { + #1010_nnnn_dddd_ddd0 (disp8=dddd_ddd||0) + :BEQZ Rn,BxxZTarget is op0000=0 & Rn & BxxZTarget { + if (Rn == 0) + goto BxxZTarget; + } + + #1010_nnnn_dddd_ddd1 + :BNEZ Rn,BxxZTarget is op0000=1 & Rn & BxxZTarget { + if (Rn != 0) + goto BxxZTarget; + } + } + + #-----------------# + # Major Opcode 11 # + #-----------------# + + with :major=11 { + #1011_dddd_dddd_ddd0 (disp12=dddd_dddd_ddd||0) + :BRA Bdisp12Target is op0000=0 & Bdisp12Target { + goto Bdisp12Target; + } + + + #1011_dddd_dddd_ddd1 + :BSR Bdisp12Target is op0000=1 & Bdisp12Target { + lp = inst_next; + call Bdisp12Target; + } + } + + #--------------------------# + # Major Opcode 12 (32 bit) # + #--------------------------# + + #N.B. we cannot use a global major=12 for these instructions + #because the "major=12" constraint needs to be grouped with + #the rest of first-halfword constraints else ; doesn't work + #(maybe there's a way to do it, cba to search for it) + + #1100_nnnn_mmmm_0000 iiii_iiii_iiii_iiii (imm16=iiii_iiii_iiii_iiii) + :ADD3 Rn,Rm,wimm16 is (major=12 & sub=0 & Rn & Rm); wimm16 { + Rn = Rm:4 + sext(wimm16:2); + } + + #1100_nnnn_0000_0001 iiii_iiii_iiii_iiii + :MOV Rn,wsimm16 is (major=12 & sub=1 & Rm=0 & Rn); wsimm16 { + Rn = sext(wsimm16:2); + } + + #1100_nnnn_0001_0001 iiii_iiii_iiii_iiii + :MOVU Rn,wimm16 is major=12 & sub=1 & Rm=1 & Rn; wimm16 { + Rn = wimm16; + } + + #1100_nnnn_0010_0001 iiii_iiii_iiii_iiii + :MOVH Rn,wimm16 is (major=12 & sub=1 & Rm=2 & Rn); wimm16 { + Rn = wimm16:4 << 16; + } + + #1100_nnnn_mmmm_0010 iiii_iiii_iiii_iiii + :SLT3 Rn,Rm,wsimm16 is (major=12 & sub=2 & Rn & Rm); wsimm16 { + r0 = zext(Rm s< sext(wsimm16:2)); + } + + #1100_nnnn_mmmm_0011 iiii_iiii_iiii_iiii + :SLTU3 Rn,Rm,wimm16 is (major=12 & sub=3 & Rn & Rm); wimm16 { + r0 = zext(Rm < zext(wimm16:2)); + } + + #1100_nnnn_mmmm_0100 iiii_iiii_iiii_iiii + :OR3 Rn,Rm,wimm16 is (major=12 & sub=4 & Rn & Rm); wimm16 { + Rn = Rm | wimm16; + } + + #1100_nnnn_mmmm_0101 iiii_iiii_iiii_iiii + :AND3 Rn,Rm,wimm16 is (major=12 & sub=5 & Rn & Rm); wimm16 { + Rn = Rm & wimm16; + } + + #1100_nnnn_mmmm_0110 iiii_iiii_iiii_iiii + :XOR3 Rn,Rm,wimm16 is (major=12 & sub=6 & Rn & Rm); wimm16 { + Rn = Rm ^ wimm16; + } + + #1100_nnnn_mmmm_1000 dddd_dddd_dddd_dddd (disp16=dddd_dddd_dddd_dddd) + :SB Rn,RmDisp16Byte is (major=12 & sub=8 & Rn) ... & RmDisp16Byte { + RmDisp16Byte = Rn:1; + } + + #1100_nnnn_mmmm_1001 dddd_dddd_dddd_dddd + :SH Rn,RmDisp16Half is (major=12 & sub=9 & Rn) ... & RmDisp16Half { + RmDisp16Half = Rn:2; + } + + #1100_nnnn_mmmm_1010 dddd_dddd_dddd_dddd + :SW Rn,RmDisp16Word is (major=12 & sub=10 & Rn) ... & RmDisp16Word { + RmDisp16Word = Rn; + } + + #1100_nnnn_mmmm_1011 dddd_dddd_dddd_dddd + :LBU Rn,RmDisp16Byte is (major=12 & sub=11 & Rn) ... & RmDisp16Byte { + Rn = zext(RmDisp16Byte); + } + + #1100_nnnn_mmmm_1100 dddd_dddd_dddd_dddd + :LB Rn,RmDisp16Byte is (major=12 & sub=12 & Rn) ... & RmDisp16Byte { + Rn = sext(RmDisp16Byte); + } + + #1100_nnnn_mmmm_1101 dddd_dddd_dddd_dddd + :LH Rn,RmDisp16Half is (major=12 & sub=13 & Rn) ... & RmDisp16Half { + Rn = sext(RmDisp16Half); + } + + #1100_nnnn_mmmm_1110 dddd_dddd_dddd_dddd + :LW Rn,RmDisp16Word is (major=12 & sub=14 & Rn) ... & RmDisp16Word { + Rn = RmDisp16Word; + } + + #1100_nnnn_mmmm_1111 dddd_dddd_dddd_dddd + :LHU Rn,RmDisp16Half is (major=12 & sub=15 & Rn) ... & RmDisp16Half { + Rn = zext(RmDisp16Half); + } + + #--------------------------# + # Major Opcode 13 (32-bit) # + #--------------------------# + + #1101_0nnn_IIII_IIII iiii_iiii_iiii_iiii (imm24=iiii_iiii_iiii_iiii||IIII_IIII) + :MOVU Rn07,imm24 is major=13 & op1111=0 & Rn07 & imm0007; wimm16 [ imm24 = imm0007 | (wimm16 << 8); ] { + Rn07 = imm24; + } + +@if COP_OPT == "1" + #TODO: is this correct? + + #1101_1000_cccc_0100 dddd_dddd_dddd_dddd (disp17=dddd_dddd_dddd_dddd||0) + :BCPEQ cccc,BCPcondTarget is (sub=4 & cccc) ... & BCPcondTarget { + if ((cccc ^ cp_flag[0,4]) == 0) + goto BCPcondTarget; + } + + #1101_1000_cccc_0101 dddd_dddd_dddd_dddd + :BCPNE cccc,BCPcondTarget is (sub=5 & cccc) ... & BCPcondTarget { + if ((cccc ^ cp_flag[0,4]) != 0) + goto BCPcondTarget; + } + + #1101_1000_cccc_0110 dddd_dddd_dddd_dddd + :BCPAT cccc,BCPcondTarget is (sub=6 & cccc) ... & BCPcondTarget { + if ((cccc & cp_flag[0,4]) != 0) + goto BCPcondTarget; + } + + #1101_1000_cccc_0111 dddd_dddd_dddd_dddd + :BCPAF cccc,BCPcondTarget is (sub=7 & cccc) ... & BCPcondTarget { + if ((cccc & cp_flag[0,4]) == 0) + goto BCPcondTarget; + } +@endif + + JmpTarget24: target is major=13 & op1111=1 & sub=8 & imm0410; wimm16 [ + target = (inst_start & 0xF0000000) | (wimm16 << 8) | (imm0410 << 1); + ] { + export *:4 target; + } + + #1101_1TTT_TTTT_1000 tttt_tttt_tttt_tttt (target24=tttt_tttt_tttt_tttt||TTT_TTTT||0) + :JMP JmpTarget24 is JmpTarget24 { + goto JmpTarget24; + } + + + #1101_1DDD_DDDD_1001 dddd_dddd_dddd_dddd (disp24=dddd_dddd_dddd_dddd||DDD_DDDD||0) + :BSR BSRTarget is BSRTarget { + lp = inst_next; + + call BSRTarget; + } + +@if __HAS_VLIW == "1" + #BSRV disp24 1101_1DDD_DDDD_1011 dddd_dddd_dddd_dddd (disp24=dddd_dddd_dddd_dddd||DDD_DDDD||0) + :BSRV BSRVTarget is BSRVTarget { + lp = (inst_next | 1); + call BSRVTarget; + } +@endif + + #--------------------------# + # Major Opcode 14 (32-bit) # + #--------------------------# + + #1110_nnnn_iiii_0000 dddd_dddd_dddd_dddd + :BEQI Rn,imm0407,BcondTarget is (sub=0b0000 & Rn & imm0407) ... & BcondTarget { + if (Rn == zext(imm0407:1)) + goto BcondTarget; + } + + #:BEQ Rn,Rm,disp17 1110_nnnn_mmmm_0001 dddd_dddd_dddd_dddd + :BEQ Rn,Rm,BcondTarget is (sub=0b0001 & Rn & Rm) ... & BcondTarget { + if (Rn == Rm) + goto BcondTarget; + } + + #:BNEI Rn,imm4,disp17 1110_nnnn_iiii_0100 dddd_dddd_dddd_dddd + :BNEI Rn,imm0407,BcondTarget is (sub=0b0100 & Rn & imm0407) ... & BcondTarget { + if (Rn != zext(imm0407:1)) + goto BcondTarget; + } + + #:BNE Rn,Rm,disp17 1110_nnnn_mmmm_0101 dddd_dddd_dddd_dddd + :BNE Rn,Rm,BcondTarget is (sub=0b0101 & Rn & Rm) ... & BcondTarget { + if (Rn != Rm) + goto BcondTarget; + } + + #:BGEI Rn,imm4,disp17 1110_nnnn_iiii_1000 dddd_dddd_dddd_dddd + :BGEI Rn,imm0407,BcondTarget is (sub=0b1000 & Rn & imm0407) ... & BcondTarget { + if (Rn s>= imm0407) + goto BcondTarget; + } + + :REPEAT Rn,end is major=14 & Rm=0 & sub=9 & Rn; wsimm16 [ + end = inst_start + (wsimm16 << 1); + + #Save required info to the target instruction + REPEAT_START = inst_start + 4; + REPEAT_REMAINING = 2; + ELR = 0; + globalset(end, REPEAT_START); + globalset(end, REPEAT_REMAINING); + globalset(end, ELR); + ] { + #rpb = inst_next; + #rpe = end & ~1:4; #Clear ELR + + #Add 1 to RPC. This is compensated by pre-decrementing instead of post-decrementing + #when decoding last instruction in block and results in better disassembly. + rpc = Rn + 1:4; + } + + + :EREPEAT end is major=14 & Rn=0 & Rm=1 & sub=9 ; wsimm16 [ + end = inst_start + (wsimm16 << 1); + + #Save required info to the target instruction + ELR = 1; + REPEAT_START = inst_start + 4; + REPEAT_REMAINING = 2; + globalset(end, REPEAT_START); + globalset(end, REPEAT_REMAINING); + globalset(end, ELR); + ] { + rpb = inst_next; + rpe = end | 1:4; #Set ELR + } + + #:BLTI Rn,imm4,disp17 1110_nnnn_iiii_1100 dddd_dddd_dddd_dddd + :BLTI Rn,imm0407,BcondTarget is (sub=0b1100 & Rn & imm0407) ... & BcondTarget { + if (Rn s< imm0407) + goto BcondTarget; + } + + #1110_nnnn_DDDD_DD10 dddd_dddd_dddd_dddd (abs24=dddd_dddd_dddd_dddd||DDDD_DD||00) + :SW Rn,(abs24) is major=14 & op0001=0b10 & Rn & imm0207; wimm16 [ abs24 = (wimm16 << 8) | (imm0207 << 2); ] { + *:4 abs24:4 = Rn; + } + + #1110_nnnn_DDDD_DD11 dddd_dddd_dddd_dddd + :LW Rn,(abs24) is major=14 & op0001=0b11 & Rn & imm0207; wimm16 [ abs24 = (wimm16 << 8) | (imm0207 << 2); ] { + Rn = *:4 abs24:4; + } + + #--------------------------# + # Major Opcode 15 (32-bit) # + #--------------------------# + +@if (DSP_OPT == "1") && (NO_DEFAULT_DSP_INSTR == "0") + #There are three variants of the DSP instruction: + # DSP Rn, Rm, code16: Rn = DSP(Rn, Rm, code16) | Two input, one output registers + # DSP0 code24 : DSP0(code24) | No input/output register + # DSP1 Rn, code20 : Rn = DSP1(Rn) | One input/output register + # + #The problem is that these three instructions share the same encoding. + #It's impossible to distinguish them without DSP-specific documentation. + # + #To reduce disassembly artifacts, this placeholder implementation will + #correspond to the DSP0 instruction but separating Rn, Rm and code16 for readability. + # + + #1111_nnnn_mmmm_0000 cccc_cccc_cccc_cccc + define pcodeop DSPOperation; + :DSPx Rn,Rm,wimm16 is major=15 & sub=0 & Rn & Rm; wimm16 { + DSPOperation(Rn, Rm, wimm16:2); + } +@endif + +@if LDZ_OPT == "1" + #1111_nnnn_mmmm_0001 0000_0000_0000_0000 + :LDZ Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=0 { + Rn = lzcount(Rm); + } +@endif + +@if AVE_OPT == "1" + #1111_nnnn_mmmm_0001 0000_0000_0000_0010 + :AVE Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=2 { + Rn = (Rn + Rm + 1:4) s>> 1; + } +@endif + +@if ABS_OPT == "1" + #1111_nnnn_mmmm_0001 0000_0000_0000_0011 + :ABS Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=3 { + local res:4 = Rn - Rm; + if (res s>= 0:4) + goto ; + res = -res; + + + Rn = res; + } +@endif + +@if MIN_OPT == "1" + #1111_nnnn_mmmm_0001 0000_0000_0000_0100 + :MIN Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=4 { + if (Rn s> Rm) + goto ; + + Rn = Rn; + goto ; + + + Rn = Rm; + + } + + #1111_nnnn_mmmm_0001 0000_0000_0000_0101 + :MAX Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=5 { + if (Rn s< Rm) + goto ; + + Rn = Rn; + goto ; + + + Rn = Rm; + + } + + #1111_nnnn_mmmm_0001 0000_0000_0000_0110 + :MINU Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=6 { + if (Rn > Rm) + goto ; + + Rn = Rn; + goto ; + + + Rn = Rm; + + } + + #1111_nnnn_mmmm_0001 0000_0000_0000_0111 + :MAXU Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=7 { + if (Rn < Rm) + goto ; + + Rn = Rn; + goto ; + + + Rn = Rm; + + } +@endif + +@if SAT_OPT == "1" + #N.B. while the MeP Instruction Set manual says that both SADD and SSUB + #should check for overflow AND underflow, it is impossible(?) for SADD to + #underflow or for SSUB to overflow. Thus, both of these cases are not implemented. + + #1111_nnnn_mmmm_0001 0000_0000_0000_1000 + :SADD Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=8 { + if (scarry(Rn, Rm)) + goto ; + + Rn = Rn + Rm; + goto ; + + + Rn = 0x7FFFFFFF:4; + + } + + #1111_nnnn_mmmm_0001 0000_0000_0000_1001 + :SADDU Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=9 { + if (carry(Rn, Rm)) + goto ; + + Rn = Rn + Rm; + goto ; + + + Rn = 0xFFFFFFFF:4; + + } + + #1111_nnnn_mmmm_0001 0000_0000_0000_1010 + :SSUB Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=10 { + if (sborrow(Rn, Rm)) + goto ; + + Rn = Rn - Rm; + goto ; + + Rn = 0x80000000:4; #TODO: Is this OK? + + } + + #1111_nnnn_mmmm_0001 0000_0000_0000_1011 + :SSUBU Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=11 { + if (Rn < Rm) + goto ; + + Rn = Rn - Rm; + goto ; + + Rn = 0:4; + + } +@endif + +@if CLP_OPT == "1" + #1111_nnnn_0000_0001 0001_0000_iiii_i000 (imm5=iiii_i) + :CLIP Rn,wimm0307 is major=15 & sub=1 & Rm=0 & Rn; wop0815=16 & wop0002=0 & wimm0307 { + local upr:4 = (1:4 << (wimm0307 - 1:4)) - 1:4; + local lwr:4 = -(1:4 << (wimm0307 - 1:4)); + + if (Rn s> upr) + goto ; + + if (Rn s< lwr) + goto ; + + goto ; + + + Rn = lwr; + goto ; + + + Rn = upr; + + } + + #Special case: CLIP Rn,0 -> Rn = 0 + :CLIP Rn," 0x0" is major=15 & sub=1 & Rm=0 & Rn; wop0815=16 & wop0002=0 & wimm0307=0 { + Rn = 0:4; + } + + #1111_nnnn_0000_0001 0001_0000_iiii_i001 + :CLIPU Rn,wimm0307 is major=15 & sub=1 & Rm=0 & Rn; wop0815=16 & wop0002=1 & wimm0307 { + if (Rn s<= 0) + goto ; + + local upr:4 = (1:4 << wimm0307) - 1; + if (Rn > upr) + goto ; + + goto ; + + Rn = upr; + goto ; + + + Rn = 0:4; + + } + + #Special case: CLIPU Rn,0 -> Rn = 0 + :CLIPU Rn," 0x0" is major=15 & sub=1 & Rm=0 & Rn; wop0815=16 & wop0002=1 & wimm0307=0 { + Rn = 0:4; + } + +@endif + +@if MUL_OPT == "1" + #1111_nnnn_mmmm_0001 0011_0000_0000_0100 + :MADD Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=0b0011000000000100 { + local old:8 = (zext(hi) << 32) | zext(lo); + local add:8 = sext(Rn) * sext(Rm); + local new:8 = old + add; + SET_hi_lo(new); + } + + #1111_nnnn_mmmm_0001 0011_0000_0000_0101 + :MADDU Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=0b0011000000000101 { + local old:8 = (zext(hi) << 32) | zext(lo); + local add:8 = zext(Rn) * zext(Rm); + local new:8 = old + add; + SET_hi_lo(new); + } + + #1111_nnnn_mmmm_0001 0011_0000_0000_0110 + :MADDR Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=0b0011000000000110 { + local old:8 = (zext(hi) << 32) | zext(lo); + local add:8 = sext(Rn) * sext(Rm); + local new:8 = old + add; + SET_hi_lo(new); + Rn = new:4; + } + + #1111_nnnn_mmmm_0001 0011_0000_0000_0111 + :MADDRU Rn,Rm is major=15 & sub=1 & Rn & Rm; wimm16=0b0011000000000111 { + local old:8 = (zext(hi) << 32) | zext(lo); + local add:8 = zext(Rn) * zext(Rm); + local new:8 = old + add; + SET_hi_lo(new); + Rn = new:4; + } +@endif + +@if (UCI_OPT == "1") && (NO_DEFAULT_UCI_INSTR == "0") + #1111_nnnn_mmmm_0010 cccc_cccc_cccc_cccc + define pcodeop UserCustomInstruction; + :UCI Rn,Rm,wimm16 is major=15 & sub=2 & Rn & Rm; wimm16 { + Rn = UserCustomInstruction(Rn, Rm, wimm16:2); + } +@endif + + #1111_nnnn_0000_0100 aaaa_aaaa_aaaa_aaaa (abs16=aaaa_aaaa_aaaa_aaaa) + define pcodeop StoreControlBusRegister; + :STCB Rn, CBusReg is major=15 & sub=4 & op0407=0 & Rn; CBusReg { + StoreControlBusRegister(CBusReg, Rn); + #Emitting a write to CBusReg breaks the pcodeop disassembly. + } + + #1111_nnnn_0001_0100 aaaa_aaaa_aaaa_aaaa + :LDCB Rn, CBusReg is major=15 & sub=4 & op0407=1 & Rn; CBusReg { + Rn = CBusReg; + } + +@if (COP_OPT == "1") + #1111_nnnn_mmmm_0101 0000_0000_iiii_iiii (imm8=iiii_iiii) + :SBCPA CRn,RmMemInc,wsimm0007 is major=15 & sub=5 & CRn & RmMemInc; wop0815=0 & wsimm0007 { + *:1 RmMemInc = CRn:1; + RmMemInc = RmMemInc + sext(wsimm0007:1); + } + + #1111_nnnn_mmmm_0101 0001_0000_iiii_iii0 (imm8=iiii_iii||0) + :SHCPA CRn,RmMemInc,imm8 is major=15 & sub=5 & CRn & RmMemInc; + wop0811=0 & wop1215=1 & wsimm0107 & wop0001=0 [ imm8 = wsimm0107 << 1; ] { + *:2 RmMemInc = CRn:2; + RmMemInc = RmMemInc + sext(imm8:1); + } + + #1111_nnnn_mmmm_0101 0010_0000_iiii_ii00 (imm8=iiii_ii||00) + :SWCPA CRn,RmMemInc,imm8 is major=15 & sub=5 & CRn & RmMemInc; + wop0811=0 & wop1215=2 & wsimm0207 & wop0002=0 [ imm8 = wsimm0207 << 2; ] { + *:4 RmMemInc = CRn:4; + RmMemInc = RmMemInc + sext(imm8:1); + } + + #1111_nnnn_mmmm_0101 0011_0000_iiii_i000 (imm8=iiii_i||000) + :SMCPA CRn,RmMemInc,imm8 is major=15 & sub=5 & CRn & RmMemInc; + wop0811=0 & wop1215=3 & wsimm0307 & wop0003=0 [ imm8 = wsimm0307 << 3; ] { + *:8 RmMemInc = CRn; + RmMemInc = RmMemInc + sext(imm8:1); + } + + #1111_nnnn_mmmm_0101 0100_0000_iiii_iiii + :LBCPA CRn,RmMemInc,wsimm0007 is major=15 & sub=5 & CRn & RmMemInc; + wop0811=0 & wop1215=4 & wsimm0007 { + CRn[0,8] = *:1 RmMemInc; + RmMemInc = RmMemInc + sext(wsimm0007:1); + } + + #1111_nnnn_mmmm_0101 0101_0000_iiii_iii0 + :LHCPA CRn,RmMemInc,imm8 is major=15 & sub=5 & CRn & RmMemInc; + wop0811=0 & wop1215=5 & wsimm0107 & wop0001=0 [ imm8 = wsimm0107 << 1; ] { + CRn[0,16] = *:2 RmMemInc; + RmMemInc = RmMemInc + sext(imm8:1); + } + + #1111_nnnn_mmmm_0101 0110_0000_iiii_ii00 + :LWCPA CRn,RmMemInc,imm8 is major=15 & sub=5 & CRn & RmMemInc; + wop0811=0 & wop1215=6 & wsimm0207 & wop0002=0 [ imm8 = wsimm0207 << 2; ] { + CRn[0,32] = *:4 RmMemInc; + RmMemInc = RmMemInc + sext(imm8:1); + } + + #1111_nnnn_mmmm_0101 0111_0000_iiii_i000 + :LMCPA CRn,RmMemInc,imm8 is major=15 & sub=5 & CRn & RmMemInc; + wop0811=0 & wop1215=7 & wsimm0307 & wop0003=0 [ imm8 = wsimm0307 << 3; ] { + CRn = *:8 RmMemInc; + RmMemInc = RmMemInc + sext(imm8:1); + } + + #1111_nnnn_mmmm_0101 0000_1000_iiii_iiii + #SBCPM0 CRn,(Rm+),imm8 + + #1111_nnnn_mmmm_0101 0001_1000_iiii_iii0 + #SHCPM0 CRn,(Rm+),imm8.align2 + + #1111_nnnn_mmmm_0101 0010_1000_iiii_ii00 + #SWCPM0 CRn,(Rm+),imm8.align4 + + #1111_nnnn_mmmm_0101 0011_1000_iiii_i000 + #SMCPM0 CRn,(Rm+),imm8.align8 + + #1111_nnnn_mmmm_0101 0100_1000_iiii_iiii + #LBCPM0 CRn,(Rm+),imm8 + + #1111_nnnn_mmmm_0101 0101_1000_iiii_iii0 + #LHCPM0 CRn,(Rm+),imm8.align2 + + #1111_nnnn_mmmm_0101 0110_1000_iiii_ii00 + #LWCPM0 CRn,(Rm+),imm8.align4 + + #1111_nnnn_mmmm_0101 0111_1000_iiii_i000 + #LMCPM0 CRn,(Rm+),imm8.align8 + + #1111_nnnn_mmmm_0101 0000_1100_iiii_iiii + #SBCPM1 CRn,(Rm+),imm8 + + #1111_nnnn_mmmm_0101 0001_1100_iiii_iii0 + #SHCPM1 CRn,(Rm+),imm8.align2 + + #1111_nnnn_mmmm_0101 0010_1100_iiii_ii00 + #SWCPM1 CRn,(Rm+),imm8.align4 + + #1111_nnnn_mmmm_0101 0011_1100_iiii_i000 + #SMCPM1 CRn,(Rm+),imm8.align8 + + #1111_nnnn_mmmm_0101 0100_1100_iiii_iiii + #LBCPM1 CRn,(Rm+),imm8 + + #1111_nnnn_mmmm_0101 0101_1100_iiii_iii0 + #LHCPM1 CRn,(Rm+),imm8.align2 + + #1111_nnnn_mmmm_0101 0110_1100_iiii_ii00 + #LWCPM1 CRn,(Rm+),imm8.align4 + + #1111_nnnn_mmmm_0101 0111_1100_iiii_i000 + #LMCPM1 CRn,(Rm+),imm8.align8 + +@if (NO_DEFAULT_CP_INSTR == "0") + define pcodeop CoprocessorFunction; + #1111_CCCC_CCCC_0111 cccc_cccc_cccc_cccc (code24=CCCC_CCCC||cccc_cccc_cccc_cccc) + :CP code24 is major=15 & sub=7 & imm0411; wimm16 [ code24 = (imm0411 << 16) | wimm16; ] { + CoprocessorFunction(code24:4); + } +@endif #NO_DEFAULT_CP_INSTR == 0 + + #1111_nnnn_mmmm_0111 1111_0000_0000_0000 + :CMOV CRn,Rm is major=15 & sub=7 & CRn & Rm; wop0003=0 & wop0311=0 & wop1215=0xF { + CRn[0,32] = Rm; + } + + #1111_nnnn_mmmm_0111 1111_0000_0000_0001 + :CMOV Rm,CRn is major=15 & sub=7 & CRn & Rm; wop0003=1 & wop0311=0 & wop1215=0xF { + Rm = CRn:4; + } + + #1111_nnnn_mmmm_0111 1111_0001_0000_0000 + :CMOVH CRn,Rm is major=15 & sub=7 & CRn & Rm; + wop0003=0 & wop0307=0 & wop0811=1 & wop1215=0xF { + CRn[32,32] = Rm; + } + + #1111_nnnn_mmmm_0111 1111_0001_0000_0001 + :CMOVH Rm,CRn is major=15 & sub=7 & CRn & Rm; + wop0003=1 & wop0307=0 & wop0811=1 & wop1215=0xF { + Rm = CRn(4); + } + +@if LARGE_CP_REGNUM == "1" + #We need to handle c16-c31, distinguished by N=1 + + #1111_nnnn_mmmm_0111 1111_0000_0000_N000 (CRn=Nnnnn) + :CMOV CRnExt,Rm is major=15 & sub=7 & CRnExt & Rm; + wop0003=0b1000 & wop0411=0 & wop1215=0xF { + CRnExt[0,32] = Rm; + } + + #1111_nnnn_mmmm_0111 1111_0000_0000_N001 + :CMOV Rm,CRnExt is major=15 & sub=7 & CRnExt & Rm; + wop0003=0b1001 & wop0411=0 & wop1215=0xF { + Rm = CRnExt:4; + } + + #1111_nnnn_mmmm_0111 1111_0001_0000_N000 + :CMOVH CRnExt,Rm is major=15 & sub=7 & CRnExt & Rm; + wop0003=0b1000 & wop0411=1 & wop1215=0xF { + CRnExt[32,32] = Rm; + } + + #1111_nnnn_mmmm_0111 1111_0001_0000_N001 + :CMOVH Rm,CRnExt is major=15 & sub=7 & CRnExt & Rm; + wop0003=0b1001 & wop0411=1 & wop1215=0xF { + Rm = CRnExt(4); + } +@endif #LARGE_CP_REGNUM == 0 + + #Use pcodeop for these two because they're coprocessor-specific + + #1111_nnnn_mmmm_0111 1111_0000_0000_NN10 (CCRn=NNnnnn) + :CMOVC CCRn,Rm is major=15 & sub=7 & op0811 & Rm; wop0001=2 & wop0203 & wop0311=0 & + wop1215=0xF [ CCRn = (wop0203 << 4) | op0811; ] { + local src:4 = $(CP_CREG_OFFSET):4 + (zext(CCRn:1) * 4:4); + Rm = *[register]:4 src; + } + + #1111_nnnn_mmmm_0111 1111_0000_0000_NN11 + define pcodeop WriteCPConfigRegister; + :CMOVC Rm,CCRn is major=15 & sub=7 & op0811 & Rm; wop0001=3 & wop0203 & wop0311=0 & + wop1215=0xF [ CCRn = (wop0203 << 4) | op0811; ] { + local dst:4 = $(CP_CREG_OFFSET):4 + (zext(CCRn:1) * 4:4); + WriteCPConfigRegister(*[register]:4 dst, Rm); + } + + #1111_nnnn_mmmm_1100 dddd_dddd_dddd_dddd + :SWCP CRn,RmDisp16 is (major=15 & sub=12 & CRn) ... & RmDisp16 { + local dst:4 = RmDisp16 & ~3; + *:4 dst = CRn:4; + } + + #1111_nnnn_mmmm_1101 dddd_dddd_dddd_dddd + :LWCP CRn,RmDisp16 is (major=15 & sub=13 & CRn) ... & RmDisp16 { + #IMPLEMENTATION NOTE: SEE LWCPI + local src:4 = RmDisp16 & ~3; + CRn[0,32] = *:4 src; + } + + #1111_nnnn_mmmm_1110 dddd_dddd_dddd_dddd + :SMCP CRn,RmDisp16 is (major=15 & sub=14 & CRn) ... & RmDisp16 { + local dst:4 = RmDisp16 & ~7; + *:8 dst = CRn; + } + + #1111_nnnn_mmmm_1111 dddd_dddd_dddd_dddd + :LMCP CRn,RmDisp16 is (major=15 & sub=15 & CRn) ... & RmDisp16 { + local src:4 = RmDisp16 & ~7; + CRn = *:8 src; + } +@endif #COP_OPT == 1 + +} #with :OM=0 diff --git a/data/languages/MeP-c5/Options.sinc b/data/languages/MeP-c5/Options.sinc new file mode 100644 index 0000000..6a7489d --- /dev/null +++ b/data/languages/MeP-c5/Options.sinc @@ -0,0 +1,120 @@ + +# VLIW/Coprocessor options + +@ifndef VL32_OPT +@define VL32_OPT 0 +@endif + +@ifndef VL64_OPT +@define VL64_OPT 0 +@endif + +@if VL32_OPT == "1" +@define __HAS_VLIW 1 +@define __VLIW_ALIGN 3 +@elif VL64_OPT == "1" +@define __HAS_VLIW 1 +@define __VLIW_ALIGN 7 +@else +@define __HAS_VLIW 0 +@endif + +@ifndef COP_OPT +@if (VL32_OPT == "0") && (VL64_OPT == "0") +@define COP_OPT 0 +@else +@define COP_OPT 1 +@endif +@endif + +@ifndef LARGE_CP_REGNUM +@define LARGE_CP_REGNUM 0 +@endif + +@ifndef NO_DEFAULT_CP_INSTR +@define NO_DEFAULT_CP_INSTR 0 +@endif + + + +# Instruction options + +@ifndef MUL_OPT +@define MUL_OPT 0 +@endif + +@ifndef DIV_OPT +@define DIV_OPT 0 +@endif + +@ifndef BIT_OPT +@define BIT_OPT 0 +@endif + +@ifndef LDZ_OPT +@define LDZ_OPT 0 +@endif + +@ifndef ABS_OPT +@define ABS_OPT 0 +@endif + +@ifndef AVE_OPT +@define AVE_OPT 0 +@endif + +@ifndef MIN_OPT +@define MIN_OPT 0 +@endif + +@ifndef CLP_OPT +@define CLP_OPT 0 +@endif + +@ifndef SAT_OPT +@define SAT_OPT 0 +@endif + +@ifndef DBG_OPT +@define DBG_OPT 0 +@endif + + #Useless? +@ifndef PTO_OPT +@define PTO_OPT 0 +@endif + + #Useless? +@ifndef HWE_OPT +@define HWE_OPT 0 +@endif + +#User Custom Instruction option + +@ifndef UCI_OPT +@define UCI_OPT 0 +@endif + +@ifndef NO_DEFAULT_UCI_INSTR +@define NO_DEFAULT_UCI_INSTR 0 +@endif + + + +#Digital Signal Processor option + +@ifndef DSP_OPT +@define DSP_OPT 0 +@endif + +@ifndef NO_DEFAULT_DSP_INSTR +@define NO_DEFAULT_DSP_INSTR 0 +@endif + + + +#Timer + +@ifndef TIMER_CHANNELS +@define TIMER_CHANNELS 1 +@endif \ No newline at end of file diff --git a/data/languages/MeP-c5/STCLDC.sinc b/data/languages/MeP-c5/STCLDC.sinc new file mode 100644 index 0000000..85a50ed --- /dev/null +++ b/data/languages/MeP-c5/STCLDC.sinc @@ -0,0 +1,259 @@ +#SLEIGH doesn't let you link a split bit pattern to registers (AFAICT?). +#This makes implementing LDC and STC super annoying if you want +#to allow the registers' names to be displayed in the listing and +#usable in "Patch Instruction". We can always cheat, though. +# +#I hope there is a divinity somewhere to forgive the sin I'm about to commit. + + #0111_nnnn_iiii_100I (imm5=I||iiii) + #STC Rn, imm5 + with :op0103=0b100 { + #N.B. we use pcodeop to ensure the transfers are listed in decompiler output. + + #TODO: should we really clear the bits that are unwriteable? + + #pc -> 0 = 0b0_0000 + :STC Rn, pc is Rn & pc & op0000=0 & op0407=0b0000 { } #Writes to pc are ignored (nop) + + #lp -> 1 = 0b0_0001 + :STC Rn, lp is Rn & lp & op0000=0 & op0407=0b0001 { + #Used in function epilogues, don't use pcodeop to make decomp less cluttered + lp = Rn; + } + + #sar -> 2 = 0b0_0010 + define pcodeop write_sar; + :STC Rn, sar is Rn & sar & op0000=0 & op0407=0b0010 { + sar = zext(Rn[0,6]); + write_sar(Rn[0,6]); + } + + #rpb -> 4 = 0b0_0100 + define pcodeop write_rpb; + :STC Rn, rpb is Rn & rpb & op0000=0 & op0407=0b0100 { + rpb = Rn & ~1; + write_rpb(Rn & ~1); + } + + #rpe -> 5 = 0b0_0101 + define pcodeop write_rpe; + :STC Rn, rpe is Rn & rpe & op0000=0 & op0407=0b0101 { + rpe = Rn; + write_rpe(Rn); + } + + #rpc -> 6 = 0b0_0110 + define pcodeop write_rpc; + :STC Rn, rpc is Rn & rpc & op0000=0 & op0407=0b0110 { + rpc = Rn; + write_rpc(Rn); + } + + #hi -> 7 = 0b0_0111 + define pcodeop write_hi; + :STC Rn, hi is Rn & hi & op0000=0 & op0407=0b0111 { + hi = Rn; + write_hi(Rn); + } + + #lo -> 8 = 0b0_1000 + define pcodeop write_lo; + :STC Rn, lo is Rn & lo & op0000=0 & op0407=0b1000 { + lo = Rn; + write_lo(Rn); + } + +@if COP_OPT == "1" + #mb0 -> 12 = 0b0_1100 + define pcodeop write_mb0; + :STC Rn, mb0 is Rn & mb0 & op0000=0 & op0407=0b1100 { + mb0 = zext(Rn:2); + write_mb0(Rn:2); + } + + #me0 -> 13 = 0b0_1101 + define pcodeop write_me0; + :STC Rn, me0 is Rn & me0 & op0000=0 & op0407=0b1101 { + me0 = zext(Rn:2); + write_me0(Rn:2); + } + + #mb1 -> 14 = 0b0_1110 + define pcodeop write_mb1; + :STC Rn, mb1 is Rn & mb1 & op0000=0 & op0407=0b1110 { + mb1 = zext(Rn:2); + write_mb1(Rn:2); + } + + #me1 -> 15 = 0b0_1111 + define pcodeop write_me1; + :STC Rn, me1 is Rn & me1 & op0000=0 & op0407=0b1111 { + me1 = zext(Rn:2); + write_me1(Rn:2); + } +@endif + #psw -> 16 = 0b1_0000 + #TODO: SOME BITS ARE SUPPOSED TO BE READ-ONLY + define pcodeop write_psw; + :STC Rn, psw is Rn & psw & op0000=1 & op0407=0b000 { + psw = Rn; + write_psw(Rn); + } + + #id -> 17 = 0b1_0001 + :STC Rn, id is Rn & id & op0000=1 & op0407=0b0001 { } #Read-only register + + #tmp -> 18 = 0b1_0010 + define pcodeop write_tmp; + :STC Rn, tmp is Rn & tmp & op0000=1 & op0407=0b0010 { + tmp = Rn; + write_tmp(Rn); + } + + #epc -> 19 = 0b1_0011 + define pcodeop write_epc; + :STC Rn, epc is Rn & epc & op0000=1 & op0407=0b0011 { + epc = Rn; + write_epc(Rn); + } + + #exc -> 20 = 0b1_0100 + #TODO: SOME BITS ARE SUPPOSED TO BE READ-ONLY + define pcodeop write_exc; + :STC Rn, exc is Rn & exc & op0000=1 & op0407=0b0100 { + exc = Rn; + write_exc(Rn); + } + + #cfg -> 21 = 0b1_0101 + #TODO: SOME BITS ARE SUPPOSED TO BE READ-ONLY + define pcodeop write_cfg; + :STC Rn, cfg is Rn & cfg & op0000=1 & op0407=0b0101 { + cfg = Rn; + write_cfg(Rn); + } + + #npc -> 23 = 0b1_0111 + define pcodeop write_npc; + :STC Rn, npc is Rn & npc & op0000=1 & op0407=0b0111 { + npc = Rn; + write_npc(Rn); + } + +@if DBG_OPT == "1" + #dbg -> 24 = 0b1_1000 + #TODO: SOME BITS ARE SUPPOSED TO BE READ-ONLY + define pcodeop write_dbg; + :STC Rn, dbg is Rn & dbg & op0000=1 & op0407=0b1000 { + dbg = Rn; + write_dbg(Rn); + } + + #depc -> 25 = 0b1_1001 + define pcodeop write_depc; + :STC Rn, depc is Rn & depc & op0000=1 & op0407=0b1001 { + depc = Rn; + write_depc(Rn); + } +@endif + #opt -> 26 = 0b1_1010 + :STC Rn, opt is Rn & opt & op0000=1 & op0407=0b1010 { } #Read-only register + + #rcfg -> 27 = 0b1_1011 + :STC Rn, rcfg is Rn & rcfg & op0000=1 & op0407=0b1011 { } #Read-only register + + #ccfg -> 28 = 0b1_1100 + :STC Rn, ccfg is Rn & ccfg & op0000=1 & op0407=0b1100 { } #Read-only register + } + + #0111_nnnn_iiii_101I (imm5=I||iiii) + #:LDC Rn,imm5 + with :op0103=0b101 { + #Super Duper Hackerino 2: Electric Boogaloo! + + #N.B. we use pcodeop to ensure the transfers are listed in decompiler output. + + #TODO: should we really clear the bits that are unwriteable? + + #pc -> 0 = 0b0_0000 + :LDC Rn, pc is Rn & pc & op0000=0 & op0407=0b0000 { Rn = pc; } + + #lp -> 1 = 0b0_0001 + :LDC Rn, lp is Rn & lp & op0000=0 & op0407=0b0001 { Rn = lp; } + + #sar -> 2 = 0b0_0010 + :LDC Rn, sar is Rn & sar & op0000=0 & op0407=0b0010 { Rn = sar; } + + #rpb -> 4 = 0b0_0100 + :LDC Rn, rpb is Rn & rpb & op0000=0 & op0407=0b0100 { Rn = rpb; } + + #rpe -> 5 = 0b0_0101 + :LDC Rn, rpe is Rn & rpe & op0000=0 & op0407=0b0101 { Rn = rpe; } + + #rpc -> 6 = 0b0_0110 + :LDC Rn, rpc is Rn & rpc & op0000=0 & op0407=0b0110 { Rn = rpc; } + + #hi -> 7 = 0b0_0111 + :LDC Rn, hi is Rn & hi & op0000=0 & op0407=0b0111 { Rn = hi; } + + #lo -> 8 = 0b0_1000 + :LDC Rn, lo is Rn & lo & op0000=0 & op0407=0b1000 { Rn = lo; } + +@if COP_OPT == "1" + #mb0 -> 12 = 0b0_1100 + :LDC Rn, mb0 is Rn & mb0 & op0000=0 & op0407=0b1100 { Rn = mb0; } + + #me0 -> 13 = 0b0_1101 + :LDC Rn, me0 is Rn & me0 & op0000=0 & op0407=0b1101 { Rn = me0; } + + #mb1 -> 14 = 0b0_1110 + :LDC Rn, mb1 is Rn & mb1 & op0000=0 & op0407=0b1110 { Rn = mb1; } + + #me1 -> 15 = 0b0_1111 + :LDC Rn, me1 is Rn & me1 & op0000=0 & op0407=0b1111 { Rn = me1; } +@endif + #psw -> 16 = 0b1_0000 + #TODO: SOME BITS ARE SUPPOSED TO BE READ-ONLY + :LDC Rn, psw is Rn & psw & op0000=1 & op0407=0b000 { Rn = psw; } + + #id -> 17 = 0b1_0001 + :LDC Rn, id is Rn & id & op0000=1 & op0407=0b0001 { Rn = id; } + + #tmp -> 18 = 0b1_0010 + :LDC Rn, tmp is Rn & tmp & op0000=1 & op0407=0b0010 { Rn = tmp; } + + #epc -> 19 = 0b1_0011 + :LDC Rn, epc is Rn & epc & op0000=1 & op0407=0b0011 { Rn = epc; } + + #exc -> 20 = 0b1_0100 + #TODO: SOME BITS ARE SUPPOSED TO BE READ-ONLY + :LDC Rn, exc is Rn & exc & op0000=1 & op0407=0b0100 { Rn = exc; } + + #cfg -> 21 = 0b1_0101 + #TODO: SOME BITS ARE SUPPOSED TO BE READ-ONLY + :LDC Rn, cfg is Rn & cfg & op0000=1 & op0407=0b0101 { Rn = cfg; } + + #npc -> 23 = 0b1_0111 + :LDC Rn, npc is Rn & npc & op0000=1 & op0407=0b0111 { Rn = npc; } + +@if DBG_OPT == "1" + #dbg -> 24 = 0b1_1000 + #TODO: SOME BITS ARE SUPPOSED TO BE READ-ONLY + :LDC Rn, dbg is Rn & dbg & op0000=1 & op0407=0b1000 { Rn = dbg; } + + #depc -> 25 = 0b1_1001 + :LDC Rn, depc is Rn & depc & op0000=1 & op0407=0b1001 { Rn = depc; } +@endif + #opt -> 26 = 0b1_1010 + :LDC Rn, opt is Rn & opt & op0000=1 & op0407=0b1010 { Rn = opt; } + + #rcfg -> 27 = 0b1_1011 + :LDC Rn, rcfg is Rn & rcfg & op0000=1 & op0407=0b1011 { Rn = rcfg; } + + #ccfg -> 28 = 0b1_1100 + :LDC Rn, ccfg is Rn & ccfg & op0000=1 & op0407=0b1100 { Rn = ccfg; } + } + + + + \ No newline at end of file diff --git a/data/languages/MeP-c5/VNZMeP.sla b/data/languages/MeP-c5/VNZMeP.sla new file mode 100644 index 0000000..4c8f795 --- /dev/null +++ b/data/languages/MeP-c5/VNZMeP.sla @@ -0,0 +1,8764 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/languages/MeP-c5/Venezia.sla b/data/languages/MeP-c5/Venezia.sla new file mode 100644 index 0000000..2c0f572 --- /dev/null +++ b/data/languages/MeP-c5/Venezia.sla @@ -0,0 +1,32078 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/languages/MeP-c5/Venezia.slaspec b/data/languages/MeP-c5/Venezia.slaspec new file mode 100644 index 0000000..31d13b6 --- /dev/null +++ b/data/languages/MeP-c5/Venezia.slaspec @@ -0,0 +1,63 @@ +@define ENDIAN "little" + +define endian=$(ENDIAN); +define alignment=2; + +@define VL64_OPT 1 +@define LARGE_CP_REGNUM 1 +@define NO_DEFAULT_CP_INSTR 1 + +#TODO: which ones are correct? +@define MUL_OPT 1 +@define DIV_OPT 1 +@define BIT_OPT 1 +@define LDZ_OPT 1 +@define ABS_OPT 1 +@define AVE_OPT 1 +@define MIN_OPT 1 +@define CLP_OPT 1 +@define SAT_OPT 1 +@define DBG_OPT 1 + +@define TIMER_CHANNELS 2 + +@include "MeP-c5.sinc" + +#TODO: implement REPEAT and EREPEAT + +define register offset=$(CP_CREG_OFFSET) size=4 [ + CSAR0 CC _ _ COFR0 COFR1 COFA0 COFA1 + CPERM0 CPERM1 CPERM2 CPERM3 _ _ _ CSAR1 + ACC0_0 ACC0_1 ACC0_2 ACC0_3 ACC0_4 ACC0_5 ACC0_6 ACC0_7 + ACC1_0 ACC1_1 ACC1_2 ACC1_3 ACC1_4 ACC1_5 ACC1_6 ACC1_7 +]; + +define register offset=$(CP_CREG_OFFSET) size=32 [ + _ _ ACC0 ACC1 +]; + +# +#p1 instruction is always in the bottom 28-bits +# + +#V1: +# 63 - 16-bit Core - 48 | 47 - p0s - 28 | 27 - p1 - 0 +# +#V2: +# 63 - 32-bit Core - 32 | 31 - xxx - 28 | 27 - p1 - 0 +# +#V3 (CP code56): +# 63 - 1111 - 60 | 59 - p0 - 52 | 51 - 0111 - 48 | 47 - p0 (cont) - 28 | 27 - p1 - 0 +# +#C3 (CP code24 in Core mode): +# 31 - 1111 - 28 | 27 - c3 - 20 | 19 - 0111 - 16 | 15 - c3 - 0 +# +# p0 is handled in CP code56 +# c3 is handled in CP code24 +# + +with :OM=1 { + +} + + diff --git a/data/noReturnFunctionConstraints.xml b/data/noReturnFunctionConstraints.xml new file mode 100644 index 0000000..e3e22f8 --- /dev/null +++ b/data/noReturnFunctionConstraints.xml @@ -0,0 +1,5 @@ + + + VitaFunctionsThatDoNotReturn.txt + + \ No newline at end of file diff --git a/extension.properties b/extension.properties new file mode 100644 index 0000000..845d04d --- /dev/null +++ b/extension.properties @@ -0,0 +1,5 @@ +name=@extname@ +description=PlaystationVita loader for Ghidra +author=CreepNT +createdOn=2023-07-10 +version=@extversion@ diff --git a/ghidra_scripts/AddHardwareDeviceBlocks.py b/ghidra_scripts/AddHardwareDeviceBlocks.py new file mode 100644 index 0000000..6fb5d81 --- /dev/null +++ b/ghidra_scripts/AddHardwareDeviceBlocks.py @@ -0,0 +1,168 @@ +#Add memory blocks corresponding to hardware devices to the memory map +#@author CreepNT +#@category Vita +#@keybinding +#@menupath Vita.Add hardware devices +#@toolbar + +from ghidra.program.model.mem import MemoryConflictException + +mem = currentProgram.getMemory() + +num_blocks = 0 +num_created = 0 +num_conflicts = 0 +num_errors = 0 + +def getAddr(va): + return currentProgram.parseAddress("0x%08X" % va)[0] + +def mapDev(name, base, size=0x1000, comment=""): + global num_blocks + global num_created + global num_conflicts + global num_errors + num_blocks += 1 + + try: + blk = mem.createUninitializedBlock(name, getAddr(base), size, False) + blk.setRead(True) + blk.setWrite(True) + blk.setExecute(False) + blk.setVolatile(True) + blk.setComment(comment) + num_created += 1 + return blk + except MemoryConflictException: + existing = mem.getBlock(getAddr(base)) + if ((existing == None) or + (existing.getStart().getOffset() != base) or + (existing.getSize() != size)): + print("Conflict creating block %s @ 0x%08X" % (name, base)) + num_conflicts += 1 + else: + print("Skipped creating block %s - already exists" % name) + return None + except: + print("Failed creating block %s @ 0x%08X" % (name, base)) + num_errors += 1 + return None + +mapDev("PERIPHBASE", 0x1A000000, 0x3000) + +mapDev("Spad32K", 0x1F000000, 0x8000) + +mapDev("SceGpio0Reg", 0xE20A0000, 0x10000) +mapDev("SceGpio1Reg", 0xE0100000, 0x10000) + +mapDev("ScePervasiveMisc", 0xE3100000) +mapDev("ScePervasiveResetReg", 0xE3101000) +mapDev("ScePervasiveGate", 0xE3102000) +mapDev("ScePervasiveBaseClk", 0xE3103000) +mapDev("SceUartClkgenReg", 0xE3104000) +mapDev("ScePervasiveVid", 0xE3105000) +mapDev("ScePervasiveMailboxReg", 0xE3106000) +mapDev("ScePervasiveTas0", 0xE3108000) +mapDev("ScePervasiveTas1", 0xE3109000) +mapDev("ScePervasiveTas2", 0xE310A000) +mapDev("ScePervasiveTas3", 0xE310B000) +mapDev("ScePervasiveTas4", 0xE310C000) +mapDev("ScePervasiveTas5", 0xE310D000) +mapDev("ScePervasiveTas6", 0xE310E000) +mapDev("ScePervasiveTas7", 0xE310F000) +mapDev("ScePervasive2Reg", 0xE3110000) + +mapDev("SceTpiuReg", 0xE3203000) +mapDev("SceFunnelReg", 0xE3204000) +mapDev("SceItmReg", 0xE3205000) + +mapDev("Base Debug ROM Table", 0xE3200000) +mapDev("ARM Cortex-A9 Debug ROM Table", 0xE3300000) +mapDev("SceDbg0Reg", 0xE3310000) +mapDev("ScePmu0Reg", 0xE3311000) +mapDev("SceDbg1Reg", 0xE3312000) +mapDev("ScePmu1Reg", 0xE3313000) +mapDev("SceDbg2Reg", 0xE3314000) +mapDev("ScePmu2Reg", 0xE3315000) +mapDev("SceDbg3Reg", 0xE3316000) +mapDev("ScePmu3Reg", 0xE3317000) +mapDev("ScePfmReg", 0xE50D0000, 0x2000, "Performance Monitoring/Analysis") + +mapDev("SceCti0Reg", 0xE3318000) +mapDev("SceCti1Reg", 0xE3319000) +mapDev("SceCti2Reg", 0xE331A000) +mapDev("SceCti3Reg", 0xE331B000) +#TODO ScePtm +#TODO VFP registers +#TODO Usb/UDCD + +mapDev("ARM/CMeP Comm", 0xE0000000, 0x10000) +mapDev("CMeP Reset", 0xE0010000, 0x10000) +mapDev("CMeP 0xE0020000 unknown", 0xE0020000, 0x10000) +mapDev("CMeP Keyring Controller", 0xE0030000, 0x10000) +mapDev("CMeP Math Processor", 0xE0040000, 0x10000) +mapDev("Bigmac Engine", 0xE0050000) +mapDev("CMeP Keyring", 0xE0058000, 0x10000) +mapDev("SceEmmcController", 0xE0070000, 0x50000) +mapDev("Unknown CMeP registers", 0xE00C0000, 0x40000) + +mapDev("SceSblDMAC5DmacKR", 0xE04E0000, 0x1000, "DMAC5 Key Ring") +mapDev("SceDmacmgrDmac0Reg", 0xE3000000) +mapDev("SceDmacmgrDmac1Reg", 0xE3010000) +mapDev("SceDmacmgrDmac2Reg", 0xE5000000) +mapDev("SceDmacmgrDmac3Reg", 0xE5010000) +mapDev("SceDmacmgrDmac4Reg", 0xE0400000) +mapDev("SceDmacmgrDmac5Reg", 0xE0410000) +mapDev("SceDmacmgrDmac6Reg", 0xE50C0000) + +mapDev("SceIftu0RegA", 0xE5020000) +mapDev("SceIftu0RegB", 0xE5021000) +mapDev("SceIftuc0Reg", 0xE5022000) +mapDev("SceIftu1RegA", 0xE5030000) +mapDev("SceIftu1RegB", 0xE5031000) +mapDev("SceItfuc1Reg", 0xE5032000) +mapDev("SceIftu2Reg", 0xE5040000) + +mapDev("SceDsi0Reg", 0xE5050000) +mapDev("SceDsi1Reg", 0xE5060000) + +mapDev("SceCompatMailbox", 0xE5070000) +mapDev("SceCompatLCDDMA", 0xE5071000) +mapDev("SceCompatSharedSram", 0xE8100000) + +mapDev("SceSonyRegbus", 0xE8000000, 0x2000) +mapDev("SceEmcTop", 0xE8200000) +mapDev("SceGrab", 0xE8300000, 0x2000) + +mapDev("LPDDR2 I/F CH1", 0xE5880000, 0x10000) +mapDev("LPDDR2 I/F CH0", 0xE6000000, 0x10000) + +mapDev("SceSpi0Reg", 0xE0A00000) +mapDev("SceSpi1Reg", 0xE0A10000) +mapDev("SceSpi2Reg", 0xE0A20000) + +mapDev("SceLT0", 0xE20B1000) +mapDev("SceLT1", 0xE20B2000) +mapDev("SceLT2", 0xE20B3000) +mapDev("SceLT3", 0xE20B4000) +mapDev("SceLT4", 0xE20B5000) +mapDev("SceLT5", 0xE20B6000) +mapDev("SceWT0", 0xE20B7000) +mapDev("SceWT1", 0xE20B8000) +mapDev("SceWT2", 0xE20B9000) +mapDev("SceWT3", 0xE20BA000) +mapDev("SceWT4", 0xE20BB000) +mapDev("SceWT5", 0xE20BC000) +mapDev("SceWT6", 0xE20BD000) +mapDev("SceWT7", 0xE20BE000) +mapDev("Timer Misc Reg", 0xE20BF000) + +mapDev("SceSdio0", 0xE5800000, 0x10000) +mapDev("SceSdio1", 0xE5810000, 0x10000) + +mapDev("BUS REGISTERS", 0xEC000000, 0x4000000) + +print("%d memory blocks declared" % num_blocks) +print("\t- %d blocks created" % num_created) +print("\t- %d conflicts" % num_conflicts) +print("\t- %d errors" % num_errors) diff --git a/ghidra_scripts/MapRAMForNSKBL.py b/ghidra_scripts/MapRAMForNSKBL.py new file mode 100644 index 0000000..a8c5214 --- /dev/null +++ b/ghidra_scripts/MapRAMForNSKBL.py @@ -0,0 +1,48 @@ +#Add the missing parts of LPDDR2TOP in the memory map for NSKBL +#@author CreepNT +#@category Vita +#@keybinding +#@menupath Vita.Add RAM for NSKBL +#@toolbar + +LPDDR2_START = 0x40000000 +LPDDR2_END = 0x60000000 +nskbl_start = 0x51000000 + +mem = currentProgram.getMemory() + +def getAddr(va): + return currentProgram.parseAddress("0x%08X" % va)[0] + +nskbl = mem.getBlock(getAddr(nskbl_start)) +if (nskbl == None): + print("NSKBL not detected at 0x%08X - aborting" % nskbl_start) +elif ((nskbl.getStart().getOffset() == LPDDR2_START) and + (nskbl.getEnd().add(1).getOffset() == LPDDR2_END)): + print("LPDDR2TOP is already mapped!") +else: + #Create a block for the DRAM before NSKBL (0x4000_0000 to nskbl_start) + block_before = mem.createInitializedBlock( + "LPDDR0", + getAddr(LPDDR2_START), + nskbl_start - LPDDR2_START, + 0, monitor, False + ) + + block_before.setRead(nskbl.isRead()) + block_before.setWrite(nskbl.isWrite()) + block_before.setExecute(nskbl.isExecute()) + block_before.setComment("LPDDR2TOP / DRAM Bank 0") + + #Create a block for the DRAM after NSKBL (up to 0x6000_0000) + #N.B. we don't need to set details on this block + #They will get copied from previous block during join + after_start = nskbl.getEnd().add(1) + block_after = mem.createInitializedBlock( + "_", after_start, + LPDDR2_END - after_start.getOffset(), + 0, monitor, False + ) + + tmp = mem.join(block_before, nskbl) + tmp = mem.join(tmp, block_after) \ No newline at end of file diff --git a/lib/LICENSES b/lib/LICENSES new file mode 100644 index 0000000..735ceed --- /dev/null +++ b/lib/LICENSES @@ -0,0 +1,23 @@ +The files in this directory are covered by the following licenses: + +-- yamlbeans-1.15.jar -- + +Copyright (c) 2008 Nathan Sweet, Copyright (c) 2006 Ola Bini + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/main/java/vitaloaderredux/analyzer/NIDAnalyzer.java b/src/main/java/vitaloaderredux/analyzer/NIDAnalyzer.java new file mode 100644 index 0000000..7d60376 --- /dev/null +++ b/src/main/java/vitaloaderredux/analyzer/NIDAnalyzer.java @@ -0,0 +1,263 @@ +package vitaloaderredux.analyzer; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; + +import docking.widgets.filechooser.GhidraFileChooser; +import ghidra.app.services.AbstractAnalyzer; +import ghidra.app.services.AnalysisPriority; +import ghidra.app.services.AnalyzerType; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.Application; +import ghidra.framework.options.Options; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.listing.CircularDependencyException; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; +import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.symbol.Symbol; +import ghidra.program.model.util.ObjectPropertyMap; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.DuplicateNameException; +import ghidra.util.exception.InvalidInputException; +import ghidra.util.filechooser.GhidraFileChooserModel; +import ghidra.util.filechooser.GhidraFileFilter; +import ghidra.util.task.TaskMonitor; + +import vitaloaderredux.database.NIDDatabase; +import vitaloaderredux.loader.ArmElfPrxLoader; +import vitaloaderredux.misc.ImportExportProperty; +import vitaloaderredux.misc.ImportExportProperty.IEType; +import vitaloaderredux.misc.ProgramProcessingHelper; +import vitaloaderredux.misc.Utils; + +/** + * NID analyzer for ARM executables. This analyzer walks the Libstub and Libent + * tables and renames all functions and variables based on a NID database + * provided to it. + */ +public class NIDAnalyzer extends AbstractAnalyzer { + + static private final String ANALYZER_TITLE = "NID Resolution"; + static private final String ANALYZER_DESCRIPTION = + "Resolves the names of all imports and exports of this module." + + "Names are extracted from a NID database in YAML format."; + + static private final DatabaseKind DATABASE_CHOICE_DEFAULT = DatabaseKind.BuiltInDatabase; + static private final String DATABASE_CHOICE_NAME = "Database"; + static private final String DATABASE_CHOICE_DESCRIPTION = + "Database from which names should be extracted for analysis"; + + static private final boolean DELETE_OLD_OPTION_DEFAULT = true; + static private final String DELETE_OLD_OPTION_NAME = "Delete old names"; + static private final String DELETE_OLD_OPTION_DESCRIPTION = + "If enabled, clears all names for imports and exports before analysis"; + + + static private final String BUILTIN_DEFAULT_DATABASE_FILENAME = "databases/DefaultNIDDatabase.yaml"; + static private final String BUILTIN_SECONDARY_DATABASE_FILENAME = "databases/SecondaryNIDDatabase.yaml"; + + public static enum DatabaseKind { + BuiltInDatabase, BuiltInSecondaryDatabase, ExternalDatabase + } + + private NIDDatabase database; + private ProgramProcessingHelper helper; + + //Options + private DatabaseKind chosenDB = DATABASE_CHOICE_DEFAULT; + private boolean clearOldNames = true; + + public NIDAnalyzer() { + super(ANALYZER_TITLE, ANALYZER_DESCRIPTION, AnalyzerType.BYTE_ANALYZER); + + // Allows to walk the binary with DB names ASAP. + setPriority(AnalysisPriority.HIGHEST_PRIORITY); + setSupportsOneTimeAnalysis(); + setDefaultEnablement(true); + } + + @Override + public boolean canAnalyze(Program program) { + return ArmElfPrxLoader.isArmExecutable(program); + } + + @Override + public void registerOptions(Options options, Program program) { + options.registerOption(DELETE_OLD_OPTION_NAME, + DELETE_OLD_OPTION_DEFAULT,null, DELETE_OLD_OPTION_DESCRIPTION); + options.registerOption(DATABASE_CHOICE_NAME, + DATABASE_CHOICE_DEFAULT, null, DATABASE_CHOICE_DESCRIPTION); + } + + @Override + public void optionsChanged(Options options, Program program) { + chosenDB = options.getEnum(DATABASE_CHOICE_NAME, DATABASE_CHOICE_DEFAULT); + clearOldNames = options.getBoolean(DELETE_OLD_OPTION_NAME, DELETE_OLD_OPTION_DEFAULT); + } + + private boolean isSystematicName(String libraryName, String symbolName) { + return symbolName.startsWith(libraryName + "_") && Utils.isSystematicName(symbolName); + } + + private void deleteNonSystematicNamedSymbols(Address address, String libraryName, boolean functionSymbols) { + Symbol[] symbols = helper.symTbl.getSymbols(address); + for (Symbol sym : symbols) { + if (isSystematicName(libraryName, sym.getName(false))) + continue; + + if (functionSymbols) { // Function symbols have to be deleted with this method. + helper.symTbl.removeSymbolSpecial(sym); + } else { + sym.delete(); + } + } + } + + // These lists store the VA of every function/variable seen during this run of + // the analyzer. + ArrayList knownFunctionsList = new ArrayList(); + ArrayList knownVariablesList = new ArrayList(); + + // Returns whether or not address was in VA list, and adds it in VA list if false. + private static boolean checkAndSetKnownAddress(ArrayList vaList, Address addr) { + final long va = addr.getUnsignedOffset(); + final boolean seenBefore = vaList.contains(va); + if (!seenBefore) { + vaList.add(va); + } + return seenBefore; + } + + private void analyzeFunction(IEType importOrExport, String libraryName, Address funcAddr, int functionNID) { + final String databaseName = database.getFunctionName(libraryName, functionNID); + final boolean functionSeenBefore = checkAndSetKnownAddress(knownFunctionsList, funcAddr); + if (clearOldNames && !functionSeenBefore) { + deleteNonSystematicNamedSymbols(funcAddr, libraryName, true); + } + + if (databaseName != null) { + // Replace the current name of the function with the DB's name + // and add back the overwritten name as a symbol. + Function func = helper.funcMgr.getFunctionAt(funcAddr); + String oldName = func.getName(false); + + try { + //Under certain circumstances, the import thunk may find itself + //in the External namespace, which leads to duplicates in program tree. + //Put the function in global namespace to prevent this from happening. + func.setParentNamespace(helper.program.getGlobalNamespace()); + + func.setName(databaseName, SourceType.USER_DEFINED); + if (importOrExport == IEType.IMPORT) { + //Set name for the thunk too + func.getThunkedFunction(true).setName(databaseName, SourceType.USER_DEFINED); + } + + helper.symTbl.createLabel(funcAddr, oldName, SourceType.ANALYSIS); + } catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) { + System.err.println(e); + e.printStackTrace(); + } + } + } + + private void analyzeVariable(IEType importOrExport, String libraryName, Address varAddr, int variableNID) { + final boolean variableSeenBefore = checkAndSetKnownAddress(knownVariablesList, varAddr); + if (clearOldNames && !variableSeenBefore) { + // Clear all symbols that are not systematic names if the variable has not been seen before. + deleteNonSystematicNamedSymbols(varAddr, libraryName, false); + } + + final String databaseName = database.getVariableName(libraryName, variableNID); + if (databaseName != null) { + // Create a new symbol with the DB's name and set it as primary + try { + helper.flatAPI.createLabel(varAddr, databaseName, /* Global Namespace */null, true, SourceType.USER_DEFINED); + } catch (Exception e) { + + } + } + } + + @Override + public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) + throws CancelledException { + + helper = new ProgramProcessingHelper(program); + + File databaseFile = null; + try { + switch (chosenDB) { + case ExternalDatabase: { + GhidraFileFilter yamlFilter = new GhidraFileFilter() { + public String getDescription() { + return "NID database (.yml, .yaml)"; + } + + public boolean accept(File pathname, GhidraFileChooserModel model) { + String fileName = pathname.getName(); + return pathname.isDirectory() || fileName.endsWith(".yml") || fileName.endsWith(".yaml"); + } + }; + + GhidraFileChooser fileChooser = new GhidraFileChooser(null); + fileChooser.setFileFilter(yamlFilter); + fileChooser.setTitle("Choose the NID database file to use for this analysis"); + fileChooser.setApproveButtonText("Use selected file"); + fileChooser.setApproveButtonToolTipText( + "Use the selected file as the NID database file for this analysis"); + // fileChooser.rescanCurrentDirectory(); + databaseFile = fileChooser.getSelectedFile(); + break; + } + case BuiltInDatabase: { + databaseFile = Application.getModuleDataFile(BUILTIN_DEFAULT_DATABASE_FILENAME).getFile(false); + break; + } + case BuiltInSecondaryDatabase: { + databaseFile = Application.getModuleDataFile(BUILTIN_SECONDARY_DATABASE_FILENAME).getFile(false); + break; + } + default: + return false; + } + } catch (FileNotFoundException e) { + return false; + } + + if (databaseFile == null) { + return false; + } + + database = new NIDDatabase(databaseFile); + ObjectPropertyMap iepMap = ArmElfPrxLoader.getImportExportPropertyMap(program); + + try { + Address iepAddr = iepMap.getFirstPropertyAddress(); + while (iepAddr != null) { + ImportExportProperty iep = iepMap.get(iepAddr); + String libName = iep.getLibraryName(); + int NID = iep.getNID(); + switch (iep.getKind()) { + case FUNCTION: + analyzeFunction(iep.getType(), libName, iepAddr, NID); + break; + case VARIABLE: + case TLS_VARIABLE: + analyzeVariable(iep.getType(), libName, iepAddr, NID); + default: + break; + } + + iepAddr = iepMap.getNextPropertyAddress(iepAddr); + } + + } catch (Exception e) { + return false; + } + return true; + } +} diff --git a/src/main/java/vitaloaderredux/arm_relocation/ArmRelocator.java b/src/main/java/vitaloaderredux/arm_relocation/ArmRelocator.java new file mode 100644 index 0000000..da81a9d --- /dev/null +++ b/src/main/java/vitaloaderredux/arm_relocation/ArmRelocator.java @@ -0,0 +1,332 @@ +package vitaloaderredux.arm_relocation; + +import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.opinion.LoadException; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.CodeUnit; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.mem.MemoryBlock; + +import vitaloaderredux.elf.MalformedElfException; + +import vitaloaderredux.loader.ArmElfPrxLoaderContext; + +import vitaloaderredux.misc.BitfieldReader; +import vitaloaderredux.misc.Datatypes; +import vitaloaderredux.misc.Utils; +import vitaloaderredux.misc.ImportExportProperty.IEKind; +import vitaloaderredux.misc.ImportExportProperty.IEType; + +public class ArmRelocator { + + static public final int SIZE_OF_FORMAT_1_RELOCATION = 8; + + //Commented out types are not supported in reftable + static private final int + //R_ARM_NONE = 0, + R_ARM_ABS32 = 2, R_ARM_TARGET1 = 38, + R_ARM_REL32 = 3, R_ARM_TARGET2 = 41, + R_ARM_THM_CALL = 10, + R_ARM_CALL = 28, + R_ARM_JUMP24 = 29, + //R_ARM_V4BX = 40, + R_ARM_PREL31 = 42, + R_ARM_MOVW_ABS_NC = 43, + R_ARM_MOVT_ABS = 44, + R_ARM_THM_MOVW_ABS_NC = 47, + R_ARM_THM_MOVT_ABS = 48, + R_ARM_RBASE = 255; + + private final ArmElfPrxLoaderContext ctx; + private final int variableSize; + private int curVariablesNum = 0; + private final int maxVariablesNum; + + private MemoryBlock varImportBlock; + private Address blockStartAddr; + + public ArmRelocator(ArmElfPrxLoaderContext context, int varImportBlockStartVA, int varImportBlockSize, int perVariableSize) throws Exception { + ctx = context; + variableSize = perVariableSize; + maxVariablesNum = varImportBlockSize / perVariableSize; + + blockStartAddr = ctx.getAddressInDefaultAS(varImportBlockStartVA); + varImportBlock = ctx.memory.createUninitializedBlock("VarImport", blockStartAddr, varImportBlockSize, false); + + varImportBlock.setRead(true); + varImportBlock.setWrite(true); + varImportBlock.setExecute(false); + varImportBlock.setComment("Imported variables memory block"); + } + + /** + * Obtain the size (in bytes) of a Prx2 format reftable. + * @param addr The address of the reftable + * @return The size of the relinfo + * @note This size includes the 32-bit header + */ + private int getPrx2RefInfoTableSize(Address tableAddr) throws MemoryAccessException, MalformedElfException { + int header = ctx.flatAPI.getInt(tableAddr); + if ((header & 0xF000000F) != 0) { + //N.B. old firmwares only check (& 0xF) == 0 + throw new MalformedElfException("Invalid reftable header"); + } + return (header & 0x0FFFFFFF0) >>> 4; + } + + /** + * Allocates a slot from the import variable slot. + * This function creates a label with the systematic name at the slot's address + * on behalf of the caller, and also registers it in the Imports/Exports table. + * @param libraryName + * @param variableNID + * @param tls true if slot is allocated for a TLS variable, false otherwise + * @return The address of the slot + * @throws Exception + */ + public Address allocateImportVariableSlot(String libraryName, int variableNID, boolean tls) throws Exception { + if (curVariablesNum >= maxVariablesNum) { + throw new LoadException("Import variable block overflow"); + } + + Address varAddr = blockStartAddr.add(curVariablesNum * variableSize); + ctx.flatAPI.createLabel(varAddr, Utils.getSystematicName(libraryName, variableNID), true); + ctx.addImportExportEntry(varAddr, libraryName, variableNID, IEType.IMPORT, tls ? IEKind.TLS_VARIABLE : IEKind.VARIABLE); + + curVariablesNum++; + + return varAddr; + } + + private int readInt(int va) throws MemoryAccessException { + return ctx.flatAPI.getInt(ctx.getAddressInDefaultAS(va)); + } + + private void writeInt(int va, int val) throws MemoryAccessException { + final Address dst = ctx.getAddressInDefaultAS(va); + ctx.flatAPI.setInt(dst, val); + } + + private void apply_one_relocation(int r_type, int S, int A, int P) throws MemoryAccessException, MalformedElfException { + final int displacement = (A - P + S); + + switch (r_type) { + case R_ARM_ABS32: + case R_ARM_TARGET1: { + writeInt(P, S + A); + break; + } + + case R_ARM_REL32: + case R_ARM_TARGET2: { + writeInt(P, displacement); + break; + } + + case R_ARM_THM_CALL: { + //This is a Thumb opcode, actually make of two 16-bit parts. + //Due to being read as a 32-bit integer, this results in them being reversed. + //To read bit X of the first part (leftmost in the ARMARM), read bit (X). + //To read bit X of the second part (rightmost in the ARMARM), read bit (X+16). + int opcode = readInt(P); + + //Only bits 0x01FF_FFFE of the displacement are used. + //Shift the displacement in advance to make the rest of the logic simpler. + BitfieldReader displacementReader = new BitfieldReader(displacement >>> 1); + + int imm11 = displacementReader.consume(11); + if ((opcode & ((1 << 12) << 16)) == 0) { //Bit 12 of 2nd part is clear: Encoding T2 (BLX) + //In BLX, imm11 is actually imm10L:H where H must be 0 - otherwise, the instruction is undefined. + //Ensure that bit H is clear by not copying it from the displacement. + imm11 &= 0x7FE; + } + + final int imm10 = displacementReader.consume(10); + final int I1 = displacementReader.consume(1); + final int I2 = displacementReader.consume(1); + final int Sign = displacementReader.consume(1); //Bit S in ARMARM + + displacementReader.assertConsumption("R_ARM_THM_CALL", 24); + + //I1 = NOT(J1 EOR S) --> J1 = NOT(I1) ^ S = (I1 == S) + //I2 = NOT(J2 EOR S) --> J2 = NOT(I2) ^ S = (I2 == 2) + final int J1 = (I1 == Sign) ? 1 : 0; + final int J2 = (I2 == Sign) ? 1 : 0; + + opcode &= 0xD000F800; + opcode |= (S << 10) | imm10; //1st 16-bit part + opcode |= ((J1 << 13) | (J2 << 11) | imm11) << 16; //2nd 16-bit part + + writeInt(P, opcode); + break; + } + + case R_ARM_CALL: + case R_ARM_JUMP24: { + int opcode = readInt(P); + + if ((opcode & 0xF0000000) == 0xF) { //BL,BLX (immediate) Encoding A2 - BLX + + //Original ASM is an optimized equivalent of: + // opcode = (opcode & 0xFE7FFFFF) | (displacement & 0x3) << 23; + // + //This sets imm24's top bit, but imm24 will be cleared right after + //so it's fine, and allows to use a single BFI instruction instead + //of a shift+OR pair, saving both space and time. + // + //The original C code is probably something akin to what follows. + + //Set the H bit of the immediate, taken from bit 1 of the displacement. + opcode = (opcode & 0xFEFFFFFF) | (displacement & 0x2) << 23; + } + opcode = (opcode & 0xFF000000) | (displacement >>> 2) & 0xFFFFFF; + + writeInt(P, opcode); + break; + } + + case R_ARM_PREL31: { + writeInt(P, displacement & 0x7FFFFFFF); + break; + } + + + case R_ARM_MOVW_ABS_NC: { + int opcode = readInt(P); + + final int target = (S + A); + final int imm12 = target & 0xFFF; + final int imm4 = (target >>> 12) & 0xF; + + opcode &= 0xFFF0F000; + opcode |= (imm4 << 16) | imm12; + + writeInt(P, opcode); + break; + } + + + case R_ARM_MOVT_ABS: { + int opcode = readInt(P); + + final int target = (S + A); + final int imm12 = (target >>> 16) & 0xFFF; + final int imm4 = (target >>> 28) & 0xF; + + opcode &= 0xFFF0F000; + opcode |= (imm4 << 16) | imm12; + + writeInt(P, opcode); + break; + } + + case R_ARM_THM_MOVW_ABS_NC: { + int opcode = readInt(P); + + final int target = (S + A); + final int imm8 = target & 0xFF; + final int imm3 = (target >>> 8) & 0x7; + final int imm1 = (target >>> 11) & 0x1; + final int imm4 = (target >>> 12) & 0xF; + + opcode &= 0x8F00FBF0; + opcode |= (imm8 << 16) | (imm3 << 28) | (imm1 << 10) | imm4; + + writeInt(P, opcode); + break; + } + + case R_ARM_THM_MOVT_ABS: { + int opcode = readInt(P); + + final int target = (S + A); + final int imm8 = (target >>> 16) & 0xFF; + final int imm3 = (target >>> 24) & 0x7; + final int imm1 = (target >>> 27) & 0x1; + final int imm4 = (target >>> 28) & 0xF; + + opcode &= 0x8F00FBF0; + opcode |= (imm8 << 16) | (imm3 << 28) | (imm1 << 10) | imm4; + + writeInt(P, opcode); + break; + } + + //case R_ARM_NONE: + //case R_ARM_V4BX: + case R_ARM_RBASE: + //Modulemgr ignores these entries + break; + + default: + throw new MalformedElfException("Unknown rel type " + r_type); + } + } + + private void markupInfo(StructConverter info, Address addr, int P) throws Exception { + ctx.createData(addr, info.toDataType()); + ctx.listing.setComment(addr, CodeUnit.PRE_COMMENT, String.format("Relocation target: 0x%08X", P)); + } + + private void processPrx1Reftable(Address infoAddr, int destVA) throws Exception { + while (ctx.flatAPI.getInt(infoAddr) != 0) { + Prx1RefInfo info = new Prx1RefInfo(ctx.getBinaryReader(infoAddr)); + apply_one_relocation(info.reltype, destVA, info.addend, info.P); + + markupInfo(info, infoAddr, info.P); + infoAddr = infoAddr.add(Prx1RefInfo.SIZE); + } + } + + private void processPrx2Reftable(Address infoAddr, Address maxAddr, int destVA) throws Exception { + while (infoAddr.compareTo(maxAddr) < 0) { + final int form = (ctx.flatAPI.getInt(infoAddr) & 0xF); + switch (form) { + case 1: { + Prx2RefInfoForm1 info = new Prx2RefInfoForm1(ctx.getBinaryReader(infoAddr)); + final int P = (int)((ctx.elfEhdr.programHeaders[info.segment].p_vaddr + info.offset) & 0xFFFFFFFFL); + + apply_one_relocation(info.reltype, destVA, info.addend, P); + + markupInfo(info, infoAddr, P); + infoAddr = infoAddr.add(Prx2RefInfoForm1.SIZE); + break; + } + case 2: { + Prx2RefInfoForm2 info = new Prx2RefInfoForm2(ctx.getBinaryReader(infoAddr)); + final int P = (int)((ctx.elfEhdr.programHeaders[info.segment].p_vaddr + info.offset) & 0xFFFFFFFFL); + + apply_one_relocation(info.reltype, destVA, info.addend, P); + + markupInfo(info, infoAddr, P); + infoAddr = infoAddr.add(Prx2RefInfoForm2.SIZE); + break; + } + default: + throw new MalformedElfException("Invalid refinfo form " + form); + } + } + + if (infoAddr.compareTo(maxAddr) != 0) { + throw new MalformedElfException("Reftable overflow"); + } + } + + /** + * Parse a reference table and perform all the relocations it contains. + * @param refTableAddr Address of the reference table + * @param destAddr Address of the referenced data/function + */ + public void processReftable(Address refTableAddr, Address destAddr) throws Exception { + final int destVA = (int)destAddr.getUnsignedOffset(); + + if (ctx.elfEhdr.isPrx1ELF()) { + processPrx1Reftable(refTableAddr, destVA); + } else { + ctx.createData(refTableAddr, Datatypes.u32); + final int tableSize = getPrx2RefInfoTableSize(refTableAddr); + final Address refInfoAddr = refTableAddr.add(4); + processPrx2Reftable(refInfoAddr, refTableAddr.add(tableSize), destVA); + } + } +} diff --git a/src/main/java/vitaloaderredux/arm_relocation/Prx1RefInfo.java b/src/main/java/vitaloaderredux/arm_relocation/Prx1RefInfo.java new file mode 100644 index 0000000..d2634b1 --- /dev/null +++ b/src/main/java/vitaloaderredux/arm_relocation/Prx1RefInfo.java @@ -0,0 +1,50 @@ +package vitaloaderredux.arm_relocation; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.StructConverter; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.InvalidDataTypeException; +import ghidra.program.model.data.StructureDataType; +import vitaloaderredux.misc.BitfieldReader; +import vitaloaderredux.misc.Datatypes; +import vitaloaderredux.misc.Utils; + +//Prx1 reference information +public class Prx1RefInfo implements StructConverter { + static public final int SIZE = 8; + + public final int reltype; + public final int addend; + public final int P; + + public DataType toDataType() { + StructureDataType dt = new StructureDataType(Datatypes.SCE_TYPES_CATPATH, "Prx1RefInfo", 0); + dt.setPackingEnabled(true); + try { + dt.addBitField(Datatypes.u32, 8, "reltype", "Relocation type (r_type)"); + dt.addBitField(Datatypes.u32, 24, "addend", "Relocation addend (r_addend)"); + } catch (InvalidDataTypeException e) { + throw new RuntimeException(e); + } + + dt.add(Datatypes.ptr, "P", "Virtual address where the reference lies"); + + + Utils.assertStructureSize(dt, SIZE); + return dt; + } + + public Prx1RefInfo(BinaryReader reader) throws IOException { + BitfieldReader bfr = new BitfieldReader(reader.readNextInt()); + + reltype = bfr.consume(8); + addend = bfr.consumeSEXT(24); + P = reader.readNextInt(); + + bfr.assertFullConsumption("Prx1RefInfo"); + Utils.assertBRSize("Prx1RefInfo", reader, SIZE); + } + +} \ No newline at end of file diff --git a/src/main/java/vitaloaderredux/arm_relocation/Prx2RefInfoForm1.java b/src/main/java/vitaloaderredux/arm_relocation/Prx2RefInfoForm1.java new file mode 100644 index 0000000..453b6b3 --- /dev/null +++ b/src/main/java/vitaloaderredux/arm_relocation/Prx2RefInfoForm1.java @@ -0,0 +1,57 @@ +package vitaloaderredux.arm_relocation; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.StructConverter; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.InvalidDataTypeException; +import ghidra.program.model.data.StructureDataType; +import vitaloaderredux.misc.BitfieldReader; +import vitaloaderredux.misc.Datatypes; +import vitaloaderredux.misc.Utils; + +//Form 1 reference information (used in reftables) +public class Prx2RefInfoForm1 implements StructConverter { + static public final int SIZE = 8; + + public final int segment; + public final int offset; + public final int addend; + public final int reltype; + + public DataType toDataType() { + StructureDataType dt = new StructureDataType(Datatypes.SCE_TYPES_CATPATH, "Prx2RefInfoForm1", 0); + dt.setPackingEnabled(true); + try { + dt.addBitField(Datatypes.u32, 4, "form", "Refinfo form (must be 1)"); + dt.addBitField(Datatypes.u32, 4, "segment", "Segment where reference lies"); + dt.addBitField(Datatypes.u32, 8, "reltype", "Relocation type (r_type)"); + dt.addBitField(Datatypes.u32, 16, "addend", "Relocation addend (r_addend)"); + } catch (InvalidDataTypeException e) { + throw new RuntimeException(e); + } + + dt.add(Datatypes.u32, "offset", "Offset where reference lies in segment (r_offset)"); + + Utils.assertStructureSize(dt, SIZE); + return dt; + } + + public Prx2RefInfoForm1(BinaryReader reader) throws IOException { + BitfieldReader bfr = new BitfieldReader(reader.readNextInt()); + + int form = bfr.consume(4); + if (form != 1) { + throw new IllegalArgumentException("Binary reader doesn't point to a form 1 RefInfo"); + } + + segment = bfr.consume(4); + reltype = bfr.consume(8); + addend = bfr.consumeSEXT(16); + offset = reader.readNextInt(); + + bfr.assertFullConsumption("Prx2RefInfoForm1"); + Utils.assertBRSize("Prx2RefInfoForm1", reader, SIZE); + } +} diff --git a/src/main/java/vitaloaderredux/arm_relocation/Prx2RefInfoForm2.java b/src/main/java/vitaloaderredux/arm_relocation/Prx2RefInfoForm2.java new file mode 100644 index 0000000..2e21563 --- /dev/null +++ b/src/main/java/vitaloaderredux/arm_relocation/Prx2RefInfoForm2.java @@ -0,0 +1,59 @@ +package vitaloaderredux.arm_relocation; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.StructConverter; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.InvalidDataTypeException; +import ghidra.program.model.data.StructureDataType; +import vitaloaderredux.misc.BitfieldReader; +import vitaloaderredux.misc.Datatypes; +import vitaloaderredux.misc.Utils; + +//Form 2 reference information (used in reftables) +public class Prx2RefInfoForm2 implements StructConverter { + static public final int SIZE = 12; + + public final int segment; + public final int offset; + public final int addend; + public final int reltype; + + public DataType toDataType() { + StructureDataType dt = new StructureDataType(Datatypes.SCE_TYPES_CATPATH, "Prx2RefInfoForm2", 0); + dt.setPackingEnabled(true); + try { + dt.addBitField(Datatypes.u32, 4, "form", "Refinfo form (must be 1)"); + dt.addBitField(Datatypes.u32, 4, "segment", "Segment where reference lies"); + dt.addBitField(Datatypes.u32, 8, "reltype", "Relocation type (r_type)"); + dt.addBitField(Datatypes.u32, 16, "", "Padding"); + } catch (InvalidDataTypeException e) { + throw new RuntimeException(e); + } + + dt.add(Datatypes.u32, "addend", "Relocation addend (r_addend)"); + dt.add(Datatypes.u32, "offset", "Offset where reference lies in segment (r_offset)"); + + Utils.assertStructureSize(dt, SIZE); + return dt; + } + + public Prx2RefInfoForm2(BinaryReader reader) throws IOException { + BitfieldReader bfr = new BitfieldReader(reader.readNextShort()); + reader.readNextShort(); //16 unused bits + + int form = bfr.consume(4); + if (form != 2) { + throw new IllegalArgumentException("Binary reader doesn't point to a form 2 RefInfo"); + } + + segment = bfr.consume(4); + reltype = bfr.consume(8); + addend = reader.readNextInt(); + offset = reader.readNextInt(); + + bfr.assertFullConsumption("Prx2RefInfoForm2"); + Utils.assertBRSize("Prx2RefInfoForm2", reader, SIZE); + } +} diff --git a/src/main/java/vitaloaderredux/database/LibraryToModuleDatabase.java b/src/main/java/vitaloaderredux/database/LibraryToModuleDatabase.java new file mode 100644 index 0000000..1f67d51 --- /dev/null +++ b/src/main/java/vitaloaderredux/database/LibraryToModuleDatabase.java @@ -0,0 +1,79 @@ +package vitaloaderredux.database; + +import java.io.FileReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.esotericsoftware.yamlbeans.YamlReader; + +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.Application; + +/** + * Class to manage a library name->module file name database. + * @author CreepNT + * + */ +public class LibraryToModuleDatabase { + /* + * LN->FN database is simply a YAML file with key:value pairs mapping + * file names to an array of library names. + * This object constructs the reverse mapping. + * + * Example database file + * + * sysmem.skprx: + * - SceSysmem + * - SceSysmemForKernel + * + * threadmgr.skprx: + * - SceThreadmgr + */ + + private final String DATABASE_NAME = "databases/LibraryToModuleDatabase.yaml"; + private final Map database; + + public LibraryToModuleDatabase(MessageLog logger) { + database = new HashMap(); + + try { + YamlReader yamlReader = new YamlReader(new FileReader(Application.getModuleDataFile(DATABASE_NAME).getFile(false))); + + @SuppressWarnings("rawtypes") //Root YAML node is always a generic Map - this is fine. + Map root = (Map)yamlReader.read(); + + for (Object key : root.keySet()) { + if (key instanceof String) { + Object value = root.get(key); + if (value instanceof List) { + String fileName = (String)key; + + @SuppressWarnings("rawtypes") //Already checked we have a List - this is fine. + List libraries = (List)value; + + for (Object elem : libraries) { + if (elem instanceof String) { + database.put((String)elem, fileName); + } else { + logger.appendMsg("Skipped element file name \"" + fileName + "\"'s array: value is not a String." ); + } + } + } else { + logger.appendMsg("Skipped YAML node " + key + ": value is not an array."); + } + } else { + logger.appendMsg("Skipped YAML node " + key + ": key is not a string."); + } + } + + } catch (Exception e) { + logger.appendMsg("Exception when reading the library to module file names database:"); + logger.appendException(e); + } + } + + public String lookup(String libraryName) { + return database.get(libraryName); + } +} diff --git a/src/main/java/vitaloaderredux/database/NIDDatabase.java b/src/main/java/vitaloaderredux/database/NIDDatabase.java new file mode 100644 index 0000000..27470e4 --- /dev/null +++ b/src/main/java/vitaloaderredux/database/NIDDatabase.java @@ -0,0 +1,108 @@ +package vitaloaderredux.database; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.HashMap; +import java.util.Map; + +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlReader; + +public class NIDDatabase { + private class PerLibraryDatabase { + private HashMap functions = new HashMap(); + private HashMap variables = new HashMap(); + + public void addFunction(int funcNID, String funcName) { + functions.put(funcNID, funcName); + } + + public void addVariable(int varNID, String varName) { + variables.put(varNID, varName); + } + + public String getFunctionName(int funcNID) { + return functions.get(funcNID); + } + + public String getVariableName(int varNID) { + return variables.get(varNID); + } + } + + private HashMap nameToLibraryMap = new HashMap(); + private PerLibraryDatabase findLibrary(String name) { + return nameToLibraryMap.get(name); + } + + public String getFunctionName(String libraryName, int functionNID) { + PerLibraryDatabase libdb = findLibrary(libraryName); + return (libdb == null) ? null : libdb.getFunctionName(functionNID); + } + + public String getVariableName(String libraryName, int variableNID) { + PerLibraryDatabase libdb = findLibrary(libraryName); + return (libdb == null) ? null : libdb.getVariableName(variableNID); + } + + /* Glue classes for YAML data + * + * Note that we have to use long/Long because YamlBeans will throw an + * exception when assigning a negative (>= 0x8000_0000) value into int/Integer. + * */ + + @SuppressWarnings("unused") + private static class YAML_Database { + public int version; + public String firmware; + public Map modules; + } + + @SuppressWarnings("unused") + private static class YAML_Module { + public long nid; public long fingerprint; + public Map libraries; + } + + @SuppressWarnings("unused") + private static class YAML_Library { + public long nid; + public boolean kernel; public boolean syscall; //Support both names, although VitaSDK uses only former... + + public Map functions; + public Map variables; + } + + public NIDDatabase(File databaseFile) { + try { + YamlReader reader = new YamlReader(new FileReader(databaseFile)); + YAML_Database database = reader.read(YAML_Database.class); + + for (Map.Entry moduleMapEntry: database.modules.entrySet()) { + YAML_Module module = moduleMapEntry.getValue(); + + for (Map.Entry libraryMapEntry: module.libraries.entrySet()) { + String libraryName = libraryMapEntry.getKey(); + YAML_Library library = libraryMapEntry.getValue(); + + PerLibraryDatabase libDB = new PerLibraryDatabase(); + if (library.functions != null) { + for (Map.Entry it: library.functions.entrySet()) + libDB.addFunction(it.getValue().intValue(), it.getKey()); + } + + if (library.variables != null) { + for (Map.Entry it: library.variables.entrySet()) + libDB.addVariable(it.getValue().intValue(), it.getKey()); + } + + nameToLibraryMap.put(libraryName, libDB); + } + } + } catch (FileNotFoundException | YamlException e) { + System.out.println("exception " + e); + } + } + +} diff --git a/src/main/java/vitaloaderredux/elf/ElfEhdr.java b/src/main/java/vitaloaderredux/elf/ElfEhdr.java new file mode 100644 index 0000000..2ca5b27 --- /dev/null +++ b/src/main/java/vitaloaderredux/elf/ElfEhdr.java @@ -0,0 +1,463 @@ +package vitaloaderredux.elf; + +import java.io.IOException; +import java.io.RandomAccessFile; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.StructConverter; +import ghidra.util.DataConverter; +import ghidra.util.LittleEndianDataConverter; +import ghidra.util.exception.DuplicateNameException; +import vitaloaderredux.misc.Datatypes; +import ghidra.app.util.bin.format.Writeable; +import ghidra.app.util.bin.format.elf.ElfConstants; +import ghidra.app.util.bin.format.elf.ElfProgramHeaderConstants; +import ghidra.program.model.data.ArrayDataType; +import ghidra.program.model.data.ByteDataType; +import ghidra.program.model.data.CharDataType; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.EnumDataType; +import ghidra.program.model.data.StructureDataType; +import ghidra.program.model.data.UnsignedIntegerDataType; +import ghidra.program.model.data.UnsignedShortDataType; + +public class ElfEhdr implements StructConverter, Writeable { + + //For e_type + static private final int ETSCEEXEC = 0xFE00, + ETSCERELEXEC = 0xFE04, ETSCEPSP2RELEXEC = 0xFFA5; + + static private final int EHDR_SIZE = 52, PHDR_SIZE = 0x20, SHDR_SIZE = 0x28; + + static public final short EM_CYGNUS_MEP = (short)0xF00D; + + public enum ElfType implements StructConverter { + ET_REL, ET_EXEC, ET_CORE, + ET_SCE_EXEC, ET_SCE_RELEXEC, ET_SCE_PSP2RELEXEC; + + static public ElfType fromInteger(int itype) { + itype &= 0xFFFF; + if (itype == ElfConstants.ET_REL) return ET_REL; + if (itype == ElfConstants.ET_EXEC) return ET_EXEC; + if (itype == ElfConstants.ET_CORE) return ET_CORE; + if (itype == ETSCEEXEC) return ET_SCE_EXEC; + if (itype == ETSCERELEXEC) return ET_SCE_RELEXEC; + if (itype == ETSCEPSP2RELEXEC) return ET_SCE_PSP2RELEXEC; + throw new IllegalArgumentException("Unreachable"); + } + + public String description() { + switch (this) { + case ET_REL: return "Relocatable executable"; + case ET_EXEC: return "Fixed executable"; + case ET_CORE: return "Core file"; + case ET_SCE_EXEC: return "SCE Executable"; + case ET_SCE_RELEXEC: return "SCE Relocatable Executable"; + case ET_SCE_PSP2RELEXEC: return "PSP2 Relocatable Executable"; + default: throw new IllegalArgumentException("Unreachable"); + } + } + + public boolean relocatable() { + return (this == ET_REL) || + (this == ET_SCE_RELEXEC) || + (this == ET_SCE_PSP2RELEXEC); + } + + static public DataType getDataType() { + if (ELFTYPE_ENUM == null) { + ELFTYPE_ENUM = new EnumDataType(Datatypes.ELF_CATPATH, "Elf32_EType", UnsignedShortDataType.dataType.getLength()); + ELFTYPE_ENUM.add("ET_REL", ElfConstants.ET_REL); + ELFTYPE_ENUM.add("ET_EXEC", ElfConstants.ET_EXEC); + ELFTYPE_ENUM.add("ET_CORE", ElfConstants.ET_CORE); + ELFTYPE_ENUM.add("ET_SCE_EXEC", ETSCEEXEC); + ELFTYPE_ENUM.add("ET_SCE_RELEXEC", ETSCERELEXEC); + ELFTYPE_ENUM.add("ET_SCE_PSP2RELEXEC", ETSCEPSP2RELEXEC); + } + return ELFTYPE_ENUM; + } + static EnumDataType ELFTYPE_ENUM = null; + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + return getDataType(); + } + } + + private static EnumDataType e_machine_enum = null; + private static DataType GetEMachineDataType() { + if (e_machine_enum == null) { + e_machine_enum = new EnumDataType(Datatypes.ELF_CATPATH, "Elf32_EMachine", 2); + e_machine_enum.add("EM_ARM", ElfConstants.EM_ARM); + e_machine_enum.add("EM_CYGNUS_MEP", EM_CYGNUS_MEP); + } + return e_machine_enum; + } + + public final byte ei_osabi; + public final byte ei_abiversion; + public final byte ei_pad[]; + + public final short e_machine; + + public final short e_type; + public final ElfType execType; + public final long e_entry; + public final long e_phoff; + public final long e_shoff; + public final long e_flags; +// public final int e_ehsize; +// public final int e_phentsize; + public final int e_phnum; + public final int e_shentsize; + public final int e_shnum; + public final int e_shstrndx; + + public ElfPhdr[] programHeaders = null; + public ElfShdr[] sectionHeaders = null; + + public final ByteProvider byteProvider; + + /** + * + * @param provider + * @throws IOException when a BinaryReader error occurs. + * @throws IllegalArgumentException when the ELF header is invalid. + */ + public ElfEhdr(ByteProvider provider) throws IOException { + byteProvider = provider; + BinaryReader reader = new BinaryReader(byteProvider, true); + + byte magic0 = reader.readNextByte(); + String magic123 = reader.readNextAsciiString(ElfConstants.MAGIC_STR_LEN); + if (magic0 != ElfConstants.MAGIC_NUM || !magic123.equals(ElfConstants.MAGIC_STR)) + { + throw new MalformedElfException("No ELF magic"); + } + + final byte ei_class = reader.readNextByte(); + if (ei_class != ElfConstants.ELF_CLASS_32) { + throw new UnsupportedElfException("Unexpected EI_CLASS"); + } + + final byte ei_data = reader.readNextByte(); + if (ei_data != ElfConstants.ELF_DATA_LE) { + throw new UnsupportedElfException("Unexpected EI_DATA"); + } + + final byte ei_version = reader.readNextByte(); + if (ei_version != ElfConstants.EV_CURRENT) { + throw new UnsupportedElfException("Unexpected EI_VERSION"); + } + + ei_osabi = reader.readNextByte(); + ei_abiversion = reader.readNextByte(); + ei_pad = reader.readNextByteArray(7); + + e_type = reader.readNextShort(); + execType = ElfType.fromInteger(e_type); + + e_machine = reader.readNextShort(); + if (e_machine != ElfConstants.EM_ARM && e_machine != EM_CYGNUS_MEP) { + throw new UnsupportedElfException("Unexpected e_machine"); + } + + long e_version = reader.readNextUnsignedInt(); + if (e_version != ElfConstants.EV_CURRENT) { + throw new UnsupportedElfException("Unexpected e_version"); + } + + e_entry = reader.readNextUnsignedInt(); + + e_phoff = reader.readNextUnsignedInt(); + if (e_phoff > provider.length()) { + throw new MalformedElfException("e_phoff " + e_phoff + " is too big (max = " + provider.length() + ")"); + } + + e_shoff = reader.readNextUnsignedInt(); + if (e_shoff > provider.length()) { + throw new MalformedElfException("e_shoff " + e_shoff + " is too big (max = " + provider.length() + ")"); + } + + e_flags = reader.readNextUnsignedInt(); + + int e_ehsize = reader.readNextUnsignedShort(); + if (e_ehsize != EHDR_SIZE) { + throw new UnsupportedElfException("Unexpected e_ehsize " + e_ehsize); + } + + int e_phentsize = reader.readNextUnsignedShort(); + if (e_phentsize != PHDR_SIZE) { + throw new UnsupportedElfException("Unexpected e_phentsize " + e_phentsize); + } + + e_phnum = reader.readNextUnsignedShort(); + + e_shentsize = reader.readNextUnsignedShort(); + if (e_shentsize != 0 && e_shentsize != SHDR_SIZE) { //e_shentsize is 0 when sections are stripped + throw new UnsupportedElfException("Unexpected e_shentsize " + e_shentsize); + } + + e_shnum = reader.readNextUnsignedShort(); + e_shstrndx = reader.readNextUnsignedShort(); + if (e_shnum > 0 && e_shstrndx >= e_shnum) { + throw new MalformedElfException(String.format("e_shstrndx (%d) >= e_shnum (%d)", e_shstrndx, e_shnum)); + } + + parseProgramHeaders(); + + try { + parseSegmentHeaders(); + parseSegmentNames(); + } catch (IOException e) { + //Silently swallow... sections aren't very important. + } + } + + public int getImageBase() { + if (e_phnum == 0) { + return 0; + } + + //Must use long to ensure comparisions work as expected. + long base = 0x81000000; + + //Find smallest p_vaddr + for (ElfPhdr Phdr: programHeaders) { + if (Phdr.p_type == ElfProgramHeaderConstants.PT_LOAD && Phdr.p_vaddr < base) { + base = Phdr.p_vaddr; + } + } + return (int)(base & 0xFFFFFFFFl); + } + + public class ModInfoLocation { + public final int segmentIndex; + public final int segmentOffset; + public final int fileOffset; + + public ModInfoLocation(int segmentIdx, int segmentFileOffset, int offsetInSegment) { + segmentIndex = segmentIdx; + segmentOffset = offsetInSegment; + fileOffset = segmentFileOffset + offsetInSegment; + } + } + + //This method is only valid for ARM ELF. + public ModInfoLocation getModuleInfoLocation() throws MalformedElfException, UnsupportedElfException { + if (e_machine != ElfConstants.EM_ARM) { + throw new UnsupportedElfException("Only ARM ELFs have a SceModuleInfo"); + } + + //If the ELF has sections, try to find .sceModuleInfo.rodata + + //TODO: is this correct? + //TODO: find segment metadata by section + /* + if (e_shnum > 0) { + for (ElfShdr sHdr: sectionHeaders) { + if (sHdr.name.equals(".sceModuleInfo.rodata")) { + return null; + } + } + } + */ + + //Legacy ELF format (Prx1): module info FILE offset is stored in first segment's p_paddr. + if (isPrx1ELF()) { + ElfPhdr Phdr = programHeaders[0]; + int modInfoOffset = (int)(Phdr.p_paddr - Phdr.p_offset); + if (modInfoOffset < 0) { + throw new MalformedElfException("Prx1 ELF: bad SceModuleInfo offset"); + } + + return new ModInfoLocation(0, (int)Phdr.p_offset, modInfoOffset); + } + + //How does Modulemgr find the SceModuleInfo? Why reinvent the wheel? + // + //Answer: in 4.00, the following method is always used: + // SceModuleInfo segment:offset = (e_entry<31:30>:e_entry<29:0>) + // + //However, 4.00 only supports infoversion 6... + // + //RE of older firmwares is required, but alas, I think + //we'll have to resort to hacks to have universal support. + + //For some ET_SCE_EXEC, module info offset is stored in the segment's p_paddr. + //TODO: is this true?! + for (int i = 0; i < e_phnum; i++) { + ElfPhdr Phdr = programHeaders[i]; + //Check paddr is non-NULL and is a valid offset + if (Phdr.p_paddr != 0 && (Phdr.p_paddr < Phdr.p_filesz)) { + //TODO: is this correct? + return new ModInfoLocation(i, (int)Phdr.p_offset, (int)Phdr.p_paddr); + } + } + + + //For ET_SCE_RELEXEC and some ET_SCE_EXEC, the Ehdr's e_entry stores the segment:offset to the SceModuleInfo. + //Top two bits = segment index, bottom 30 bits = offset in segment. + byte segNdx = (byte)((e_entry >> 30) & 0x3); + int segOffset = (int)(e_entry & 0x3FFFFFFF); + + if (segNdx > e_phnum) { + throw new MalformedElfException(String.format("segNdx (%d) > e_phnum (%d)", segNdx, e_phnum)); + } + + ElfPhdr segHdr = programHeaders[segNdx]; + if (segHdr.p_type != ElfProgramHeaderConstants.PT_LOAD) { + throw new MalformedElfException(String.format("Illegal segment type 0x%X for SceModuleInfo", segHdr.p_type)); + } + + if (segOffset > segHdr.p_filesz) { + throw new MalformedElfException(String.format("segOffset (%d) > seg.p_filesz (%d)", segOffset, segHdr.p_filesz)); + } + + //TODO: is this correct? + return new ModInfoLocation(segNdx, (int)segHdr.p_offset, segOffset); + } + + /** + * Get the offset in file of the SceModuleInfo structure. + * @return File offset of SceModuleInfo, or -1 if not found + * @throws UnsupportedElfException + * @throws MalformedElfException + */ + public long getModuleInfoFileOffset() throws MalformedElfException, UnsupportedElfException { + ModInfoLocation loc = getModuleInfoLocation(); + return loc.fileOffset; + } + + private BinaryReader makeReaderForOffset(long offset) { + return new BinaryReader(byteProvider, LittleEndianDataConverter.INSTANCE, offset); + } + + private void parseProgramHeaders() throws IOException { + if (e_phnum > 0) { + programHeaders = new ElfPhdr[e_phnum]; + for (int i = 0; i < e_phnum; i++) { + BinaryReader hdrReader = makeReaderForOffset(e_phoff + i * PHDR_SIZE); + programHeaders[i] = new ElfPhdr(hdrReader); + } + } + } + + private void parseSegmentHeaders() throws IOException { + if (e_shnum > 0) { + sectionHeaders = new ElfShdr[e_shnum]; + for (int i = 0; i < e_shnum; i++) { + BinaryReader hdrReader = makeReaderForOffset(e_shoff + i * e_shentsize); + sectionHeaders[i] = new ElfShdr(hdrReader); + } + } + } + + private void parseSegmentNames() throws IOException { + if (e_shnum <= 0) + return; + + if (e_shstrndx >= 0) { + ElfShdr namesSection = sectionHeaders[e_shstrndx]; + BinaryReader namesReader = makeReaderForOffset(namesSection.sh_offset); + + for (int i = 0; i < e_shnum; i++) { + ElfShdr section = sectionHeaders[i]; + String name = namesReader.readAsciiString(section.sh_name); + if (!name.equals("")) { + section.name = name; + } else { + section.name = String.format("SECTION_%d", i); + } + } + } else { + for (int i = 0; i < e_shnum; i++) { + sectionHeaders[i].name = String.format("SECTION_%d", i); + } + } + + } + + public static DataType getDataType() { + DataType e_typeDatatype = ElfType.getDataType(); + DataType u8 = ByteDataType.dataType; + DataType u16 = UnsignedShortDataType.dataType; + DataType u32 = UnsignedIntegerDataType.dataType; + DataType char8 = CharDataType.dataType; + + StructureDataType dt = new StructureDataType(Datatypes.ELF_CATPATH, "Elf32_Ehdr", 0); + dt.add(u8, "ei_magic", "0x7F"); + dt.add(new ArrayDataType(char8, 3, char8.getLength()), "ei_magic_str", "ELF"); + dt.add(u8, "ei_class", "Bitness class"); + dt.add(u8, "ei_data", "Data ordering"); + dt.add(u8, "ei_version", "ELF version"); + dt.add(u8, "ei_osabi", "OS ABI"); + dt.add(u8, "ei_abiversion", "ABI Version"); + dt.add(new ArrayDataType(u8, 7, u8.getLength()), "ei_pad", "Padding"); + dt.add(e_typeDatatype, "e_type", "ELF file type"); + dt.add(GetEMachineDataType(), "e_machine", "ELF target machine"); + dt.add(u32, "e_version", "ELF Ehdr version"); + dt.add(u32, "e_entry", "Entrypoint"); + dt.add(u32, "e_phoff", "Offset to Program headers"); + dt.add(u32, "e_shoff", "Offset to Section headers"); + dt.add(u32, "e_flags", null); + dt.add(u16, "e_ehsize", "Size of ELF header"); + dt.add(u16, "e_phentsize", "Size of program header"); + dt.add(u16, "e_phnum", "Number of program headers"); + dt.add(u16, "e_shentsize", "Size of section header"); + dt.add(u16, "e_shnum", "Number of section headers"); + dt.add(u16, "e_shstrndx", "Index of string section"); + return dt; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + return getDataType(); + } + + @Override + public void write(RandomAccessFile raf, DataConverter dc) throws IOException { + raf.write((byte)0x7F); + raf.write((byte)'E'); + raf.write((byte)'L'); + raf.write((byte)'F'); + raf.write(ElfConstants.ELF_CLASS_32); + raf.write(ElfConstants.ELF_DATA_LE); + raf.write(ElfConstants.EV_CURRENT); + raf.write(ei_osabi); + raf.write(ei_abiversion); + raf.write(ei_pad); + + DataConverter LEDC = LittleEndianDataConverter.INSTANCE; + + //Multi-byte fields must be converted to be written in little endian + raf.write(LEDC.getBytes(e_type)); + raf.write(LEDC.getBytes(e_machine)); + raf.write(LEDC.getBytes((int)ElfConstants.EV_CURRENT)); + raf.write(LEDC.getBytes((int)e_entry)); + raf.write(LEDC.getBytes((int)e_phoff)); + raf.write(LEDC.getBytes((int)e_shoff)); + raf.write(LEDC.getBytes((int)e_flags)); + raf.write(LEDC.getBytes((short)EHDR_SIZE)); + raf.write(LEDC.getBytes((short)PHDR_SIZE)); + raf.write(LEDC.getBytes((short)e_phnum)); + raf.write(LEDC.getBytes((short)e_shentsize)); + raf.write(LEDC.getBytes((short)e_shnum)); + raf.write(LEDC.getBytes((short)e_shstrndx)); + } + + public boolean isPrx1ELF() { + if (e_type == (short)ETSCEPSP2RELEXEC) + return true; + + if (e_type == ElfConstants.ET_EXEC) + return true; + + if (e_type == ElfConstants.ET_REL) + return true; + + //Either ET_SCE_EXEC, ET_SCE_RELEXEC or ET_CORE (which are generated by faps-coredump) + return false; + } +} diff --git a/src/main/java/vitaloaderredux/elf/ElfPhdr.java b/src/main/java/vitaloaderredux/elf/ElfPhdr.java new file mode 100644 index 0000000..cbb677b --- /dev/null +++ b/src/main/java/vitaloaderredux/elf/ElfPhdr.java @@ -0,0 +1,89 @@ +package vitaloaderredux.elf; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.EnumDataType; +import ghidra.program.model.data.StructureDataType; +import ghidra.program.model.data.UnsignedIntegerDataType; +import vitaloaderredux.misc.Datatypes; +import ghidra.app.util.bin.format.elf.ElfProgramHeaderConstants; + +public class ElfPhdr { + //TODO: verify only those exist + static public final int PT_SCE_RELA = 0x60000000; + static public final int PT_SCE_COMMENT = 0x6FFFFF00; + static public final int PT_SCE_VERSION = 0x6FFFFF01; + static public final int PT_ARM_UNWIND = 0x70000001; //Also called PT_ARM_EXIDX + static public final int PT_SCE_PPURELA = 0x700000A4; //Used for relocations in old fw + + /** + * @return DataType for the p_type field + */ + static public DataType getPhdrTypeDataType() { + EnumDataType dt = new EnumDataType(Datatypes.ELF_CATPATH, "Elf32_PhdrType", 4); + dt.add("PT_NULL", ElfProgramHeaderConstants.PT_NULL); + dt.add("PT_LOAD", ElfProgramHeaderConstants.PT_LOAD); + dt.add("PT_NOTE", ElfProgramHeaderConstants.PT_NOTE); + dt.add("PT_SCE_RELA", PT_SCE_RELA, "Relocation segment"); + dt.add("PT_SCE_COMMENT", PT_SCE_COMMENT); + dt.add("PT_SCE_VERSION", PT_SCE_VERSION); + dt.add("PT_ARM_UNWIND", PT_ARM_UNWIND, "Exception unwind tables segment"); + dt.add("PT_SCE_PPURELA", PT_SCE_PPURELA, "Legacy relocation segment"); + return dt; + } + + /** + * @return DataType for the p_flags field + */ + static public DataType getPhdrFlagsDataType() { + EnumDataType dt = new EnumDataType(Datatypes.ELF_CATPATH, "Elf32_PhdrFlags", 4); + dt.add("PF_X", 1); + dt.add("PF_W", 2); + dt.add("PF_R", 4); + return dt; + } + + static public DataType getDataType() { + if (PHDR_DATATYPE == null) { + DataType uint = UnsignedIntegerDataType.dataType; + + //TODO: make p_flags a EnumDataType as well + PHDR_DATATYPE = new StructureDataType(Datatypes.ELF_CATPATH, "Elf32_Phdr", 0); + PHDR_DATATYPE.add(getPhdrTypeDataType(), "p_type", ""); + PHDR_DATATYPE.add(uint, "p_offset", ""); + PHDR_DATATYPE.add(uint, "p_vaddr", ""); + PHDR_DATATYPE.add(uint, "p_vaddr", ""); + PHDR_DATATYPE.add(uint, "p_filesz", ""); + PHDR_DATATYPE.add(uint, "p_memsz", ""); + PHDR_DATATYPE.add(getPhdrFlagsDataType(), "p_flags", ""); + PHDR_DATATYPE.add(uint, "p_align", ""); + } + return PHDR_DATATYPE; + } + static StructureDataType PHDR_DATATYPE = null; + + public final long p_type; + public final long p_offset; + public final long p_vaddr; + public final long p_paddr; + public final long p_filesz; + public final long p_memsz; + public final long p_flags; + public final long p_align; + + //User data. Can be used to store an object associated to the segment. + public Object userData; + + public ElfPhdr(BinaryReader reader) throws IOException { + p_type = reader.readNextUnsignedInt(); + p_offset = reader.readNextUnsignedInt(); + p_vaddr = reader.readNextUnsignedInt(); + p_paddr = reader.readNextUnsignedInt(); + p_filesz = reader.readNextUnsignedInt(); + p_memsz = reader.readNextUnsignedInt(); + p_flags = reader.readNextUnsignedInt(); + p_align = reader.readNextUnsignedInt(); + } +} diff --git a/src/main/java/vitaloaderredux/elf/ElfShdr.java b/src/main/java/vitaloaderredux/elf/ElfShdr.java new file mode 100644 index 0000000..86223af --- /dev/null +++ b/src/main/java/vitaloaderredux/elf/ElfShdr.java @@ -0,0 +1,115 @@ +package vitaloaderredux.elf; + + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.elf.ElfSectionHeaderConstants; +import ghidra.program.model.data.CategoryPath; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.EnumDataType; +import ghidra.program.model.data.StructureDataType; +import ghidra.program.model.data.UnsignedIntegerDataType; + +import vitaloaderredux.misc.Datatypes; + +public class ElfShdr { + static public final CategoryPath ELF_CATPATH = Datatypes.ELF_CATPATH; + + /** + * @return DataType for the sh_type field + */ + static public DataType getShdrTypeDataType() { + EnumDataType dt = new EnumDataType(ELF_CATPATH, "Elf32_ShdrType", 4); + dt.add("SHT_NULL", ElfSectionHeaderConstants.SHT_NULL, "Unused entry"); + dt.add("SHT_PROGBITS", ElfSectionHeaderConstants.SHT_PROGBITS, "Program data"); + dt.add("SHT_SYMTAB", ElfSectionHeaderConstants.SHT_SYMTAB, "Symbol table"); + dt.add("SHT_STRTAB", ElfSectionHeaderConstants.SHT_STRTAB, "String table"); + dt.add("SHT_RELA", ElfSectionHeaderConstants.SHT_RELA, "Relocation entries with addends"); + dt.add("SHT_HASH", ElfSectionHeaderConstants.SHT_HASH, "Symbol hash table"); + dt.add("SHT_DYNAMIC", ElfSectionHeaderConstants.SHT_DYNAMIC, "Dynamic linking information"); + dt.add("SHT_NOTE", ElfSectionHeaderConstants.SHT_NOTE, "Notes"); + dt.add("SHT_NOBITS", ElfSectionHeaderConstants.SHT_NOBITS, "Program space with no data (bss)"); + dt.add("SHT_REL", ElfSectionHeaderConstants.SHT_REL, "Relocation entries, no addends"); + dt.add("SHT_SHLIB", ElfSectionHeaderConstants.SHT_SHLIB, "Reserved"); + dt.add("SHT_DYNSYM", ElfSectionHeaderConstants.SHT_DYNSYM, "Dynamic linker symbol table"); + dt.add("SHT_INIT_ARRAY", ElfSectionHeaderConstants.SHT_INIT_ARRAY, "Array of constructors"); + dt.add("SHT_FINI_ARRAY", ElfSectionHeaderConstants.SHT_FINI_ARRAY, "Array of destructors"); + dt.add("SHT_PREINIT_ARRAY", ElfSectionHeaderConstants.SHT_PREINIT_ARRAY, "Array of pre-constructors"); + dt.add("SHT_GROUP", ElfSectionHeaderConstants.SHT_GROUP, "Section group"); + dt.add("SHT_SYMTAB_SHNDX", ElfSectionHeaderConstants.SHT_SYMTAB_SHNDX, "Extended section indices"); + //TODO: add SCE section header types + return dt; + } + + /** + * @return DataType for the sh_flags field + */ + static public DataType getShdrFlagsDataType() { + EnumDataType dt = new EnumDataType(Datatypes.ELF_CATPATH, "Elf32_ShdrFlags", 4); + dt.add("SHF_WRITE", ElfSectionHeaderConstants.SHF_WRITE, "Writable section"); + dt.add("SHF_ALLOC", ElfSectionHeaderConstants.SHF_ALLOC, "Section occupies memory during execution"); + dt.add("SHF_EXECINSTR", ElfSectionHeaderConstants.SHF_EXECINSTR, "Executable section"); + + dt.add("SHF_MERGE", ElfSectionHeaderConstants.SHF_MERGE, "Section might be merged"); + dt.add("SHF_STRINGS", ElfSectionHeaderConstants.SHF_STRINGS, "Section contains NUL-terminated strings"); + dt.add("SHF_INFO_LINK", ElfSectionHeaderConstants.SHF_INFO_LINK, "sh_info holds an SHT index"); + dt.add("SHF_LINK_ORDER", ElfSectionHeaderConstants.SHF_LINK_ORDER, "Section order must be preserved after combining"); + dt.add("SHF_OS_NONCONFORMING", ElfSectionHeaderConstants.SHF_OS_NONCONFORMING, "OS-specific handling is required"); + dt.add("SHF_GROUP", ElfSectionHeaderConstants.SHF_GROUP, "Section is member of a group"); + dt.add("SHF_TLS", ElfSectionHeaderConstants.SHF_TLS, "Sections holds thread-local data"); + dt.add("SHF_COMPRESSED", ElfSectionHeaderConstants.SHF_COMPRESSED, "Section is compressed"); + return dt; + } + + static public DataType getDataType() { + if (SHDR_DATATYPE == null) { + DataType uint = UnsignedIntegerDataType.dataType; + + //TODO: make sh_flags a EnumDataType as well + SHDR_DATATYPE = new StructureDataType(ELF_CATPATH, "Elf32_Shdr", 0); + SHDR_DATATYPE.add(uint, "sh_name", ""); + SHDR_DATATYPE.add(getShdrTypeDataType(), "sh_type", ""); + SHDR_DATATYPE.add(getShdrFlagsDataType(), "sh_flags", ""); + SHDR_DATATYPE.add(uint, "sh_addr", ""); + SHDR_DATATYPE.add(uint, "sh_offset", ""); + SHDR_DATATYPE.add(uint, "sh_size", ""); + SHDR_DATATYPE.add(uint, "sh_link", ""); + SHDR_DATATYPE.add(uint, "sh_info", ""); + SHDR_DATATYPE.add(uint, "sh_addralign", ""); + SHDR_DATATYPE.add(uint, "sh_entsize", ""); + } + return SHDR_DATATYPE; + } + static StructureDataType SHDR_DATATYPE = null; + + public final long sh_name; //offset to name in .shstrtab section + public final long sh_type; //section type + public final long sh_flags; //section attributes + public final long sh_addr; //section virtual address + public final long sh_offset; //section file offset + public final long sh_size; //section size in file + public final long sh_link; + public final long sh_info; + public final long sh_addralign; + public final long sh_entsize; + + //Section name. Must be filled up by class user. + public String name = null; + + //User data. Can be used by class user to store an object associated to the section. + public Object userData; + + public ElfShdr(BinaryReader reader) throws IOException { + sh_name = reader.readNextUnsignedInt(); + sh_type = reader.readNextUnsignedInt(); + sh_flags = reader.readNextUnsignedInt(); + sh_addr = reader.readNextUnsignedInt(); + sh_offset = reader.readNextUnsignedInt(); + sh_size = reader.readNextUnsignedInt(); + sh_link = reader.readNextUnsignedInt(); + sh_info = reader.readNextUnsignedInt(); + sh_addralign = reader.readNextUnsignedInt(); + sh_entsize = reader.readNextUnsignedInt(); + } +} \ No newline at end of file diff --git a/src/main/java/vitaloaderredux/elf/MalformedElfException.java b/src/main/java/vitaloaderredux/elf/MalformedElfException.java new file mode 100644 index 0000000..5b1aa1b --- /dev/null +++ b/src/main/java/vitaloaderredux/elf/MalformedElfException.java @@ -0,0 +1,15 @@ +package vitaloaderredux.elf; + +import ghidra.app.util.opinion.LoadException; + +public class MalformedElfException extends LoadException { + private static final long serialVersionUID = 6850465584723476757L; + + public MalformedElfException(String message) { + super("Malformed ELF: " + message); + } + + public MalformedElfException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/vitaloaderredux/elf/UnsupportedElfException.java b/src/main/java/vitaloaderredux/elf/UnsupportedElfException.java new file mode 100644 index 0000000..1c26b0f --- /dev/null +++ b/src/main/java/vitaloaderredux/elf/UnsupportedElfException.java @@ -0,0 +1,15 @@ +package vitaloaderredux.elf; + +import ghidra.app.util.opinion.LoadException; + +public class UnsupportedElfException extends LoadException { + private static final long serialVersionUID = -8975223096035645703L; + + public UnsupportedElfException(String message) { + super("Unsupported ELF: " + message); + } + + public UnsupportedElfException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/vitaloaderredux/loader/ArmElfPrxLoader.java b/src/main/java/vitaloaderredux/loader/ArmElfPrxLoader.java new file mode 100644 index 0000000..b83cadb --- /dev/null +++ b/src/main/java/vitaloaderredux/loader/ArmElfPrxLoader.java @@ -0,0 +1,652 @@ +package vitaloaderredux.loader; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +import ghidra.app.util.MemoryBlockUtils; +import ghidra.app.util.Option; +import ghidra.app.util.OptionUtils; +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.elf.ElfConstants; +import ghidra.app.util.bin.format.elf.ElfProgramHeaderConstants; +import ghidra.app.util.importer.MessageLog; +import ghidra.app.util.opinion.AbstractLibrarySupportLoader; +import ghidra.app.util.opinion.LoadException; +import ghidra.app.util.opinion.LoadSpec; +import ghidra.app.util.opinion.LoaderTier; +import ghidra.framework.model.DomainObject; +import ghidra.framework.options.Options; +import ghidra.framework.store.LockException; +import ghidra.program.database.mem.FileBytes; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOutOfBoundsException; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.data.ByteDataType; +import ghidra.program.model.data.DataType; + +import ghidra.program.model.lang.LanguageCompilerSpecPair; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.program.model.util.ObjectPropertyMap; +import ghidra.program.model.util.VoidPropertyMap; +import ghidra.util.MonitoredInputStream; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.DuplicateNameException; +import ghidra.util.task.TaskMonitor; + +import vitaloaderredux.elf.ElfEhdr; +import vitaloaderredux.elf.ElfEhdr.ModInfoLocation; +import vitaloaderredux.elf.ElfPhdr; +import vitaloaderredux.elf.ElfShdr; +import vitaloaderredux.elf.MalformedElfException; +import vitaloaderredux.elf.UnsupportedElfException; + +import vitaloaderredux.misc.Datatypes; +import vitaloaderredux.misc.HexOption; +import vitaloaderredux.misc.ImportExportProperty; +import vitaloaderredux.scetypes.ILibent; +import vitaloaderredux.scetypes.ILibstub; +import vitaloaderredux.scetypes.SELFConstants; +import vitaloaderredux.scetypes.SceLibent_0x1C; +import vitaloaderredux.scetypes.SceLibent_0x20; +import vitaloaderredux.scetypes.SceLibstub_0x24; +import vitaloaderredux.scetypes.SceLibstub_0x2C; +import vitaloaderredux.scetypes.SceLibstub_0x34; +import vitaloaderredux.scetypes.SceModuleInfo; + +/** + * + * @author CreepNT + * + * @implNote This parser may be less strict than the SceKernelModulemgr (S)ELF parser. + * Thus, getting a module to load properly with this loader is not a guarantee that + * it will be accepted by SceKernelModulemgr. + */ +public class ArmElfPrxLoader extends AbstractLibrarySupportLoader { + + private static final LanguageCompilerSpecPair LANGUAGE = + new LanguageCompilerSpecPair("ARM:LE:32:v7", "default"); + + public static final String MODULE_INFO_LOCATOR_USRPROPNAME = "[VLR:ARM] SceModuleInfo locator"; + public static final String IMPORTEXPORT_LOCATOR_USRPROPNAME = "[VLR:ARM] Imports/Exports locator"; + + private static final String FILE_FORMAT_NAME = "ARM ELF-PRX for PlayStation®Vita"; + + private static final String VARIMPORT_BLOCK_ADDRESS_OPTNAME = "Variable Imports Block Base"; + private static final String VARIMPORT_BLOCK_SIZE_OPTNAME = "Variable Imports Block Size"; + private static final String VARIMPORT_SIZE_OPTNAME = "Imported Variables Size"; + + private static final int NOACCESS = 0; + private static final int PF_R = ElfProgramHeaderConstants.PF_R; + private static final int PF_W = ElfProgramHeaderConstants.PF_W; + private static final int PF_X = ElfProgramHeaderConstants.PF_X; + + private static final Address OTHERAS_START = AddressSpace.OTHER_SPACE.getAddress(0); + + @Override + public String getName() { + return FILE_FORMAT_NAME; + } + + @Override + public Collection findSupportedLoadSpecs(ByteProvider provider) throws IOException { + List loadSpecs = new ArrayList<>(); + + ElfEhdr ehdr; + + try { + ehdr = new ElfEhdr(provider); + } catch (IOException | IllegalArgumentException e) { + return loadSpecs; + } + + if (ehdr.e_machine != ElfConstants.EM_ARM) + return loadSpecs; + + //Tiny sanity check: modules, check that SceModuleInfo.name field is valid. + long modInfoOffset; + try { + modInfoOffset = ehdr.getModuleInfoFileOffset(); + } catch (MalformedElfException e) { + System.err.println(e.getMessage()); + throw e; + } + if (modInfoOffset < 0) { + return loadSpecs; + } + + BinaryReader modInfoReader = new BinaryReader(provider, true); + modInfoReader.setPointerIndex(modInfoOffset + 4); + if (!SceModuleInfo.verifyModuleInfoName(modInfoReader, true)) { + return loadSpecs; + } + + loadSpecs.add(new LoadSpec(this, /* use a sensical default base address */ 0x81000000, LANGUAGE, true)); + return loadSpecs; + } + + @Override + public List