diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index bf3004d..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-# To get started with Dependabot version updates, you'll need to specify which
-# package ecosystems to update and where the package manifests are located.
-# Please see the documentation for all configuration options:
-# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
-
-version: 2
-updates:
- - package-ecosystem: "github-actions" # See documentation for possible values
- directory: "/" # Location of package manifests
- schedule:
- interval: "daily"
-
- - package-ecosystem: "gradle" # See documentation for possible values
- directory: "/" # Location of package manifests
- schedule:
- interval: "daily"
diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml
deleted file mode 100644
index 9aab2a5..0000000
--- a/.github/workflows/update-gradle-wrapper.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: Update Gradle Wrapper
-
-on:
- schedule:
- - cron: "0 0 * * *"
- workflow_dispatch:
-
-jobs:
- update-gradle-wrapper:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout the code
- uses: actions/checkout@v4
-
- - name: Set up Java
- uses: actions/setup-java@v4
- with:
- distribution: "temurin"
- java-version: 21
-
- - name: Grant execute permission for gradlew
- run: chmod +x gradlew
-
- - name: Update Gradle Wrapper
- uses: gradle-update/update-gradle-wrapper-action@v1
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 3aa494f..9661ac7 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,6 +2,5 @@
-
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index f9952f2..4cb3439 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -12,8 +12,8 @@ android {
applicationId = "es.chiteroman.playintegrityfix"
minSdk = 26
targetSdk = 34
- versionCode = 15900
- versionName = "v15.9"
+ versionCode = 15910
+ versionName = "v15.9.1"
multiDexEnabled = false
buildFeatures {
@@ -75,7 +75,6 @@ android {
dependencies {
implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0")
- implementation("com.bytedance.android:shadowhook:1.0.9")
}
tasks.register("updateModuleProp") {
diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt
index 3709910..8fa5ec3 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -3,8 +3,17 @@ cmake_minimum_required(VERSION 3.22.1)
project(playintegrityfix)
find_package(cxx REQUIRED CONFIG)
-find_package(shadowhook REQUIRED CONFIG)
-add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp)
+if (${ANDROID_ABI} STREQUAL "arm64-v8a")
+ set(ARCH "arm64")
+elseif (${ANDROID_ABI} STREQUAL "armeabi-v7a")
+ set(ARCH "arm")
+endif ()
-target_link_libraries(${CMAKE_PROJECT_NAME} log cxx::cxx shadowhook::shadowhook)
\ No newline at end of file
+file(GLOB shadowhook_src shadowhook/*.c shadowhook/arch/${ARCH}/*.c shadowhook/common/*.c shadowhook/third_party/*/*.c)
+
+add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp ${shadowhook_src})
+
+target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE . shadowhook shadowhook/include shadowhook/arch/${ARCH} shadowhook/common shadowhook/third_party/xdl shadowhook/third_party/bsd shadowhook/third_party/lss)
+
+target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log cxx::cxx)
\ No newline at end of file
diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp
index f9ba56f..aeb699c 100644
--- a/app/src/main/cpp/main.cpp
+++ b/app/src/main/cpp/main.cpp
@@ -11,6 +11,8 @@
#define PIF_JSON "/data/adb/pif.json"
+#define PIF_JSON_DEFAULT "/data/adb/modules/playintegrityfix/pif.json"
+
static std::string FIRST_API_LEVEL, SECURITY_PATCH, BUILD_ID;
typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
@@ -34,8 +36,6 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
if (!FIRST_API_LEVEL.empty()) {
value = FIRST_API_LEVEL.c_str();
- } else {
- value = "21";
}
LOGD("[%s]: %s", name, value);
@@ -144,6 +144,7 @@ public:
if (jsonSize < 1) {
close(fd);
LOGD("JSON file not found!");
+ api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
return;
}
@@ -160,7 +161,7 @@ public:
}
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
- if (dexVector.empty()) return;
+ if (dexVector.empty() || json.empty()) return;
injectDex();
@@ -172,8 +173,8 @@ public:
}
private:
- zygisk::Api *api;
- JNIEnv *env;
+ zygisk::Api *api = nullptr;
+ JNIEnv *env = nullptr;
std::vector dexVector;
nlohmann::json json;
@@ -285,6 +286,8 @@ static std::vector readFile(const char *path) {
vector.resize(size);
fread(vector.data(), 1, size, file);
fclose(file);
+ } else {
+ LOGD("Couldn't read %s file!", path);
}
return vector;
@@ -292,8 +295,13 @@ static std::vector readFile(const char *path) {
static void companion(int fd) {
- auto dexVector = readFile(CLASSES_DEX);
- auto jsonVector = readFile(PIF_JSON);
+ std::vector dexVector, jsonVector;
+
+ dexVector = readFile(CLASSES_DEX);
+
+ jsonVector = readFile(PIF_JSON);
+
+ if (jsonVector.empty()) jsonVector = readFile(PIF_JSON_DEFAULT);
long dexSize = dexVector.size();
long jsonSize = jsonVector.size();
diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c
new file mode 100644
index 0000000..d71e4f8
--- /dev/null
+++ b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c
@@ -0,0 +1,446 @@
+// Copyright (c) 2021-2022 ByteDance Inc.
+//
+// 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.
+//
+
+// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11.
+
+#include "sh_a32.h"
+
+#include
+#include
+#include
+
+#include "sh_log.h"
+
+// https://developer.arm.com/documentation/ddi0406/latest
+// https://developer.arm.com/documentation/ddi0597/latest
+
+typedef enum {
+ IGNORED = 0,
+ B_A1,
+ BX_A1,
+ BL_IMM_A1,
+ BLX_IMM_A2,
+ ADD_REG_A1,
+ ADD_REG_PC_A1,
+ SUB_REG_A1,
+ SUB_REG_PC_A1,
+ ADR_A1,
+ ADR_A2,
+ MOV_REG_A1,
+ MOV_REG_PC_A1,
+ LDR_LIT_A1,
+ LDR_LIT_PC_A1,
+ LDRB_LIT_A1,
+ LDRD_LIT_A1,
+ LDRH_LIT_A1,
+ LDRSB_LIT_A1,
+ LDRSH_LIT_A1,
+ LDR_REG_A1,
+ LDR_REG_PC_A1,
+ LDRB_REG_A1,
+ LDRD_REG_A1,
+ LDRH_REG_A1,
+ LDRSB_REG_A1,
+ LDRSH_REG_A1
+} sh_a32_type_t;
+
+static sh_a32_type_t sh_a32_get_type(uint32_t inst) {
+ if (((inst & 0x0F000000u) == 0x0A000000) && ((inst & 0xF0000000) != 0xF0000000))
+ return B_A1;
+ else if (((inst & 0x0FFFFFFFu) == 0x012FFF1F) && ((inst & 0xF0000000) != 0xF0000000))
+ return BX_A1;
+ else if (((inst & 0x0F000000u) == 0x0B000000) && ((inst & 0xF0000000) != 0xF0000000))
+ return BL_IMM_A1;
+ else if ((inst & 0xFE000000) == 0xFA000000)
+ return BLX_IMM_A2;
+ else if (((inst & 0x0FE00010u) == 0x00800000) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) &&
+ (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F)))
+ return ((inst & 0x0000F000u) == 0x0000F000) ? ADD_REG_PC_A1 : ADD_REG_A1;
+ else if (((inst & 0x0FE00010u) == 0x00400000) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) &&
+ (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F)))
+ return ((inst & 0x0000F000u) == 0x0000F000) ? SUB_REG_PC_A1 : SUB_REG_A1;
+ else if (((inst & 0x0FFF0000u) == 0x028F0000) && ((inst & 0xF0000000) != 0xF0000000))
+ return ADR_A1;
+ else if (((inst & 0x0FFF0000u) == 0x024F0000) && ((inst & 0xF0000000) != 0xF0000000))
+ return ADR_A2;
+ else if (((inst & 0x0FEF001Fu) == 0x01A0000F) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x0010F000u) != 0x0010F000) &&
+ (!(((inst & 0x0000F000u) == 0x0000F000) && ((inst & 0x00000FF0u) != 0x00000000))))
+ return ((inst & 0x0000F000u) == 0x0000F000) ? MOV_REG_PC_A1 : MOV_REG_A1;
+ else if (((inst & 0x0F7F0000u) == 0x051F0000) && ((inst & 0xF0000000) != 0xF0000000))
+ return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_A1 : LDR_LIT_A1;
+ else if (((inst & 0x0F7F0000u) == 0x055F0000) && ((inst & 0xF0000000) != 0xF0000000))
+ return LDRB_LIT_A1;
+ else if (((inst & 0x0F7F00F0u) == 0x014F00D0) && ((inst & 0xF0000000) != 0xF0000000))
+ return LDRD_LIT_A1;
+ else if (((inst & 0x0F7F00F0u) == 0x015F00B0) && ((inst & 0xF0000000) != 0xF0000000))
+ return LDRH_LIT_A1;
+ else if (((inst & 0x0F7F00F0u) == 0x015F00D0) && ((inst & 0xF0000000) != 0xF0000000))
+ return LDRSB_LIT_A1;
+ else if (((inst & 0x0F7F00F0u) == 0x015F00F0) && ((inst & 0xF0000000) != 0xF0000000))
+ return LDRSH_LIT_A1;
+ else if (((inst & 0x0E5F0010u) == 0x061F0000) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x01200000u) != 0x00200000))
+ return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_REG_PC_A1 : LDR_REG_A1;
+ else if (((inst & 0x0E5F0010u) == 0x065F0000) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x01200000u) != 0x00200000))
+ return LDRB_REG_A1;
+ else if (((inst & 0x0E5F0FF0u) == 0x000F00D0) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x01200000u) != 0x00200000))
+ return LDRD_REG_A1;
+ else if (((inst & 0x0E5F0FF0u) == 0x001F00B0) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x01200000u) != 0x00200000))
+ return LDRH_REG_A1;
+ else if (((inst & 0x0E5F0FF0u) == 0x001F00D0) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x01200000u) != 0x00200000))
+ return LDRSB_REG_A1;
+ else if (((inst & 0x0E5F0FF0u) == 0x001F00F0) && ((inst & 0xF0000000) != 0xF0000000) &&
+ ((inst & 0x01200000u) != 0x00200000))
+ return LDRSH_REG_A1;
+ else
+ return IGNORED;
+}
+
+size_t sh_a32_get_rewrite_inst_len(uint32_t inst) {
+ static uint8_t map[] = {
+ 4, // IGNORED
+ 12, // B_A1
+ 12, // BX_A1
+ 16, // BL_IMM_A1
+ 16, // BLX_IMM_A2
+ 32, // ADD_REG_A1
+ 32, // ADD_REG_PC_A1
+ 32, // SUB_REG_A1
+ 32, // SUB_REG_PC_A1
+ 12, // ADR_A1
+ 12, // ADR_A2
+ 32, // MOV_REG_A1
+ 12, // MOV_REG_PC_A1
+ 24, // LDR_LIT_A1
+ 36, // LDR_LIT_PC_A1
+ 24, // LDRB_LIT_A1
+ 24, // LDRD_LIT_A1
+ 24, // LDRH_LIT_A1
+ 24, // LDRSB_LIT_A1
+ 24, // LDRSH_LIT_A1
+ 32, // LDR_REG_A1
+ 36, // LDR_REG_PC_A1
+ 32, // LDRB_REG_A1
+ 32, // LDRD_REG_A1
+ 32, // LDRH_REG_A1
+ 32, // LDRSB_REG_A1
+ 32 // LDRSH_REG_A1
+ };
+
+ return (size_t)(map[sh_a32_get_type(inst)]);
+}
+
+static bool sh_a32_is_addr_need_fix(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) {
+ return (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr);
+}
+
+static uintptr_t sh_a32_fix_addr(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) {
+ if (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr) {
+ uintptr_t cursor_addr = rinfo->overwrite_start_addr;
+ size_t offset = 0;
+ for (size_t i = 0; i < rinfo->rewrite_inst_lens_cnt; i++) {
+ if (cursor_addr >= addr) break;
+ cursor_addr += 4;
+ offset += rinfo->rewrite_inst_lens[i];
+ }
+ uintptr_t fixed_addr = (uintptr_t)rinfo->rewrite_buf + offset;
+ SH_LOG_INFO("a32 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr);
+ return fixed_addr;
+ }
+
+ return addr;
+}
+
+static size_t sh_a32_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
+ sh_a32_rewrite_info_t *rinfo) {
+ uint32_t cond;
+ if (type == B_A1 || type == BL_IMM_A1 || type == BX_A1)
+ cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
+ else
+ // type == BLX_IMM_A2
+ cond = 0xE; // 1110 None (AL)
+
+ uint32_t addr;
+ if (type == B_A1 || type == BL_IMM_A1) {
+ uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0);
+ uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(imm24 << 2u, 26u);
+ addr = pc + imm32; // arm -> arm
+ } else if (type == BLX_IMM_A2) {
+ uint32_t h = SH_UTIL_GET_BIT_32(inst, 24);
+ uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0);
+ uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32((imm24 << 2u) | (h << 1u), 26u);
+ addr = SH_UTIL_SET_BIT0(pc + imm32); // arm -> thumb
+ } else {
+ // type == BX_A1
+ // BX PC
+ // PC must be even, and the "arm" instruction must be at a 4-byte aligned address,
+ // so the instruction set must keep "arm" unchanged.
+ addr = pc; // arm -> arm
+ }
+ addr = sh_a32_fix_addr(addr, rinfo);
+
+ size_t idx = 0;
+ if (type == BL_IMM_A1 || type == BLX_IMM_A2) {
+ buf[idx++] = 0x028FE008u | (cond << 28u); // ADD LR, PC, #8
+ }
+ buf[idx++] = 0x059FF000u | (cond << 28u); // LDR PC, [PC, #0]
+ buf[idx++] = 0xEA000000; // B #0
+ buf[idx++] = addr;
+ return idx * 4; // 12 or 16
+}
+
+static size_t sh_a32_rewrite_add_or_sub(uint32_t *buf, uint32_t inst, uintptr_t pc) {
+ // ADD{S} , , PC{, } or ADD{S} , PC, {, }
+ // SUB{S} , , PC{, } or SUB{S} , PC, {, }
+ uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
+ uint32_t rn = SH_UTIL_GET_BITS_32(inst, 19, 16);
+ uint32_t rm = SH_UTIL_GET_BITS_32(inst, 3, 0);
+ uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12);
+
+ uint32_t rx; // r0 - r3
+ for (rx = 3;; --rx)
+ if (rx != rn && rx != rm && rx != rd) break;
+
+ if (rd == 0xF) // Rd == PC
+ {
+ uint32_t ry; // r0 - r4
+ for (ry = 4;; --ry)
+ if (ry != rn && ry != rm && ry != rd && ry != rx) break;
+
+ buf[0] = 0x0A000000u | (cond << 28u); // B #0
+ buf[1] = 0xEA000005; // B #20
+ buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC}
+ buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8]
+ if (rn == 0xF)
+ // Rn == PC
+ buf[4] =
+ (inst & 0x0FF00FFFu) | 0xE0000000 | (ry << 12u) | (rx << 16u); // ADD/SUB Ry, Rx, Rm{, }
+ else
+ // Rm == PC
+ buf[4] = (inst & 0x0FFF0FF0u) | 0xE0000000 | (ry << 12u) | rx; // ADD/SUB Ry, Rn, Rx{, }
+ buf[5] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8]
+ buf[6] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC}
+ buf[7] = pc;
+ return 32;
+ } else {
+ buf[0] = 0x0A000000u | (cond << 28u); // B #0
+ buf[1] = 0xEA000005; // B #20
+ buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx}
+ buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8]
+ if (rn == 0xF)
+ // Rn == PC
+ buf[4] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // ADD/SUB{S} Rd, Rx, Rm{, }
+ else
+ // Rm == PC
+ buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // ADD/SUB{S} Rd, Rn, Rx{, }
+ buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx}
+ buf[6] = 0xEA000000; // B #0
+ buf[7] = pc;
+ return 32;
+ }
+}
+
+static size_t sh_a32_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
+ sh_a32_rewrite_info_t *rinfo) {
+ uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
+ uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); // r0 - r15
+ uint32_t imm12 = SH_UTIL_GET_BITS_32(inst, 11, 0);
+ uint32_t imm32 = sh_util_arm_expand_imm(imm12);
+ uint32_t addr = (type == ADR_A1 ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32));
+ if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
+
+ buf[0] = 0x059F0000u | (cond << 28u) | (rd << 12u); // LDR Rd, [PC, #0]
+ buf[1] = 0xEA000000; // B #0
+ buf[2] = addr;
+ return 12;
+}
+
+static size_t sh_a32_rewrite_mov(uint32_t *buf, uint32_t inst, uintptr_t pc) {
+ // MOV{S} , PC
+ uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
+ uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12);
+ uint32_t rx = (rd == 0) ? 1 : 0;
+
+ if (rd == 0xF) // Rd == PC (MOV PC, PC)
+ {
+ buf[0] = 0x059FF000u | (cond << 28u); // LDR PC, [PC, #0]
+ buf[1] = 0xEA000000; // B #0
+ buf[2] = pc;
+ return 12;
+ } else {
+ buf[0] = 0x0A000000u | (cond << 28u); // B #0
+ buf[1] = 0xEA000005; // B #20
+ buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx}
+ buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8]
+ buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // MOV{S} Rd, Rx{, #/RRX}
+ buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx}
+ buf[6] = 0xEA000000; // B #0
+ buf[7] = pc;
+ return 32;
+ }
+}
+
+static size_t sh_a32_rewrite_ldr_lit(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type,
+ sh_a32_rewrite_info_t *rinfo) {
+ uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28);
+ uint32_t u = SH_UTIL_GET_BIT_32(inst, 23);
+ uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12);
+
+ uint32_t imm32;
+ if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1)
+ imm32 = SH_UTIL_GET_BITS_32(inst, 11, 0);
+ else
+ imm32 = (SH_UTIL_GET_BITS_32(inst, 11, 8) << 4u) + SH_UTIL_GET_BITS_32(inst, 3, 0);
+ uint32_t addr = (u ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32));
+ if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed
+
+ if (type == LDR_LIT_PC_A1 && rt == 0xF) {
+ // Rt == PC
+ buf[0] = 0x0A000000u | (cond << 28u); // B #0
+ buf[1] = 0xEA000006; // B #24
+ buf[2] = 0xE92D0003; // PUSH {R0, R1}
+ buf[3] = 0xE59F0000; // LDR R0, [PC, #0]
+ buf[4] = 0xEA000000; // B #0
+ buf[5] = addr; //
+ buf[6] = 0xE5900000; // LDR R0, [R0]
+ buf[7] = 0xE58D0004; // STR R0, [SP, #4]
+ buf[8] = 0xE8BD8001; // POP {R0, PC}
+ return 36;
+ } else {
+ buf[0] = 0x0A000000u | (cond << 28u); // B #0
+ buf[1] = 0xEA000003; // B #12
+ buf[2] = 0xE59F0000 | (rt << 12u); // LDR Rt, [PC, #0]
+ buf[3] = 0xEA000000; // B #0
+ buf[4] = addr; //
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wswitch"
+ switch (type) {
+ case LDR_LIT_A1:
+ buf[5] = 0xE5900000 | (rt << 16u) | (rt << 12u); // LDR Rt, [Rt]
+ break;
+ case LDRB_LIT_A1:
+ buf[5] = 0xE5D00000 | (rt << 16u) | (rt << 12u); // LDRB Rt, [Rt]
+ break;
+ case LDRD_LIT_A1:
+ buf[5] = 0xE1C000D0 | (rt << 16u) | (rt << 12u); // LDRD Rt, [Rt]
+ break;
+ case LDRH_LIT_A1:
+ buf[5] = 0xE1D000B0 | (rt << 16u) | (rt << 12u); // LDRH Rt, [Rt]
+ break;
+ case LDRSB_LIT_A1:
+ buf[5] = 0xE1D000D0 | (rt << 16u) | (rt << 12u); // LDRSB Rt, [Rt]
+ break;
+ case LDRSH_LIT_A1:
+ buf[5] = 0xE1D000F0 | (rt << 16u) | (rt << 12u); // LDRSH Rt, [Rt]
+ break;
+ }
+#pragma clang diagnostic pop
+ return 24;
+ }
+}
+
+static size_t sh_a32_rewrite_ldr_reg(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type) {
+ // LDR