分享者才是学习中最大的受益者!
前言
安卓学习(一)
安卓学习(二)
安卓学习思路方法总结(三)
安卓学习思路方法总结(四)
安卓学习思路方法总结(五)
NDK动态
Eclipse创建工程
然后一路默认 Finish即可
这个 前面讲过
去掉Hello world
我们今天的目标是做一个计算机
计算机
放入两个Plain Test
在放入两个TestView
选中之后 修改文本
导入四个Button 做运算符
同样修改名字
进入xml底层 进行查看
双击即可
修改ID信息
+:add
-:sub
*:mul
/:div
进行保存
把位置重新规划好
Java代码编写
创建一个方法进行初始化控件
init()
初始化控件
绑定编辑框的变量
定义first变量
绑定编辑框
定义好类型:EditText
对应xml文件
定义第二个编辑框
对应xml文档
然后绑定Button类型的运算符
对应xml文档
一样的方法 绑定其他三个运算符
对应xml文档
定义两个全局变量
定义native方法
native
方法的实现是在so
层
获取编辑框中的值
one
使用getText
转到toString
two也是一样的
定义运算方法
监听绑定按钮
new一个OnClickListener
通过onClick进行监听
逻辑是通过ID来执行命令操作
接收按钮Id
key->v中getId的按钮
value是add按钮的ID
继续添加其他三个运算符
显示界面
需要去调用方法Toast
需要三个参数
之前讲述过 这里就不展开说了
context参数:上下文使用 MainActivity中的
text参数:我们获取的add(one,tWo)
duration参数:使用show()进行显示
注:add这个类型是float
类型,需要转换为string
类型
加个字符串即可
进行其他三个运算符的显示
默认值的绑定
使用OnClickListener
进行默认值的绑定
定义变量cli
修饰符:final
我们在加减乘除的时候 都要用到编辑框
最后把编辑框放入case
中
保存 即可
JNI层编写
创建JNI文件夹
创建.c
文件
写入头文件
jfloat
使用jfloat 前两个参数是固定的 查万能表即可
然后使用方法add 后面两个是传入的参数
jfloat add(JNIEvn* env, jobject obj, jfloat a, jfloat b)
方法体写入+
一样的方法写入其他三个 运算符
注:
方法名是不需要一样的
因为会使用JNI的接口进行两边函数的关联
C层和Java层的绑定
使用结构体定义结构体数组:JNINativeMethod
JNINativeMethod nativeMethod[]{};
写入结构体:struct
查万能表
//定义一个动态注册JNINativeMethod结构体,这个与动态注册有关,里面有三个元素
typedef struct {
const char* name; //第一个参数 name 是java 方法名;
const char* signature; //第二个参数 signature 用于描述方法的参数与返回值,也就是java方法签名信息,
void* fnPtr; //第三个参数 fnPtr 是函数指针,指向 jni 函数;
} JNINativeMethod;
可以看到需要三个参数
1、java方法名称
2、参数、返回值、签名信息
3、函数指针
{"add","(FF)F",(void*)add},
{"sub","(FF)F",(void*)sub},
{"mul","(FF)F",(void*)mul},
{"div","(FF)F",(void*)div},
定义函数
使用RegisterNatives
查万能表
jint(*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
四个参数 进行指出
第二个参数jclass 需要调用FindClass
重新定义一个方法aaa
继续指出,定义参数,被修饰的方法的类路径
第三个参数 是定义的数组名称
第四个参数 是数组的数量
条件判断
写入一个if条件判断
和0比较
满足条件,返回-1
不满足条件,返回0
查万能表可知
#define JNI_OK (0)#define JNI_ERR (-1)
注:这里的数组个数jnit
为4位
严谨的写法是:
sizeof(nativeMethod)/sizeof(nativeMethod[0])
这个时候 注册函数 就写完了
写动态注册
JNI_Onload
继续 开始
写动态注册
使用JNI_Onload
查万能表可知
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
给方法体定义一个jni
jint(*GetEnv)(JavaVM*, void**, jint);
GetEnv
有三个参数
第一个参数VM:补全第二个参数void**:二级指针,指向(&)env的地址第三个参数jint:版本
最后 还需要进行指出
条件判断
判断env是否获取成功
不等于0就返回-1
继续对注册函数 进行条件判断
不等于0就返回-1
加上env参数
最后添加一个返回版本
so库
编写两个.mk
文件即可
把之前的拿过来 就可以
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE:= jsq#模块名称 LOCAL_SRC_FILES := jsq.c #源文件 .c或者.cppLOCAL_ARM_MODE := arm #编译后的指令集 ARM指令LOCAL_LDLIBS += -llog #依赖库include $(BUILD_SHARED_LIBRARY) #指定编译文件的类型
注:so库要调用的编译文件类型是:BUILD_SHARED_LIBRARY
Application.mk
APP_ABI := x86 armeabi-v7aAPP_PLATFORM := android-8
简单排错
编译
刷新
java层调用so库
ARM汇编
要去熟悉so库了,冲冲冲
丢入一个so文件
so
文件的位置:一个项目的libs目录下
进制转换对应表
一、跳转指令
B:表示程序跳转
1.B:无条件跳转2.BL:带链接的无条件跳转3.BX:带状态切换的无条件跳转 根据目标地址最低位切换状态(arm/thumb)4.BLX:带链接和状态切换的无条件跳转5.B 1oc_:地址6.BEQ,BNE
二、存储器与寄存器交互数据指令核心
存储器、寄存器:用来存储数据
小数据:寄存器;大数据:栈
存储器(主存,内存)寄存器中放的数据:可以是字符串,可以是数,也可以是一个地址,它可以放各种类型的数据存储地址单元:地址与地址中存在的值
1、LDR、STR
LDR:从存储器中加载数据到寄存器 ⬅ Load(从右到左)
STR:将寄存器的数据存储到存储器 ➡ Store(从左往右)
LDR R8,[R9,#4]
R8为待加载数据的寄存器,加载值为R9+0×4
所指向的存储单元
就是把右边放到左边
[]如果没括号就是直接把值直接放入R8
举个例子
LDR PC, [SP+4+var_4]
解析
SP 栈指针寄存器,地址数据SP+4+var_4将被放进PCSTR R8,[R9,#4]:和LDR相反将R8寄存器的数据存储到R9+0x4指向的存储单元
2、LDM、STM
LDM:将存储器的数据加载到一个寄存器列表 ➡ (从左往右)LDM RO,{R1-R3}将R0指向的存储单元的数据`依次`加载到R1,R2,R3寄存器LDR:从存储器中加载数据到寄存器←Load LDR:用在函数快结尾的地方,添加对象的地方STM:将一个寄存器列表的数据存储到指定的存储器 ⬅(从右到左)
3、PUSH、POP
PUSH:将寄存器值推入堆栈 压栈POP:将堆栈值推出到寄存器 岀栈
4、SWP SWP
SWP:将寄存器与存储器之间的数据进行交换SWP R1,R1 [RO]将R1寄存器与R0指向的存储单元的内容进行交换堆,队列:数据结构,栈是竖的,后进先岀,且只能从栈顶依次填入数据
三、数据传送指令
MOV:move MOV:将立即数或寄存器的数据传送到目标寄存器MOV RO,#8 R0=8把后面的#8值给前面的RO
四、数据算术运算指令
ADD(+),SUB(-),MUL(*),DIV(/)涉及到:有符号,无符号运算,带进位运算
五、数据逻辑运算指令
与:AND全1出1两边为真,结果才为真或:ORR有1出1有一个为真,结果就为真异或:EOR 相同为零,不同为1如果两个值相等就为零,不相等就为1
移位:实质是乘,除,类似于小数点移位但与平常算数相反,小数点左移,数变小;小数点右移,数变大
逻辑移位小数点左移数变大,小数点右移数变小,且按2的倍数进行,因为是2进制LSL:逻辑左移LSR:逻辑右移LSL R0,R1҅#2 R0=R1*4 LSR R0,R1҅#2 R0=R1*2
六、比较指令
CMP:比较CMP R0 #0 R0寄存器中的值与0比较标志位:如z位,这个都可以在动态调试时,寄存器窗口看到
七、其他指令
协处理器指令:SWT(切换用户模式、伪指令:DCB
八、寄存器寻址方式
用到的是数据传送指令
立即寻址:MOV RO,#1234R0=0×1234寄存器寻址:MOV R0,R1 R0=R1寄存器移位寻址:MOV RO,R1,LSL #2R0=R1*4寄存器间接寻址:LDR R0,[R1]将R1寄存器中的值作为地址,取出地址中的值赋R0寄存器间接基址偏移寻址:LDR R0,[R1,#-4]将R1寄存器的值(-0×4)的值作为地址,取出地址中的值给RO
简单分析so库
1、从存储器中加载括号里面的RO值给R3寄存器中2、没括号就是数据,把括号內的数据直接放入R1寄存器里面3、把{R4,LR}这两个寄存器里面的值依次放到SP指向的地址,有STM就会有LDM对应4、ADD是加,R1+PC在把值放入R1里面5、把(R3+#0x18)数据作为一个地址,然后取这个地址里面的值放入R3里面6、MOV把R0里面的值放入R4里面7、BLX是进行跳转8、把R4作为一个地址取地址里面的值,放入R129、把(byte_DA4)数据放入R2里面10、MOV这里是把#4的值放入R3里11、BLX进行跳转,SUB减12、最后MOV给值,LDM返回
ARM处理器寻址方式
前言
寻址方式是根据指令中给出的地址码字段来实现寻找真实操作数地址的方式
了解其中一些基础的
1、寄存器寻址
把R2值给R1,或者是把R1给R2在给R0
2、立即寻址
立即数,只能放在寄存器里面存放,立即数要以#
为前缀,表示16进制的0x
3、寄存器偏移寻址
LSR:逻辑右移,寄存器中字的高端空出的位补0ASR:算术右移,移位过程中保持符号位不变,即如果源操作数为正数,则字的高端空出的位补0,否则补1ROR:循环右移,由字的低端移出的位填入字的高端空岀的位RRX:带扩展的循环右移,操作数右移一位,高端空出的位用原C标志值填充。
4、寄存器间接寻址
第二个参数都是方括号:[]
一个地址
直接取这个地址叫:直接寻址
取在该地址中取某个值叫:间接寻址
5、基址寻址
:#
后数字如果是1,就取该地址加1后的数值
后数字如果是-1,就是数值減1作为地址
6、多寄存器寻址
多寄存器寻址就是一次可以传送几个寄存器值,允许一条指令传送16个寄存器的任何子集或所有寄存器。多寄存器寻址指令举例如下:LDMIA R1!,{R2-R7,R12}->将R1单元中的数据读出到R2-R7,R12,R1自动加1STMIA R0!,{R3-R6,R10}->将R3-R6,R10中的数据保存到R0指向的地址,R0自动加1
使用多寄存器寻址指令时,寄存器子集的顺序时由小到大的顺序排列,连续的寄存器可用一
连接,否则,用,
分隔书写
简单来讲
就是需要跳转的时候,把寄存器的值放在一个特定的寄存器里面,把这些值暂时存放起来
执行完特定的函数在将特定的寄存器里面的值取出来,然后在执行下一步操作
OK 这次就到这里
- 本文作者: 略略略
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/662
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!