Android 下内联汇编,Android Studio 汇编开发

版权归作者所有,如有转发,请注明文章出处:cyrus-studio.github.io/blog/

内联汇编

Android 内联汇编非常适用于 ARM 架构的性能优化和底层操作,通常用于加密、解密、特定指令优化等领域。

1. 基础语法

内联汇编在 C/C++ 代码中通过 asm 或 asm 关键字进行声明,格式如下

js 复制代码
asm ("汇编指令" : 输出操作数 : 输入操作数 : 破坏描述符);

详细说明:

  • 汇编指令:这是我们想要执行的汇编代码,通常是 ARM 或 ARM64 指令。

  • 输出操作数:指定汇编代码的输出结果如何映射到 C++ 变量。

  • 输入操作数:指定传递给汇编代码的输入。

  • 破坏描述符:用于告诉编译器哪些寄存器或内存位置将被汇编代码修改,以避免编译器优化引起的问题。

2. 占位符

占位符用于在汇编指令中插入 C++ 变量,格式为 %0、%1 等,对应输出和输入操作数的顺序。

例如

perl 复制代码
int x = 10, y = 20, result;
asm("add %0, %1, %2" : "=r"(result) : "r"(x), "r"(y));

上面的代码将 x 和 y 相加并将结果存入 result。

3. 输出操作数和输入操作数

=r 表示输出操作数是一个通用寄存器类型。

r 表示输入操作数是一个寄存器类型。

例如

perl 复制代码
int a = 5, b = 3, result;
asm("mul %0, %1, %2" : "=r"(result) : "r"(a), "r"(b));

这段代码在 ARM 架构中将 a 和 b 相乘,结果存入 result。

4. 破坏描述符

破坏描述符(clobber)用于告诉编译器哪些寄存器或内存位置将被汇编代码修改,以避免编译器优化引起的问题。

常用的描述符包括:

  • "cc":表示汇编代码将更改条件代码寄存器。

  • "memory":表示汇编代码可能更改内存内容。

例如

perl 复制代码
asm("mov %0, #0\n"
    "cmp %1, %2\n"
    "moveq %0, #1"
    : "=r"(result)
    : "r"(a), "r"(b)
    : "cc");

这里 cc 表示条件标志寄存器会被更改,编译器需要考虑这一点。

5. 使用 volatile

在汇编指令前添加 volatile 关键字,确保编译器不会优化或重新排序该段汇编代码。

例如

arduino 复制代码
asm volatile ("nop"); // 表示这是一个空操作,编译器不会优化掉

6. 指针操作

内联汇编还可以使用指针操作对内存内容进行直接操作。例如

ini 复制代码
int value = 42;
int* ptr = &value;
asm("ldr %0, [%1]" : "=r"(value) : "r"(ptr) : "memory");

这里 ldr 从 ptr 指向的内存地址加载值到 value 中。

7. 示例:简单加法操作

以下是一个在 Android ARM 架构中使用内联汇编执行加法的示例

perl 复制代码
int a = 10, b = 20, sum;
asm("add %0, %1, %2" : "=r"(sum) : "r"(a), "r"(b));

这段代码执行 a + b 并将结果存储在 sum 中。

多行汇编可以使用反斜杠 \n 进行换行。例如,计算两个数的平方和

perl 复制代码
int x = 3, y = 4, result;
asm(
    "mul %0, %1, %1\n"   // result = x * x
    "mla %0, %2, %2, %0" // result += y * y (multiply-accumulate)
    : "=r"(result)
    : "r"(x), "r"(y)
);

Android Studio 汇编开发

首先创建 Native C++ 工程

创建 Activity,声明 native 函数,点击按钮调用 native 层用汇编实现的加密/解密方法并打印返回结果。

kotlin 复制代码
package com.cyrus.example.assembly

import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.cyrus.example.R


/**
 * 内联汇编
 */
class AssemblyActivity : AppCompatActivity() {

    // 加载 native 库
    init {
        System.loadLibrary("assembly-lib");
    }

    // 通过内联汇编实现的加密函数
    external fun encryptString(input: String?): String

    // 通过内联汇编实现的解密函数
    external fun decryptString(input: String?): String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_assembly) // 更新布局文件名

        // 原始字符串
        val input = "Hello, 内联汇编!"

        // 加密按钮
        val encryptButton = findViewById<Button>(R.id.button_encrypt)
        encryptButton.setOnClickListener { view: View? ->
            // 调用 C++ 方法获取加密后的字符串
            val encrypted = encryptString(input)

            // 打印原字符串和加密后的字符串
            val message = "Original: $input\nEncrypted: $encrypted"
            Toast.makeText(this@AssemblyActivity, message, Toast.LENGTH_LONG).show()
        }

        // 解密按钮
        val decryptButton = findViewById<Button>(R.id.button_decrypt)
        decryptButton.setOnClickListener { view: View? ->

            // 调用 C++ 方法获取加密后的字符串
            val encrypted = encryptString(input)

            val decrypted = decryptString(encrypted)

            // 打印加密字符串和解密后的字符串
            val message = "Encrypted: $encrypted\nDecrypted: $decrypted"
            Toast.makeText(this@AssemblyActivity, message, Toast.LENGTH_LONG).show()
        }

    }
}

创建 assembly-lib.cpp,编写内联汇编代码

arduino 复制代码
#include <jni.h>
#include <string>
#include <android/log.h>

#define LOG_TAG "assembly-lib.cpp"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)


extern "C"
JNIEXPORT jstring JNICALL
Java_com_cyrus_example_assembly_AssemblyActivity_encryptString(JNIEnv *env, jobject /* this */,
                                                               jstring input) {
    const char *inputStr = env->GetStringUTFChars(input, nullptr);
    std::string encryptedStr(inputStr);

    // 获取输入字符串的 Unicode 码点
    const jchar *inputChars = env->GetStringChars(input, nullptr);
    jsize length = env->GetStringLength(input);

    // 创建加密后的字符串
    jchar *encryptedChars = new jchar[length];
    for (jsize i = 0; i < length; i++) {
        jchar c = inputChars[i];

        // 使用内联汇编对每个 Unicode 字符的值加 3,实现加密
        asm volatile (
                "add %0, %1, #3\n"     // 每个字符的 Unicode 值加 3
                : "=r"(c)              // 输出到 c
                : "r"(c)               // 输入 c
                );
        encryptedChars[i] = c;
    }

    // 释放输入字符串的内存
    env->ReleaseStringChars(input, inputChars);
    jstring encryptedString = env->NewString(encryptedChars, length);

    // 释放加密字符串的内存
    delete[] encryptedChars;

    return encryptedString;
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_cyrus_example_assembly_AssemblyActivity_decryptString(JNIEnv *env, jobject /* this */,
                                                               jstring input) {
    const char *inputStr = env->GetStringUTFChars(input, nullptr);
    std::string decryptedStr(inputStr);

    // 获取输入字符串的 Unicode 码点
    const jchar *inputChars = env->GetStringChars(input, nullptr);
    jsize length = env->GetStringLength(input);

    // 创建解密后的字符串
    jchar *decryptedChars = new jchar[length];
    for (jsize i = 0; i < length; i++) {
        jchar c = inputChars[i];

        // 使用内联汇编对每个 Unicode 字符的值减 3,实现解密
        asm volatile (
                "sub %0, %1, #3\n"     // 每个字符的 Unicode 值减 3
                : "=r"(c)              // 输出到 c
                : "r"(c)               // 输入 c
                );
        decryptedChars[i] = c;
    }

    // 释放输入字符串的内存
    env->ReleaseStringChars(input, inputChars);
    jstring decryptedString = env->NewString(decryptedChars, length);

    // 释放解密字符串的内存
    delete[] decryptedChars;

    return decryptedString;
}

配置 CMakeLists.txt

perl 复制代码
cmake_minimum_required(VERSION 3.4.1)

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the NDK library that you want CMake to locate.
        log)

add_library( # 设置库的名称
        assembly-lib

        # 设置库的类型
        SHARED

        # 设置源文件路径
        assembly-lib.cpp)

target_link_libraries( # 将 log 库链接到目标库
        assembly-lib
        ${log-lib})

运行测试

兼容不同的 CPU 架构

在 Android 开发中,编写兼容不同架构的内联汇编代码时,可以通过条件编译来处理不同的指令集。

由于 Android 设备可能使用不同的 CPU 架构(如 ARM、ARM64、x86 和 x86_64),使用条件编译和 NDK 的特性,我们可以让代码适配不同的 CPU 架构。

1. 使用条件编译判断架构

通过 #ifdef 和 #if defined(...) 指令,判断当前编译架构并编写相应的内联汇编代码。

arduino 复制代码
extern "C"
JNIEXPORT jint JNICALL
Java_com_cyrus_example_assembly_AssemblyActivity_addNumbers(JNIEnv *env, jobject, jint a,
                                                            jint b) {
    int result;

#if defined(__aarch64__)
    // ARM64 内联汇编版本
    asm volatile (
            "add %w[result], %w[val1], %w[val2]\n"  // 执行加法
            : [result] "=r" (result)               // 输出操作数
    : [val1] "r" (a), [val2] "r" (b)       // 输入操作数
    );
#elif defined(__arm__)
    // ARM 32-bit 内联汇编版本
    asm volatile (
        "add %[result], %[val1], %[val2]\n"    // 执行加法
        : [result] "=r" (result)               // 输出操作数
        : [val1] "r" (a), [val2] "r" (b)       // 输入操作数
    );
#elif defined(__i386__)
    // x86 内联汇编版本
    asm volatile (
        "addl %[val1], %[val2]\n"
        "movl %[val2], %[result]\n"           // 使用32位 x86 指令完成加法
        : [result] "=r" (result)
        : [val1] "r" (a), [val2] "r" (b)
    );
#elif defined(__x86_64__)
    // x86_64 内联汇编版本
    asm volatile (
        "addq %[val1], %[val2]\n"
        "movq %[val2], %[result]\n"           // 使用64位 x86 指令完成加法
        : [result] "=r" (result)
        : [val1] "r" (a), [val2] "r" (b)
    );
#else
    // 如果架构不支持,使用 C++ 代码实现
    result = a + b;
#endif

    LOGI("Result of addition: %d", result);
    return result;
}

2. 使用 abiFilters 指定编译目标

使用 abiFilters 来指定不同的 ABI,以便编译每个架构的共享库

arduino 复制代码
// build.gradle
android {
    defaultConfig {
        ndk {
            abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
        }
    }
}

build 出来的 apk 会包含不同 CPU 架构下的 so

最后,在不同 CPU 架构下的设备下运行测试正常

源码

github.com/CYRUS-STUDI...

相关推荐
烬奇小云2 小时前
认识一下Unicorn
android·python·安全·系统安全
顾北川_野15 小时前
Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
android
CYRUS STUDIO15 小时前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
右手吉他15 小时前
Android ANR分析总结
android
PenguinLetsGo17 小时前
关于 Android15 GKI2407R40 导致梆梆加固软件崩溃
android·linux
杨武博19 小时前
音频格式转换
android·音视频
音视频牛哥21 小时前
Android音视频直播低延迟探究之:WLAN低延迟模式
android·音视频·实时音视频·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
ChangYan.21 小时前
CondaError: Run ‘conda init‘ before ‘conda activate‘解决办法
android·conda
二流小码农21 小时前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
夏非夏1 天前
Android 生成并加载PDF文件
android