diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..0086358d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: true diff --git a/.github/ISSUE_TEMPLATE/feature_suggestion.yml b/.github/ISSUE_TEMPLATE/feature_suggestion.yml new file mode 100644 index 00000000..c67448df --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_suggestion.yml @@ -0,0 +1,45 @@ +name: Feature suggestion +description: Suggest a new feature (cool new ideas are interesting to hear about) +title: "[Suggestion]: " +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thank you for suggesting a new feature! + - type: textarea + id: the-feature + attributes: + label: Explain the feature + description: What does this new feature do? How it would be done? + placeholder: | + - Print capybara ascii art on each run + - Introduce achievements + - Play the Oof sound on errors + validations: + required: true + - type: textarea + id: advantage + attributes: + label: Advantage + description: What are the pros of this new feature? + placeholder: | + - XYZ is a common case. + - The produced assembly would be more correct because... + - Would fix Windows builds. + - type: textarea + id: disadvantage + attributes: + label: Disadvantage + description: What could be any drawback of the new feature? + placeholder: | + - Slower runtime. + - Harder to debug. + - Would fix Windows builds. + - type: textarea + id: example + attributes: + label: Example(s) + description: Include examples on how this feature would look like or related info. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/frug_report.yml b/.github/ISSUE_TEMPLATE/frug_report.yml new file mode 100644 index 00000000..4102f54a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/frug_report.yml @@ -0,0 +1,78 @@ +name: Frug Report +description: File a frug report (because something isn't working as it should) +title: "[Frug]: " +labels: ["frug"] +body: + - type: markdown + attributes: + value: | + Thank you for filing a frug report! + - type: textarea + id: what-happened + attributes: + label: Explain the problem. + description: What happened? What did you expect to happen? + placeholder: What went wrong? + validations: + required: true + - type: textarea + id: reproducer + attributes: + label: Reproducer + description: Please provide instructions to reproduce the problem. + placeholder: | + Use the following file (attach it please) and run IJK with parameters ABC. + Clone the repo on github.com/example/example and do XYZ + validations: + required: true + - type: dropdown + id: instruction-set-flavor + attributes: + label: Is this issue related to an specific MIPS extension? + description: rabbitizer supports many MIPS flavors, which one were you using specifically? + options: + - I don't know / Does not apply + - Normal MIPS CPU (without any extensions) + - RSP (Reality Signal Processor, the Nintendo 64 vector coprocessor) + - R3000 GTE (Geometry Transformation Engine, a Sony PlayStation 1 extension) + - R4000 ALLEGREX (A Sony PlayStation Portable extension) + - R5900 EE (Emotion Engine, A Sony PlayStation 2 extension) + default: 0 + validations: + required: true + - type: input + id: rabbitizer-version + attributes: + label: rabbitizer version + description: What version of rabbitizer are you running? (`pip show rabbitizer` if installed via `pip` or check `include/common/RabbitizerVersion.h` if used as a C or C++ library) + validations: + required: true + - type: input + id: splat-spimdisasm-version + attributes: + label: "Optional: splat/spimdisasm version" + description: Were you running rabbitizer through splat or spimdisasm? What are their versions? + validations: + required: false + - type: textarea + id: other-version + attributes: + label: "Optional: Version of other stuff" + description: Here you can put the version of whatever other software you think may be relevant, like Python, rabbitizer, binutils, OS, etc. + placeholder: | + - Python: 4.18 + - clang: 17.½ + - binutils: 2.π + - Wine on WSL2 on Windows 11 on VirtualBox on OpenBSD on Minecraft command blocks. + - Etc + validations: + required: false + - type: textarea + id: user-comments + attributes: + label: "Extra comments?" + description: Here you can mention any other additional comments or info you may want to say. + placeholder: | + There's no problem leaving this. + validations: + required: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 6862ad14..fe1823fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.12.0] - 2024-07-21 + +### Added + +- `RegistersTracker.getJrRegData`. + - Does what the old `RegistersTracker.getJrInfo` method does, but it returns + an actual object instead of an nullable tuple and offers extra information. +- `RegistersTracker.processBranch`. + - Allows tracking which registers has been used to decide branching. + +### Deprecated + +- `RegistersTracker.getJrInfo`. + - Use `RegistersTracker.getJrRegData` instead. + ## [1.11.2] - 2024-07-16 ### Added @@ -643,6 +658,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - First version [unreleased]: https://github.com/Decompollaborate/rabbitizer/compare/master...develop +[1.12.0]: https://github.com/Decompollaborate/rabbitizer/compare/1.11.2...1.12.0 [1.11.2]: https://github.com/Decompollaborate/rabbitizer/compare/1.11.1...1.11.2 [1.11.1]: https://github.com/Decompollaborate/rabbitizer/compare/1.11.0...1.11.1 [1.11.0]: https://github.com/Decompollaborate/rabbitizer/compare/1.10.0...1.11.0 diff --git a/Cargo.toml b/Cargo.toml index 85188108..c52a58c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "rabbitizer" # Version should be synced with include/common/RabbitizerVersion.h -version = "1.11.2" +version = "1.12.0" edition = "2021" authors = ["Anghelo Carvajal "] description = "MIPS instruction decoder" diff --git a/README.md b/README.md index 7a29e5ed..678c134e 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ If you use a `requirements.txt` file in your repository, then you can add this library with the following line: ```txt -rabbitizer>=1.11.2,<2.0.0 +rabbitizer>=1.12.0,<2.0.0 ``` ### Development version @@ -109,7 +109,7 @@ cargo add rabbitizer Or you can add it manually to your `Cargo.toml`: ```toml -rabbitizer = "1.11.2" +rabbitizer = "1.12.0" ``` See this crate at . diff --git a/cplusplus/include/analysis/JrRegData.hpp b/cplusplus/include/analysis/JrRegData.hpp new file mode 100644 index 00000000..767b701b --- /dev/null +++ b/cplusplus/include/analysis/JrRegData.hpp @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: © 2024 Decompollaborate */ +/* SPDX-License-Identifier: MIT */ + +#ifndef RABBITIZER_JR_REG_DATA_HPP +#define RABBITIZER_JR_REG_DATA_HPP +#pragma once + +#include "analysis/RabbitizerJrRegData.h" + +namespace rabbitizer { + class JrRegData { + protected: + RabbitizerJrRegData regData; + + public: + JrRegData(); + JrRegData(const RabbitizerJrRegData &other); + + /** + * Returns a pointer to the inner RabbitizerJrRegData. + * It is recommended to not mess with it unless you know what you are doing. + */ + RabbitizerJrRegData *getCPtr(); + const RabbitizerJrRegData *getCPtr() const; + + bool hasInfo() const; + + int offset() const; + uint32_t address() const; + bool checkedForBranching() const; + int lastBranchOffset() const; + }; +} + +#endif diff --git a/cplusplus/include/analysis/RegistersTracker.hpp b/cplusplus/include/analysis/RegistersTracker.hpp index 1fa0ad79..7f8752a8 100644 --- a/cplusplus/include/analysis/RegistersTracker.hpp +++ b/cplusplus/include/analysis/RegistersTracker.hpp @@ -8,6 +8,7 @@ #include "analysis/RabbitizerRegistersTracker.h" #include "analysis/LoPairingInfo.hpp" +#include "analysis/JrRegData.hpp" #include "instructions/InstructionBase.hpp" @@ -32,7 +33,9 @@ namespace rabbitizer { void overwriteRegisters(const InstructionBase &instr, int instrOffset); void unsetRegistersAfterFuncCall(const InstructionBase &instr, const InstructionBase &prevInstr); bool getAddressIfCanSetType(const InstructionBase &instr, int instrOffset, uint32_t *dstAddress) const; + //! @deprecated: use getJrRegData instead bool getJrInfo(const InstructionBase &instr, int *dstOffset, uint32_t *dstAddress) const; + JrRegData getJrRegData(const InstructionBase &instr) const; void processLui(const InstructionBase &instr, int instrOffset); void processLui(const InstructionBase &instr, int instrOffset, const InstructionBase &prevInstr); @@ -41,6 +44,7 @@ namespace rabbitizer { void processConstant(const InstructionBase &instr, uint32_t value, int offset); LoPairingInfo preprocessLoAndGetInfo(const InstructionBase &instr, int instrOffset); void processLo(const InstructionBase &instr, uint32_t value, int offset); + void processBranch(const InstructionBase &instr, int offset); bool hasLoButNoHi(const InstructionBase &instr) const; }; }; diff --git a/cplusplus/src/analysis/JrRegData.cpp b/cplusplus/src/analysis/JrRegData.cpp new file mode 100644 index 00000000..8c166c47 --- /dev/null +++ b/cplusplus/src/analysis/JrRegData.cpp @@ -0,0 +1,37 @@ +/* SPDX-FileCopyrightText: © 2024 Decompollaborate */ +/* SPDX-License-Identifier: MIT */ + +#include "analysis/JrRegData.hpp" + +using namespace rabbitizer; + +JrRegData::JrRegData() { + RabbitizerJrRegData_init(&this->regData); +} +JrRegData::JrRegData(const RabbitizerJrRegData &other) { + RabbitizerJrRegData_copy(&this->regData, &other); +} + +RabbitizerJrRegData *JrRegData::getCPtr() { + return &this->regData; +} +const RabbitizerJrRegData *JrRegData::getCPtr() const { + return &this->regData; +} + +bool JrRegData::hasInfo() const { + return this->regData.hasInfo; +} + +int JrRegData::offset() const { + return this->regData.offset; +} +uint32_t JrRegData::address() const { + return this->regData.address; +} +bool JrRegData::checkedForBranching() const { + return this->regData.checkedForBranching; +} +int JrRegData::lastBranchOffset() const { + return this->regData.lastBranchOffset; +} diff --git a/cplusplus/src/analysis/RegistersTracker.cpp b/cplusplus/src/analysis/RegistersTracker.cpp index 0cd84eef..e7f1b65f 100644 --- a/cplusplus/src/analysis/RegistersTracker.cpp +++ b/cplusplus/src/analysis/RegistersTracker.cpp @@ -38,6 +38,9 @@ bool RegistersTracker::getAddressIfCanSetType(const InstructionBase &instr, int bool RegistersTracker::getJrInfo(const InstructionBase &instr, int *dstOffset, uint32_t *dstAddress) const { return RabbitizerRegistersTracker_getJrInfo(&this->tracker, instr.getCPtr(), dstOffset, dstAddress); } +JrRegData RegistersTracker::getJrRegData(const InstructionBase &instr) const { + return JrRegData(RabbitizerRegistersTracker_getJrRegData(&this->tracker, instr.getCPtr())); +} void RegistersTracker::processLui(const InstructionBase &instr, int instrOffset) { RabbitizerRegistersTracker_processLui(&this->tracker, instr.getCPtr(), instrOffset, NULL); @@ -60,6 +63,9 @@ LoPairingInfo RegistersTracker::preprocessLoAndGetInfo(const InstructionBase &in void RegistersTracker::processLo(const InstructionBase &instr, uint32_t value, int offset) { RabbitizerRegistersTracker_processLo(&this->tracker, instr.getCPtr(), value, offset); } +void RegistersTracker::processBranch(const InstructionBase &instr, int offset) { + RabbitizerRegistersTracker_processBranch(&this->tracker, instr.getCPtr(), offset); +} bool RegistersTracker::hasLoButNoHi(const InstructionBase &instr) const { return RabbitizerRegistersTracker_hasLoButNoHi(&this->tracker, instr.getCPtr()); } diff --git a/include/analysis/RabbitizerJrRegData.h b/include/analysis/RabbitizerJrRegData.h new file mode 100644 index 00000000..157ddf23 --- /dev/null +++ b/include/analysis/RabbitizerJrRegData.h @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: © 2024 Decompollaborate */ +/* SPDX-License-Identifier: MIT */ + +#ifndef RABBITIZER_JR_REG_DATA_H +#define RABBITIZER_JR_REG_DATA_H +#pragma once + +#include +#include + +#include "common/Utils.h" + +#include "RabbitizerTrackedRegisterState.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct RabbitizerJrRegData { + bool hasInfo; + + int offset; + uint32_t address; + bool checkedForBranching; + int lastBranchOffset; +} RabbitizerJrRegData; + + +NON_NULL(1) +void RabbitizerJrRegData_init(RabbitizerJrRegData *self); +NON_NULL(1, 2) +void RabbitizerJrRegData_copy(RabbitizerJrRegData *self, const RabbitizerJrRegData *other); + +NON_NULL(1, 2) +void RabbitizerJrRegData_initFromRegisterState(RabbitizerJrRegData *self, const RabbitizerTrackedRegisterState *state); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/analysis/RabbitizerRegistersTracker.h b/include/analysis/RabbitizerRegistersTracker.h index 681ab495..1445fc38 100644 --- a/include/analysis/RabbitizerRegistersTracker.h +++ b/include/analysis/RabbitizerRegistersTracker.h @@ -8,6 +8,7 @@ #include "common/Utils.h" #include "RabbitizerTrackedRegisterState.h" #include "RabbitizerLoPairingInfo.h" +#include "RabbitizerJrRegData.h" #include "instructions/RabbitizerInstruction.h" #ifdef __cplusplus @@ -36,8 +37,12 @@ NON_NULL(1, 2, 3) void RabbitizerRegistersTracker_unsetRegistersAfterFuncCall(RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr, const RabbitizerInstruction *prevInstr); NON_NULL(1, 2, 4) bool RabbitizerRegistersTracker_getAddressIfCanSetType(const RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr, int instrOffset, uint32_t *dstAddress); + NON_NULL(1, 2, 3, 4) +//! @deprecated: use `RabbitizerRegistersTracker_getJrRegData` instead bool RabbitizerRegistersTracker_getJrInfo(const RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr, int *dstOffset, uint32_t *dstAddress); +NON_NULL(1, 2) +RabbitizerJrRegData RabbitizerRegistersTracker_getJrRegData(const RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr); // prevInstr can be NULL NON_NULL(1, 2) @@ -55,6 +60,8 @@ RabbitizerLoPairingInfo RabbitizerRegistersTracker_preprocessLoAndGetInfo(Rabbit NON_NULL(1, 2) void RabbitizerRegistersTracker_processLo(RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr, uint32_t value, int offset); NON_NULL(1, 2) +void RabbitizerRegistersTracker_processBranch(RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr, int instrOffset); +NON_NULL(1, 2) bool RabbitizerRegistersTracker_hasLoButNoHi(const RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr); diff --git a/include/analysis/RabbitizerTrackedRegisterState.h b/include/analysis/RabbitizerTrackedRegisterState.h index 05ea8ee9..99ba2f23 100644 --- a/include/analysis/RabbitizerTrackedRegisterState.h +++ b/include/analysis/RabbitizerTrackedRegisterState.h @@ -31,6 +31,9 @@ typedef struct RabbitizerTrackedRegisterState { bool dereferenced; int dereferenceOffset; + bool checkedForBranching; + int lastBranchOffset; + uint32_t value; } RabbitizerTrackedRegisterState; @@ -48,6 +51,8 @@ NON_NULL(1) void RabbitizerTrackedRegisterState_clearGp(RabbitizerTrackedRegisterState *self); NON_NULL(1) void RabbitizerTrackedRegisterState_clearLo(RabbitizerTrackedRegisterState *self); +NON_NULL(1) +void RabbitizerTrackedRegisterState_clearBranch(RabbitizerTrackedRegisterState *self); NON_NULL(1, 2) void RabbitizerTrackedRegisterState_copyState(RabbitizerTrackedRegisterState *self, const RabbitizerTrackedRegisterState *other); @@ -58,6 +63,8 @@ NON_NULL(1) void RabbitizerTrackedRegisterState_setGpLoad(RabbitizerTrackedRegisterState *self, uint32_t value, int offset); NON_NULL(1) void RabbitizerTrackedRegisterState_setLo(RabbitizerTrackedRegisterState *self, uint32_t value, int offset); +NON_NULL(1) +void RabbitizerTrackedRegisterState_setBranching(RabbitizerTrackedRegisterState *self, int offset); NON_NULL(1) void RabbitizerTrackedRegisterState_deref(RabbitizerTrackedRegisterState *self, int offset); diff --git a/include/common/RabbitizerVersion.h b/include/common/RabbitizerVersion.h index 9f92b730..6f38f3b4 100644 --- a/include/common/RabbitizerVersion.h +++ b/include/common/RabbitizerVersion.h @@ -13,8 +13,8 @@ extern "C" { // Header version #define RAB_VERSION_MAJOR 1 -#define RAB_VERSION_MINOR 11 -#define RAB_VERSION_PATCH 2 +#define RAB_VERSION_MINOR 12 +#define RAB_VERSION_PATCH 0 #define RAB_VERSION_STR RAB_STRINGIFY(RAB_VERSION_MAJOR) "." RAB_STRINGIFY(RAB_VERSION_MINOR) "." RAB_STRINGIFY(RAB_VERSION_PATCH) diff --git a/pyproject.toml b/pyproject.toml index 752ff3d0..810c47f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ [project] name = "rabbitizer" # Version should be synced with include/common/RabbitizerVersion.h -version = "1.11.2" +version = "1.12.0" description = "MIPS instruction decoder" # license = "MIT" readme = "README.md" diff --git a/rabbitizer/JrRegData.pyi b/rabbitizer/JrRegData.pyi new file mode 100644 index 00000000..77b20b3f --- /dev/null +++ b/rabbitizer/JrRegData.pyi @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: © 2022-2024 Decompollaborate +# SPDX-License-Identifier: MIT + +from __future__ import annotations + + +class JrRegData: + def hasInfo(self) -> bool: ... + def offset(self) -> int: ... + def address(self) -> int: ... + def checkedForBranching(self) -> bool: ... + def lastBranchOffset(self) -> bool: ... diff --git a/rabbitizer/RegistersTracker.pyi b/rabbitizer/RegistersTracker.pyi index 20d6ac42..48bba147 100644 --- a/rabbitizer/RegistersTracker.pyi +++ b/rabbitizer/RegistersTracker.pyi @@ -7,6 +7,7 @@ from __future__ import annotations from .rabbitizer import Instruction from .LoPairingInfo import LoPairingInfo +from .JrRegData import JrRegData class RegistersTracker: @@ -16,7 +17,9 @@ class RegistersTracker: def overwriteRegisters(self, instr: Instruction, instructionOffset: int) -> None: ... def unsetRegistersAfterFuncCall(self, instr: Instruction, prevInstr: Instruction) -> None: ... def getAddressIfCanSetType(self, instr: Instruction, instrOffset: int) -> int|None: ... + #! deprecated: use `getJrRegData` instead def getJrInfo(self, instr: Instruction) -> tuple[int, int]|None: ... + def getJrRegData(self, instr: Instruction) -> JrRegData: ... def processLui(self, instr: Instruction, instrOffset: int, prevInstr: Instruction|None=None) -> None: ... def processGpLoad(self, instr: Instruction, instrOffset: int) -> None: ... @@ -26,4 +29,5 @@ class RegistersTracker: """Use `.preprocessLoAndGetInfo()` instead""" def preprocessLoAndGetInfo(self, instr: Instruction, instrOffset: int) -> LoPairingInfo: ... def processLo(self, instr: Instruction, value: int, offset: int) -> None: ... + def processBranch(self, instr: Instruction, instrOffset: int) -> None: ... def hasLoButNoHi(self, instr: Instruction) -> bool: ... diff --git a/rabbitizer/__init__.pyi b/rabbitizer/__init__.pyi index e3132195..87aaed3d 100644 --- a/rabbitizer/__init__.pyi +++ b/rabbitizer/__init__.pyi @@ -33,4 +33,5 @@ from .Config import * from .rabbitizer import * +from .JrRegData import * from .RegistersTracker import * diff --git a/rabbitizer/rabbitizer.pyi b/rabbitizer/rabbitizer.pyi index d868e842..91b63436 100644 --- a/rabbitizer/rabbitizer.pyi +++ b/rabbitizer/rabbitizer.pyi @@ -215,8 +215,8 @@ class Instruction: """Check if the instruction and its register is the one usually used for jumping with jumptables. - Specfically, this checks if the instruction is a `jr` but not its register - is not `$ra`. + Specfically, this checks if the instruction is a `jr` but its register is + not `$ra`. Returns `False` if the instruction is not a `jr` or if it is a `jr` but the register is `$ra`. diff --git a/rabbitizer/rabbitizer_module.c b/rabbitizer/rabbitizer_module.c index a6a24cdd..9c09469a 100644 --- a/rabbitizer/rabbitizer_module.c +++ b/rabbitizer/rabbitizer_module.c @@ -58,6 +58,7 @@ static ModuleAttributes rabbitizer_module_attributes[] = { MODULE_ATTRIBUTE_TYPE(LoPairingInfo), MODULE_ATTRIBUTE_TYPE(TrackedRegisterState), MODULE_ATTRIBUTE_TYPE(RegistersTracker), + MODULE_ATTRIBUTE_TYPE(JrRegData), }; static int rabbitizer_module_attributes_Ready(void) { diff --git a/rabbitizer/rabbitizer_module.h b/rabbitizer/rabbitizer_module.h index 40365c87..550db12a 100644 --- a/rabbitizer/rabbitizer_module.h +++ b/rabbitizer/rabbitizer_module.h @@ -33,6 +33,7 @@ DECL_RAB_TYPE(Instruction, instr) DECL_RAB_TYPE(LoPairingInfo, pairingInfo) DECL_RAB_TYPE(TrackedRegisterState, registerState) DECL_RAB_TYPE(RegistersTracker, tracker) +DECL_RAB_TYPE(JrRegData, jrRegData) DECL_ENUM(Abi) diff --git a/rabbitizer/rabbitizer_type_JrRegData.c b/rabbitizer/rabbitizer_type_JrRegData.c new file mode 100644 index 00000000..5a618023 --- /dev/null +++ b/rabbitizer/rabbitizer_type_JrRegData.c @@ -0,0 +1,78 @@ +/* SPDX-FileCopyrightText: © 2022-2024 Decompollaborate */ +/* SPDX-License-Identifier: MIT */ + +#include "rabbitizer_module.h" + + +static void rabbitizer_type_JrRegData_dealloc(PyRabbitizerJrRegData *self) { + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static int rabbitizer_type_JrRegData_init(PyRabbitizerJrRegData *self, UNUSED PyObject *args, UNUSED PyObject *kwds) { + RabbitizerJrRegData_init(&self->jrRegData); + + return 0; +} + + +static PyObject *rabbitizer_type_JrRegData_hasInfo(PyRabbitizerJrRegData *self, UNUSED PyObject *closure) { + if (self->jrRegData.hasInfo) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static PyObject *rabbitizer_type_JrRegData_offset(PyRabbitizerJrRegData *self, UNUSED PyObject *closure) { + return PyLong_FromLong(self->jrRegData.offset); +} + +static PyObject *rabbitizer_type_JrRegData_address(PyRabbitizerJrRegData *self, UNUSED PyObject *closure) { + return PyLong_FromUnsignedLong(self->jrRegData.address); +} + +static PyObject *rabbitizer_type_JrRegData_checkedForBranching(PyRabbitizerJrRegData *self, UNUSED PyObject *closure) { + if (self->jrRegData.checkedForBranching) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static PyObject *rabbitizer_type_JrRegData_lastBranchOffset(PyRabbitizerJrRegData *self, UNUSED PyObject *closure) { + return PyLong_FromLong(self->jrRegData.lastBranchOffset); +} + + +#define METHOD_NO_ARGS(name, docs) { #name, (PyCFunction) (void *) rabbitizer_type_JrRegData_##name, METH_NOARGS, PyDoc_STR(docs) } +#define METHOD_ARGS(name, docs) { #name, (PyCFunction) (void *) rabbitizer_type_JrRegData_##name, METH_VARARGS | METH_KEYWORDS, PyDoc_STR(docs) } + +static PyMethodDef rabbitizer_type_JrRegData_methods[] = { + METHOD_NO_ARGS(hasInfo, ""), + METHOD_NO_ARGS(offset, ""), + METHOD_NO_ARGS(address, ""), + METHOD_NO_ARGS(checkedForBranching, ""), + METHOD_NO_ARGS(lastBranchOffset, ""), + + { 0 }, +}; + + + +DEF_RAB_TYPE(JrRegData) + + +PyTypeObject rabbitizer_type_JrRegData_TypeObject = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "rabbitizer.JrRegData", + .tp_doc = PyDoc_STR("JrRegData"), + .tp_basicsize = sizeof(PyRabbitizerJrRegData), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_new = PyType_GenericNew, + .tp_init = (initproc) rabbitizer_type_JrRegData_init, + .tp_dealloc = (destructor) rabbitizer_type_JrRegData_dealloc, + // .tp_repr = (reprfunc) rabbitizer_type_JrRegData_repr, + // .tp_as_sequence = &rabbitizer_type_JrRegData_classSeqMethods, + // .tp_str = (reprfunc) rabbitizer_type_JrRegData_str, + .tp_methods = rabbitizer_type_JrRegData_methods, + // .tp_getset = rabbitizer_type_JrRegData_getsetters, +}; diff --git a/rabbitizer/rabbitizer_type_RegistersTracker.c b/rabbitizer/rabbitizer_type_RegistersTracker.c index 039b111d..d86cf054 100644 --- a/rabbitizer/rabbitizer_type_RegistersTracker.c +++ b/rabbitizer/rabbitizer_type_RegistersTracker.c @@ -103,6 +103,26 @@ static PyObject *rabbitizer_type_RegistersTracker_getJrInfo(PyRabbitizerRegister Py_RETURN_NONE; } +static PyObject *rabbitizer_type_RegistersTracker_getJrRegData(PyRabbitizerRegistersTracker *self, PyObject *args, PyObject *kwds) { + static char *kwlist[] = { "instr", NULL }; + PyRabbitizerInstruction *instr; + PyRabbitizerJrRegData *ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kwlist, &rabbitizer_type_Instruction_TypeObject, &instr)) { + return NULL; + } + + ret = (PyRabbitizerJrRegData*)PyObject_CallObject((PyObject*)&rabbitizer_type_JrRegData_TypeObject, NULL); + if (ret == NULL) { + PyErr_SetString(PyExc_RuntimeError, "Internal error: not able to instance JrRegData object"); + return NULL; + } + + ret->jrRegData = RabbitizerRegistersTracker_getJrRegData(&self->tracker, &instr->instr); + + return (PyObject*)ret; +} + static PyObject *rabbitizer_type_RegistersTracker_processLui(PyRabbitizerRegistersTracker *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "instr", "instrOffset", "prevInstr", NULL }; PyRabbitizerInstruction *instr; @@ -221,6 +241,20 @@ static PyObject *rabbitizer_type_RegistersTracker_processLo(PyRabbitizerRegister Py_RETURN_NONE; } +static PyObject *rabbitizer_type_RegistersTracker_processBranch(PyRabbitizerRegistersTracker *self, PyObject *args, PyObject *kwds) { + static char *kwlist[] = { "instr", "instrOffset", NULL }; + PyRabbitizerInstruction *instr; + int instrOffset; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!i", kwlist, &rabbitizer_type_Instruction_TypeObject, &instr, &instrOffset)) { + return NULL; + } + + RabbitizerRegistersTracker_processBranch(&self->tracker, &instr->instr, instrOffset); + + Py_RETURN_NONE; +} + static PyObject *rabbitizer_type_RegistersTracker_hasLoButNoHi(PyRabbitizerRegistersTracker *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "instr", NULL }; PyRabbitizerInstruction *instr; @@ -245,6 +279,7 @@ static PyMethodDef rabbitizer_type_RegistersTracker_methods[] = { METHOD_ARGS(unsetRegistersAfterFuncCall, ""), METHOD_ARGS(getAddressIfCanSetType, ""), METHOD_ARGS(getJrInfo, ""), + METHOD_ARGS(getJrRegData, ""), METHOD_ARGS(processLui, ""), METHOD_ARGS(processGpLoad, ""), METHOD_ARGS(getLuiOffsetForConstant, ""), @@ -252,6 +287,7 @@ static PyMethodDef rabbitizer_type_RegistersTracker_methods[] = { METHOD_ARGS(getLuiOffsetForLo, ""), METHOD_ARGS(preprocessLoAndGetInfo, ""), METHOD_ARGS(processLo, ""), + METHOD_ARGS(processBranch, ""), METHOD_ARGS(hasLoButNoHi, ""), { 0 }, @@ -288,7 +324,7 @@ PyObject *rabbitizer_type_RegistersTracker___getitem__(PyRabbitizerRegistersTrac } -static PySequenceMethods example_classSeqMethods = { +static PySequenceMethods rabbitizer_type_RegistersTracker_classSeqMethods = { .sq_item = (ssizeargfunc)rabbitizer_type_RegistersTracker___getitem__, // sq_item }; @@ -307,7 +343,7 @@ PyTypeObject rabbitizer_type_RegistersTracker_TypeObject = { .tp_init = (initproc) rabbitizer_type_RegistersTracker_init, .tp_dealloc = (destructor) rabbitizer_type_RegistersTracker_dealloc, // .tp_repr = (reprfunc) rabbitizer_type_RegistersTracker_repr, - .tp_as_sequence = &example_classSeqMethods, + .tp_as_sequence = &rabbitizer_type_RegistersTracker_classSeqMethods, // .tp_str = (reprfunc) rabbitizer_type_RegistersTracker_str, .tp_methods = rabbitizer_type_RegistersTracker_methods, // .tp_getset = rabbitizer_type_RegistersTracker_getsetters, diff --git a/rabbitizer/rabbitizer_type_TrackedRegisterState.c b/rabbitizer/rabbitizer_type_TrackedRegisterState.c index 89cb995b..11597f8b 100644 --- a/rabbitizer/rabbitizer_type_TrackedRegisterState.c +++ b/rabbitizer/rabbitizer_type_TrackedRegisterState.c @@ -56,6 +56,9 @@ DEF_MEMBER_GET_INT(loOffset) DEF_MEMBER_GET_BOOL(dereferenced) DEF_MEMBER_GET_INT(dereferenceOffset) +DEF_MEMBER_GET_BOOL(checkedForBranching) +DEF_MEMBER_GET_INT(lastBranchOffset) + DEF_MEMBER_GET_UINT(value) @@ -79,6 +82,9 @@ static PyGetSetDef rabbitizer_type_TrackedRegisterState_getsetters[] = { MEMBER_GET(dereferenced, "", NULL), MEMBER_GET(dereferenceOffset, "", NULL), + MEMBER_GET(checkedForBranching, "", NULL), + MEMBER_GET(lastBranchOffset, "", NULL), + MEMBER_GET(value, "", NULL), { 0 } diff --git a/src/analysis/RabbitizerJrRegData.c b/src/analysis/RabbitizerJrRegData.c new file mode 100644 index 00000000..cf3be804 --- /dev/null +++ b/src/analysis/RabbitizerJrRegData.c @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: © 2024 Decompollaborate */ +/* SPDX-License-Identifier: MIT */ + +#include "analysis/RabbitizerJrRegData.h" + +void RabbitizerJrRegData_init(RabbitizerJrRegData *self) { + *self = (RabbitizerJrRegData){ 0 }; +} + +void RabbitizerJrRegData_copy(RabbitizerJrRegData *self, const RabbitizerJrRegData *other) { + *self = *other; +} + +void RabbitizerJrRegData_initFromRegisterState(RabbitizerJrRegData *self, const RabbitizerTrackedRegisterState *state) { + RabbitizerJrRegData_init(self); + + if (!state->hasLoValue || !state->dereferenced) { + return; + } + + self->hasInfo = true; + self->offset = state->loOffset; + self->address = state->value; + self->checkedForBranching = state->checkedForBranching; + self->lastBranchOffset = state->lastBranchOffset; +} diff --git a/src/analysis/RabbitizerRegistersTracker.c b/src/analysis/RabbitizerRegistersTracker.c index ea63bcf2..550d89f8 100644 --- a/src/analysis/RabbitizerRegistersTracker.c +++ b/src/analysis/RabbitizerRegistersTracker.c @@ -73,6 +73,7 @@ bool RabbitizerRegistersTracker_moveRegisters(RabbitizerRegistersTracker *self, srcState = &self->registers[reg]; RabbitizerTrackedRegisterState_copyState(&self->registers[rd], srcState); + RabbitizerTrackedRegisterState_clearBranch(&self->registers[rd]); return true; } @@ -81,6 +82,7 @@ bool RabbitizerRegistersTracker_moveRegisters(RabbitizerRegistersTracker *self, if (RabbitizerTrackedRegisterState_hasAnyValue(srcState)) { RabbitizerTrackedRegisterState_copyState(dstState, srcState); + RabbitizerTrackedRegisterState_clearBranch(&self->registers[rd]); return true; } @@ -141,6 +143,7 @@ void RabbitizerRegistersTracker_overwriteRegisters(RabbitizerRegistersTracker *s RabbitizerTrackedRegisterState_clearGp(state); RabbitizerTrackedRegisterState_clearLo(state); } + RabbitizerTrackedRegisterState_clearBranch(state); } } @@ -192,6 +195,16 @@ bool RabbitizerRegistersTracker_getJrInfo(const RabbitizerRegistersTracker *self return true; } +RabbitizerJrRegData RabbitizerRegistersTracker_getJrRegData(const RabbitizerRegistersTracker *self, + const RabbitizerInstruction *instr) { + const RabbitizerTrackedRegisterState *state = &self->registers[RAB_INSTR_GET_rs(instr)]; + RabbitizerJrRegData jrRegData; + + RabbitizerJrRegData_initFromRegisterState(&jrRegData, state); + + return jrRegData; +} + // prevInstr can be NULL void RabbitizerRegistersTracker_processLui(RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr, int instrOffset, const RabbitizerInstruction *prevInstr) { @@ -267,6 +280,7 @@ bool RabbitizerRegistersTracker_getLuiOffsetForLo(RabbitizerRegistersTracker *se // Simulate a dereference RabbitizerTrackedRegisterState_dereferenceState(&self->registers[RAB_INSTR_GET_rt(instr)], state, instrOffset); + RabbitizerTrackedRegisterState_clearBranch(&self->registers[RAB_INSTR_GET_rt(instr)]); } } @@ -311,6 +325,7 @@ RabbitizerLoPairingInfo RabbitizerRegistersTracker_preprocessLoAndGetInfo(Rabbit // Simulate a dereference RabbitizerTrackedRegisterState_dereferenceState(&self->registers[RAB_INSTR_GET_rt(instr)], state, instrOffset); + RabbitizerTrackedRegisterState_clearBranch(&self->registers[RAB_INSTR_GET_rt(instr)]); } } @@ -334,6 +349,34 @@ void RabbitizerRegistersTracker_processLo(RabbitizerRegistersTracker *self, cons RabbitizerTrackedRegisterState_clearHi(stateDst); RabbitizerTrackedRegisterState_clearGp(stateDst); } + RabbitizerTrackedRegisterState_clearBranch(stateDst); +} + +void RabbitizerRegistersTracker_processBranch(RabbitizerRegistersTracker *self, const RabbitizerInstruction *instr, + int instrOffset) { + RabbitizerTrackedRegisterState *state = NULL; + bool isBranch = + RabbitizerInstrDescriptor_isBranch(instr->descriptor) || RabbitizerInstruction_isUnconditionalBranch(instr); + + assert(isBranch); + if (!isBranch) { + return; + } + + if (RabbitizerInstrDescriptor_readsRs(instr->descriptor)) { + state = &self->registers[RAB_INSTR_GET_rs(instr)]; + RabbitizerTrackedRegisterState_setBranching(state, instrOffset); + } + + if (RabbitizerInstrDescriptor_readsRt(instr->descriptor)) { + state = &self->registers[RAB_INSTR_GET_rt(instr)]; + RabbitizerTrackedRegisterState_setBranching(state, instrOffset); + } + + if (RabbitizerInstrDescriptor_readsRd(instr->descriptor)) { + state = &self->registers[RAB_INSTR_GET_rd(instr)]; + RabbitizerTrackedRegisterState_setBranching(state, instrOffset); + } } bool RabbitizerRegistersTracker_hasLoButNoHi(const RabbitizerRegistersTracker *self, diff --git a/src/analysis/RabbitizerTrackedRegisterState.c b/src/analysis/RabbitizerTrackedRegisterState.c index 1e846057..596617fb 100644 --- a/src/analysis/RabbitizerTrackedRegisterState.c +++ b/src/analysis/RabbitizerTrackedRegisterState.c @@ -23,6 +23,9 @@ void RabbitizerTrackedRegisterState_init(RabbitizerTrackedRegisterState *self, i self->dereferenced = false; self->dereferenceOffset = 0; + self->checkedForBranching = false; + self->lastBranchOffset = 0; + self->value = 0; } @@ -41,6 +44,10 @@ void RabbitizerTrackedRegisterState_clear(RabbitizerTrackedRegisterState *self) self->loOffset = 0; self->dereferenced = false; self->dereferenceOffset = 0; + + self->checkedForBranching = false; + self->lastBranchOffset = false; + self->value = 0; } @@ -62,6 +69,11 @@ void RabbitizerTrackedRegisterState_clearLo(RabbitizerTrackedRegisterState *self self->dereferenceOffset = 0; } +void RabbitizerTrackedRegisterState_clearBranch(RabbitizerTrackedRegisterState *self) { + self->checkedForBranching = 0; + self->lastBranchOffset = 0; +} + void RabbitizerTrackedRegisterState_copyState(RabbitizerTrackedRegisterState *self, const RabbitizerTrackedRegisterState *other) { self->hasLuiValue = other->hasLuiValue; @@ -76,6 +88,9 @@ void RabbitizerTrackedRegisterState_copyState(RabbitizerTrackedRegisterState *se self->dereferenced = other->dereferenced; self->dereferenceOffset = other->dereferenceOffset; + self->checkedForBranching = other->checkedForBranching; + self->lastBranchOffset = other->lastBranchOffset; + self->value = other->value; } @@ -103,6 +118,11 @@ void RabbitizerTrackedRegisterState_setLo(RabbitizerTrackedRegisterState *self, self->dereferenceOffset = 0; } +void RabbitizerTrackedRegisterState_setBranching(RabbitizerTrackedRegisterState *self, int offset) { + self->checkedForBranching = true; + self->lastBranchOffset = offset; +} + void RabbitizerTrackedRegisterState_deref(RabbitizerTrackedRegisterState *self, int offset) { self->dereferenced = true; self->dereferenceOffset = offset; diff --git a/src/instructions/RabbitizerInstruction/RabbitizerInstruction_Examination.c b/src/instructions/RabbitizerInstruction/RabbitizerInstruction_Examination.c index ed4231b1..f33308d3 100644 --- a/src/instructions/RabbitizerInstruction/RabbitizerInstruction_Examination.c +++ b/src/instructions/RabbitizerInstruction/RabbitizerInstruction_Examination.c @@ -135,8 +135,8 @@ bool RabbitizerInstruction_isReturn(const RabbitizerInstruction *self) { * Check if the instruction and its register is the one usually used for * jumping with jumptables. * - * Specfically, this checks if the instruction is a `jr` but not its register - * is not `$ra`. + * Specfically, this checks if the instruction is a `jr` but its register is + * not `$ra`. * * Returns `False` if the instruction is not a `jr` or if it is a `jr` but * the register is `$ra`.