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.assemblyimport 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?): Stringoverride 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;
#endifLOGI("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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Spring Boot 常用注解总结

在 Java 开发中&#xff0c;Spring 和 Spring Boot 框架广泛应用于企业级应用开发。这两个框架提供了丰富的注解&#xff0c;使得开发更加高效和便捷。本文将对 Spring 和 Spring Boot 中常用的注解进行总结。 一、Spring 常用注解 1. Component 作用&#xff1a;用于将普通的…

安装宝塔 Windows 面板

操作场景 宝塔面板是一款使用很方便、功能强大、交互友好且终身免费的服务器管理软件&#xff0c;支持 Linux 与 Windows 系统。在宝塔面板中&#xff0c;您可以一键配置 LAMP、LNMP、网站、数据库、FTP、SSL&#xff0c;还可以通过 Web 端轻松管理服务器。 本文介绍如何在 W…

Ubuntu 的 ROS 操作系统 turtlebot3 gazebo仿真

引言 TurtleBot3 Gazebo仿真环境是一个非常强大的工具&#xff0c;能够帮助开发者在虚拟环境中测试和验证机器人算法。 Gazebo是一个开源的3D机器人仿真平台&#xff0c;它能支持物理引擎&#xff0c;允许机器人在虚拟环境中模拟和测试。结合ROS&#xff0c;它能提供一个完整的…

计算器上的MC、MR、M+、M—、CE是什么意思?

在计算器中&#xff0c; MC键叫做memory clear&#xff0c;中文 清除存储&#xff0c;是一个清除寄存器中存储数字的指令。 MS键叫做memory save&#xff0c;中文 存入存储。 而MR键&#xff0c;则是一个读取原先存储在寄存器中的数字的指令。 M键指将当前数值存入寄存器以…

MacOs上如何彻底卸载DevEco Studio?

在Mac上彻底卸载DevEco Studio&#xff0c;你可以执行以下步骤&#xff1a; 打开Finder。 按下Command Shift G&#xff0c;输入~/Library/Application Support/&#xff0c;然后按Enter。 删除Huawei文件夹。 打开终端应用程序。 执行以下命令来删除DevEco Studio的相关…

「IDE」集成开发环境专栏目录大纲

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「IDE」集成开发环境&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定…

Git - 日志

记录常用的日志查询方式及格式 git log commit <commitID> Author: <user><<email>> Date: <time><info>Change-Id: <changeID>git log -p # 在 git log 的基础上追加补丁信息 diff --git <file> <file> index 54f4…

Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)

作者&#xff1a;来自 Elastic Benjamin Trent Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)。 嵌入模型输出 float32 向量&#xff0c;通常对于高效处理和实际应用来说太大。Elasticsearch 支持 int8 标量量化&#xff0c;以减小向量大小&#xff0c;同时保持性能。其他…

Odoo:免费开源的钢铁冶金行业ERP管理系统

文 / 开源智造 Odoo亚太金牌服务 简介 Odoo免费开源ERP集成计质量设备大宗原料采购&#xff0c;备件设材全生命周期&#xff0c;多业务模式货控销售&#xff0c;全要素追溯单品&#xff0c;无人值守计量物流&#xff0c;大宗贸易交易和精细化成本管理等方案&#xff1b;覆盖…

Linux设置socks代理

公司里绝大多数主机已经禁止外网访问&#xff0c;仅保留一台主机设置socks作为代理服务器。如下为对socks这一概念的学习整理 什么是socks 是一种OSI模型下会话层的协议&#xff0c;位于表示层与传输层之间&#xff0c;作用是&#xff1a; exchanges network packets between…

MySQL数据库:SQL语言入门 (学习笔记)

SQL&#xff08;Structured Query Language&#xff09;是结构化查询语言的简称&#xff0c;它是一种数据库查询和程序设计语言&#xff0c;同时也是目前使用最广泛的关系型数据库操作语言。&#xff08;95%适用于所有关系型数据库&#xff09; 【 SQL是关系型数据库通用的操作…

视频会议接入GB28181视频指挥调度,语音对讲方案

传统的视频会议指挥调度系统目前主流的互联网会议大部分都是私有协议&#xff0c;功能都很独立。目前主流的视频监控国标都最GB平台&#xff0c;新的需求要求融合平台要接入监控等设备&#xff0c;并能实现观看监控接入会议&#xff0c;实时语音设备指挥现场工作人员办公实施。…

Objective-C 1.0和2.0有什么区别?

Objective-C ObjC比较小众&#xff0c;在1980年左右由Stepstone公司的Brad Cox和Tom Love发明。后来NeXT公司获得ObjC语言使用权&#xff0c;再后来到1996年NeXT被苹果公司收购也变成苹果公司使用&#xff0c;Mac市场占有率本身就不高&#xff0c;ObjC没有太多程序员。在移动互…

go reflect 反射

目录 一、反射 1、reflect.Type 和 reflect.Value 2、rtype 和 rvalue 3、reflect.TypeOf 工作原理 4、reflect.ValueOf 工作原理 5、reflect.ValueOf 与 reflect.TypeOf 比较 6、性能优化建议 二、问题&#xff1a; 1、静态类型和动态类型 2、值类型与引用类型 &…

萤石设备视频接入平台EasyCVR海康私有化视频平台监控硬盘和普通硬盘有何区别?

在现代安防监控领域&#xff0c;对于数据存储和视频处理的需求日益增长&#xff0c;特别是在需要长时间、高稳定性监控的环境中&#xff0c;选择合适的存储设备和监控系统显得尤为重要。本文将深入探讨监控硬盘与普通硬盘的区别&#xff0c;并详细介绍海康私有化视频平台EasyCV…

一学就废|Python基础碎片,字符串编码

Unicode 万国码 在 Python 3 中&#xff0c;字符串由 Unicode 表示&#xff0c;而不是字节。ASCII 码是定义字符数字代码的最著名的标准。数字值最初只定义 128 个字符&#xff0c;因此 ASCII 只包含控制代码、数字、小写字母、大写字母等。然而&#xff0c;我们不足以表示世界…

npm list @types/node 命令用于列出当前项目中 @types/node 包及其依赖关系

文章目录 作用示例常用选项示例命令注意事项 1、实战举例**解决方法**1. **锁定唯一的 types/node 版本**2. **清理依赖并重新安装**3. **设置 tsconfig.json 的 types**4. **验证 Promise 类型支持** **总结** npm list types/node 命令用于列出当前项目中 types/node 包及其…

Qt--命令行终端程序开发

提示&#xff1a;本文为学习记录&#xff0c;若有错误&#xff0c;请联系作者&#xff0c;谦虚受教。 文章目录 前言一、头文件二、cpp文件三、使用流程如图所示 总结 前言 Constant dropping wears the stone. 一、头文件 #ifndef TERMINALWIDGET_H #define TERMINALWIDGET_…

shell脚本基本概念讲解

文章目录 &#x1f34a;自我介绍&#x1f34a;脚本概述shell脚本的运行方法test-1.sh &#x1f34a;shell中的变量test2.shtest3.shtest4.sh 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1…

【Linux】常用命令(2.6万字汇总)

文章目录 Linux常用命令汇总1. 基础知识1.1. Linux系统命令行的含义1.2. 命令的组成 2. 基础知识2.1. 关闭系统2.2. 关闭重启2.3. 帮助命令&#xff08;help&#xff09;2.4. 命令说明书&#xff08;man&#xff09;2.5. 切换用户&#xff08;su&#xff09;2.6.历史指令 3.目录…