android ARM 汇编学习 —— hello world
android ARM 汇编学习—— 在 android 设备上编译c/cpp代码并用objdump/readelf等工具分析
adb putty 连上手机,用busybox vi 写一个 helloworld c
root@HM2014813:/data/local/tmp # busybox vi hello.c
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Hello ARM World\n");
return 0;
}
用 gcc 编译成汇编,并查看
root@HM2014813:/data/local/tmp # gcc hello.c -S && cat ./hello.s
.arch armv5te
.fpu softvfp
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 34, 0
.eabi_attribute 18, 4
.file "hello.c"
.section .rodata
.align 2
.LC0:
.ascii "Hello ARM World\000"
.text
.align 2
.global main
.type main, %function
main:
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 1, uses_anonymous_args = 0
stmfd sp!, {fp, lr}
add fp, sp, #4
sub sp, sp, #8
str r0, [fp, #-8]
str r1, [fp, #-12]
ldr r3, .L3
.LPIC0:
add r3, pc, r3
mov r0, r3
bl puts(PLT)
mov r3, #0
mov r0, r3
sub sp, fp, #4
@ sp needed
ldmfd sp!, {fp, pc}
.L4:
.align 2
.L3:
.word .LC0-(.LPIC0+8)
.size main, .-main
.ident "GCC: (GNU) 4.8.2"
.section .note.GNU-stack,"",%progbits
下面开始分析:
.arch armv5te
.fpu softvfp
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 34, 0
.eabi_attribute 18, 4
.file "hello.c"
开始这几行是给 ARM cpu 的声明,.arch 指明体系架构类型, .fpu 指明 Floating Point Unit 浮点运算单元的运算模式,接下去几行是 ARM Embedded Application Binary Interface EABI 的 option, 最后一行指明文件名称
.section .rodata
.align 2
.LC0:
.ascii "Hello ARM World\000"
c code 里的字符串 "Hello ARM World\n" 编译后存入 .rodata section 。
.text
.align 2
.global main
.type main, %function
main:
上面这几行是函数声明,在 .text section , 函数名是 main
.LC0:
.ascii "Hello ARM World\000" ; label .LC0 对应的内存地址存放字符串,或者说 .LC0 存放的是字符串的首地址
.text
.align 2
.global main
.type main, %function
stmfd sp!, {fp, lr} ; 压栈,栈向地地址生长,sp 保存着栈顶地址, fp 是帧指针,lr 是返回地址
; fp : 在C程序编译过程中,函数局部变量被分配在一个连续存储区内,这个连续存储区就是该函数的帧
; 这句指令效果是,帧指针被保存在栈顶位置,lr 指针被保存在地址为 (fp - 4Byte) 的位置上(因为一条指令是4Byte),然后sp生长2个指针长度,即往低地址移动 8 个字节
add fp, sp, #4 ; fp = sp + 4
sub sp, sp, #8 ; sp = sp - 8, 这一句和接下来给 r0,r1赋值的两句都没看懂
str r0, [fp, #-8]
str r1, [fp, #-12]
ldr r3, .L3 ; 将 label .L3 的值赋给 r3 , .L3 是一个label, 可以理解为一个地址, 这个地址是这么算的 .LC0-(.LPIC0+8)
; .LC0 是要打印的字符串的地址, .LPIC0 其实是指令 ‘add r3,pc,r3’ 的地址, +8 即往前越2条指令,所以其实是指令 'bl puts(PLT)' 的地址,即调用printf函数的地址
; 结合起来看, .L3 保存的是要打印的字符串的地址和跳转指令的地址的 offset, 这个 offset 的作用是? 这个 offset 加上 pc 之后就是字符串的地址
.LPIC0:
add r3, pc, r3 ; ArmV5 的流水线是3,意味着执行这条指令的时候,pc 的值指向的指令是 bl puts(PLT), r3 即 .L3 , 加起来就是要打印的字符串的地址
mov r0, r3 ; 将字符串地址保存在 r0
bl puts(PLT) ; 打印
mov r3, #0
mov r0, r3 ; return 0
sub sp, fp, #4
@ sp needed
ldmfd sp!, {fp, pc} ; 恢复pc寄存器,跳出函数
.L4:
.align 2
.L3:
.word .LC0-(.LPIC0+8)
.size main, .-main
.ident "GCC: (GNU) 4.8.2"
.section .note.GNU-stack,"",%progbits
上面这个过程比较难理解的是,传给printf的字符串的地址是动态算出来的, .LC0-(.LPIC0+8) + pc ,为什么这么做,网上找了个大概靠谱的解释:http://tieba.baidu.com/p/2983668645
ARM立即数在我的印象里最大是4096再大就只能相对寻址,显然所有的指针都只能间接寻址,也就是需要一个内存来存地址那就是L3。。。。L3是个常量,存LPIC0到字符串LC0的相对位置,只要PC+该相对位置就是字符串位置,因为ARM9是3级流水线一次区取条指令,你当前的PC位置并不是紧接的下一条,而是下3条的位置,通常单片机是1级流水线当前PC就是下条的位置,不用加减任何数,但ARM9需要+8 如果是ARM11的五级流水线加的更多。。。。
ARM不能像单片机那样,想取某个标签地址,就可以 mov r1,#标签 因为ARM立即数寻址有限制。。。所有的指针都会超过限制,所以会用另一种方式直接算出寻址位置的地址和
全局变量位置的相对地址,在调用时用PC+相对地址即可 但要 PC+(相对地址-8), 因为
仪的在减号后面的括号里面所以 按结合律规则 -8编程+8
参考 :
Android ARM Assembly: Registers, Memory and Addressing Modes
ARMv7匯編代碼分析
android ARM 汇编学习 —— hello world的更多相关文章
- android ARM 汇编学习—— 在 android 设备上编译c/cpp代码并用objdump/readelf等工具分析
学习 android 逆向分析过程中,需要学习 Arm 指令,不可避免要编写一些 test code 并分析其指令,这是这篇文档的背景. 在目前 android 提供的开发环境里,如果要编写 c / ...
- arm汇编学习(五)
新增个手写GNU语法arm的方法,以后可以狂逆狂写 hello.S文件 .data msg: .ascii "Hello, ARM!\n" len = . - msg .text ...
- arm汇编学习(四)
一.android jni实现1.静态实现jni:先由Java得到本地方法的声明,然后再通过JNI实现该声明方法.2.动态实现jni:先通过JNI重载JNI_OnLoad()实现本地方法,然后直接在J ...
- arm汇编学习(三)
一.ndk编译android上运行的c程序 新建个hello目录,底下要有jni目录,下面就是Android.mk文件 1.Android.mk文件内容如下: LOCAL_PATH:= $(call ...
- ARM汇编学习笔记
ARM RISC (Reduced Instruction Set Computers) X86 CISC (Complex Instruction Set Computers) ...
- Android ARM指令学习
在逆向分析Android APK的时候,往往需要分析它的.so文件.这个.so文件就是Linux的动态链接库,只不过是在ARM-cpu下编译的.所以学习Android下的ARM指令很重要.目前,市面上 ...
- arm汇编学习(六)---跳转到thumb状态
通常函数返回使用 pop {r7,pc}或bx lr等方式(bx,b类似jmp为跳转指令,但bx可以指定跳转区域究竟为thumb还是arm指令.thumb指令指令的时候,直接填写该地址却总是产生SIG ...
- ARM 汇编学习笔记
- ARM学习笔记11——GNU ARM汇编程序设计
GNU ARM汇编程序设计中,每行的语法格式如下: [<label>:] [<instruction | directive | pseudo-instruction>] @c ...
随机推荐
- virsh查看迁移信息的两个命令
virsh qemu-monitor-command uuid --hmp info migrate, 能看到迁移所设置的相关参数,和进度相关信息: virsh domjobinfo uuid命令, ...
- NLP系列-中文分词(基于统计)
上文已经介绍了基于词典的中文分词,现在让我们来看一下基于统计的中文分词. 统计分词: 统计分词的主要思想是把每个词看做是由字组成的,如果相连的字在不同文本中出现的次数越多,就证明这段相连的字很有可能就 ...
- php+Mysql 页面登录代码
登录界面设置: <?php/** * Created by xx. * User: msi * Date: 2017/10/26 * Time: 18:12 *///session每次用之前都要 ...
- mysql 数据库新增用户
1.user表中host为%含义: Host列指定了允许用户登录所使用的IP,比如user=root Host=192.168.1.1.这里的意思就是说root用户只能通过192.168.1.1的客户 ...
- Dispose的调用顺序
非托管资源的释放顺序. 这是应该先释放 reader 再释放 stream. 或者直接使用using,防止出错 .
- java对数组的操作
1 拷贝数组 数组全拷贝 数组定位拷贝 2 判断数组是否相等(每个元素都对应相等) 3 数组和集合的相互转化 import java.util.Arrays; import java.util.Lis ...
- HighCharts中几种tooltip的显示格式
推荐学习地址 => https://www.hcharts.cn/docs/basic-tooltip https://api.hcharts.cn/#Highcharts.numberFo ...
- setCharacterEncoding 是在request.getParameter获取参数之前 设置request的编码格式 一步到位
setCharacterEncoding 是在request.getParameter获取参数之前 设置request的编码格式 一步到位
- requestAnimationFrame实现一帧的函数节流
用一个变量判断raf的回调是否已经执行了,已经执行了说明过了一帧,通常是16.7ms,达到了函数节流一帧的目的. var locked = false; window.addEventListense ...
- 东北育才冲刺noip(day9)
这十天来呢,感觉自己进步很大,(虽然被碾压的很惨),看到了自己以前完全没见过,也没想过的算法,打开新世界的大门. 同时呢,也感觉自己太弱了,于是就注册了这个博客. 为了促进进步,在这里立下flag,我 ...