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

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

内联汇编

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

1. 基础语法

内联汇编在 C/C++ 代码中通过 asm 或 asm 关键字进行声明,格式如下
asm (“汇编指令” : 输出操作数 : 输入操作数 : 破坏描述符);

详细说明:

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

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

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

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

2. 占位符

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

例如

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

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

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

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

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

例如

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”:表示汇编代码可能更改内存内容。

例如

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

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

5. 使用 volatile

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

例如

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

6. 指针操作

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

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

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

7. 示例:简单加法操作

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

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

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

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

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++ 工程
image.png

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

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,编写内联汇编代码

#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

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})

运行测试
image.png

兼容不同的 CPU 架构

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

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

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

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

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,以便编译每个架构的共享库

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

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

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

源码

https://github.com/CYRUS-STUDIO/AndroidExample

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/911846.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【go从零单排】泛型(Generics)、链表

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在Go语言中&#xff0c;泛型&#xff08;Generics&#xff09;允许你编写可以处理…

vue项目删除无用的依赖

1.安装依赖检查工具 npm i depcheck2.查看无用的依赖 depcheck3.手动删除pageage.json中的无用的依赖&#xff08;如果有sass和sass-loader不要删&#xff0c;会引起项目报错&#xff09; 4.全部删除完成之后&#xff0c;删除package-lock.json文件&#xff0c;删除node_mod…

「Qt Widget中文示例指南」如何创建一个窗口标志?(一)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 窗口标志要么是类型…

移植 AWTK 到 纯血鸿蒙 (HarmonyOS NEXT) 系统 (9) - 编译现有的AWTK应用程序

AWTK 应用程序开发完成后&#xff0c;在配置文件中添加 harmonyos 的选项&#xff0c;通过create_project.py脚本即可生成 DevEco Studio的工程。 安装开发环境 DevEco Studio HarmonyOS 的开发工具。 Python 运行环境。 git 源码管理工具。 下载 awtk 和 awtk-harmonyos…

C++ 参数传递 笔记

目录 1、输入参数的传递方式-选择传值还是传引用&#xff1f; 处理用户信息 处理坐标 处理配置 处理ID 2、对于需要修改的参数,使用非const引用传递 有趣的例外&#xff1a;警惕表象的迷惑 需要警惕的陷阱 “糟糕”的update方法&#xff1a; “完美”的set_name与set…

【Eclipse系列】eclipse安装与常规配置(含插件)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、下载与安装 二、常规设置 1.1.设置工作空间(workspace) 1.2.设置字体和字体大小 ​编辑 1.3.设置编码 1.4.去除验证(validation) 1.5.去除单词验证(spelli…

内网对抗-信息收集篇SPN扫描DC定位角色区域定性服务探针安全防护凭据获取

知识点&#xff1a; 1、信息收集篇-网络架构-出网&角色&服务&成员 2、信息收集篇-安全防护-杀毒&防火墙&流量监控 3、信息收集篇-密码凭据-系统&工具&网站&网络域渗透的信息收集&#xff1a; 在攻防演练中&#xff0c;当完成边界突破后进入内…

C语言 | Leetcode C语言题解之第540题有序数组中的单一元素

题目&#xff1a; 题解&#xff1a; int singleNonDuplicate(int* nums, int numsSize) {int low 0, high numsSize - 1;while (low < high) {int mid (high - low) / 2 low;mid - mid & 1;if (nums[mid] nums[mid 1]) {low mid 2;} else {high mid;}}return …

【开发】关于Java中String与Integer的小小知识点(使用等号对比引用对象)

一个很简单的小知识点 我们都知道&#xff0c;如果使用对比包装类型或对象&#xff0c;那么比较的都是两者之间的地址&#xff08;指针或句柄&#xff09;&#xff0c;而非对象本身&#xff0c;那么且看下方的代码。 public class A {public static void main(String[] args)…

纯前端实现在线预览excel文件(插件: LuckyExcel、Luckysheet)

概述 在实际开发中&#xff0c;遇到需要在线预览各种文件的需求&#xff0c;最近遇到在线预览excel文件的需求&#xff0c;在此记录一下&#xff01;本文主要功能实现&#xff0c;用于插件 LuckyExcel &#xff0c;Luckysheet&#xff01;废话不多说&#xff0c;上代码&#xf…

洛谷每日一题——B2143 进制转换、P1003 [NOIP2011 提高组] 铺地毯

B2143 进制转换 题目描述 进制转换 - 洛谷 运行代码 #include<stdio.h> int main(){int a,b,i0,j,num[20];char k[]{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F};scanf("%d",&a);scanf("%d",&b);do{i;num[i]a%b;aa/b;}while(a!0);printf("&qu…

Elasticsearch的自定义查询方法到底是啥?

Elasticsearch主要的目的就是查询&#xff0c;默认提供的查询方法是查询全部&#xff0c;不满足我们的需求&#xff0c;可以定义查询方法 自定义查询方法 单条件查询 我们查询的需求&#xff1a;从title中查询所有包含"鼠标"这个分词的商品数据 SELECT * FROM it…

环境配置与搭建

安装pytorch 官网连链接&#xff1a;https://pytorch.org/ 特殊包名 cv2 pip install opencv-python sklearn pip install scikit-learnPIL pip install Pillow使用jupyter notebook pip install jupyter安装显卡驱动 Windows Linux 视频教程&#xff1a; 【ubuntu2…

jmeter常用配置元件介绍总结之函数助手

系列文章目录 1.windows、linux安装jmeter及设置中文显示 2.jmeter常用配置元件介绍总结之安装插件 3.jmeter常用配置元件介绍总结之取样器 jmeter常用配置元件介绍总结之函数助手 1.进入函数助手对话框2.常用函数的使用介绍2.1.RandomFromMultipleVars函数2.2.Random函数2.3.R…

【excel基本操作-sumif绝对引用和相对引用

低量级数据的存储 复杂且无法优化的数据报表 怎么学excel? 一、输入与输出 二、计算与处理 三、可视化 四、连接匹配与自动化 excel操作笔记 打开表格第一步筛选 所以筛选的快捷键&#xff1a;shiftctrll 排序&#xff1a;多列排序 开始-排序与筛选-自定义排序-设置关键字添…

【项目计划文档】软件项目计划书,项目总体计划(word原件)

项目开发计划包括项目描述、项目组织、成本预算、人力资源估算、设备资源计划、沟通计划、采购计划、风险计划、项目过程定义及项目的进度安排和里程碑、质量计划、数据管理计划、度量和分析计划、监控计划和培训计划等。 软件全套精华资料包清单部分文件列表&#xff1a; 工作…

音视频入门基础:FLV专题(23)——FFmpeg源码中,获取FLV文件音频信息的实现(下)

音视频入门基础&#xff1a;FLV专题系列文章&#xff1a; 音视频入门基础&#xff1a;FLV专题&#xff08;1&#xff09;——FLV官方文档下载 音视频入门基础&#xff1a;FLV专题&#xff08;2&#xff09;——使用FFmpeg命令生成flv文件 音视频入门基础&#xff1a;FLV专题…

模型 阿玛拉定律(炒作周期)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。短期乐观&#xff0c;长期低估。 1 阿玛拉定律的应用 1.1 全球定位系统&#xff08;GPS&#xff09;的发展 全球定位系统&#xff08;GPS&#xff09;的发展是阿玛拉定律的一个典型应用案例&#xf…

Kubernetes的概述与架构

Kubernetes 的概述 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统&#xff0c;其服务、支持和工具的使用范围广泛。 Kubernetes 这个名字源于…

【CAN通信】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、CAN通信简介二、CAN通信的逻辑电平分析三、CAN通信的差分信号线设计CAN标准数据帧格式四、设备发送数据优先级总结 一、CAN通信简介 CAN&#xff08;Controlle…