这篇主要是理解 gas assembly 指令指示符。 并手写 gas 汇编文件并与 c/c++ 交互
(感谢大家支持,我的被标记精华了,哈哈哈~)
大概在有几章就结束了,没有啥资料可以学了。
gas 指令指示符
我们修改上一章的代码,在之前的代码中 加入 全局变量 。
#include
#include "stdint.h"
// 测试全局变量 的 int64 和 string 类型
uint64_t glob_num = 100;
const char* glob_str = "sum:%d\n";
int add(int a,int b){
return a + b + glob_num;
}
int main(int argc, char const *argv[])
{
while (1)
{
int a = 10;
int b = 30;
int sum = add(a,b);
printf(glob_str,sum);
getchar();
}
/* code */
return 0;
}
生成 assembly
.text
.file "HelloAssembly.c"
.globl add // -- Begin function add
.p2align 2
.type add,@function
add: // @add
.cfi_startproc
// %bb.0:
sub sp, sp, #16
.cfi_def_cfa_offset 16
str w0, [sp, #12]
str w1, [sp, #8]
ldr w8, [sp, #12]
ldr w9, [sp, #8]
add w9, w8, w9
adrp x8, glob_num
ldr x8, [x8, :lo12:glob_num]
add x8, x8, w9, sxtw
mov w0, w8
add sp, sp, #16
ret
.Lfunc_end0:
.size add, .Lfunc_end0-add
.cfi_endproc
// -- End function
.globl main // -- Begin function main
.p2align 2
.type main,@function
main: // @main
.cfi_startproc
// %bb.0:
sub sp, sp, #48
stp x29, x30, [sp, #32] // 16-byte Folded Spill
add x29, sp, #32
.cfi_def_cfa w29, 16
.cfi_offset w30, -8
.cfi_offset w29, -16
stur wzr, [x29, #-4]
stur w0, [x29, #-8]
str x1, [sp, #16]
b .LBB1_1
.LBB1_1: // =>This Inner Loop Header: Depth=1
mov w8, #10
str w8, [sp, #12]
mov w8, #30
str w8, [sp, #8]
ldr w0, [sp, #12]
ldr w1, [sp, #8]
bl add
str w0, [sp, #4]
adrp x8, glob_str
ldr x0, [x8, :lo12:glob_str]
ldr w1, [sp, #4]
bl printf
bl getchar
b .LBB1_1
.Lfunc_end1:
.size main, .Lfunc_end1-main
.cfi_endproc
// -- End function
.type glob_num,home.php?mod=space&uid=245521 // @glob_num
.data
.globl glob_num
.p2align 3
glob_num:
.xword 100 // 0x64
.size glob_num, 8
.type .L.str,@object // @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "sum:%d\n"
.size .L.str, 8
.type glob_str,@object // @glob_str
.data
.globl glob_str
.p2align 3
glob_str:
.xword .L.str
.size glob_str, 8
.ident "Android (8490178, based on r450784d) clang version 14.0.6 (https://android.googlesource.com/toolchain/llvm-project 4c603efb0cca074e9238af8b4106c30add4418f6)"
.section ".note.GNU-stack","",@progbits
debug 信息
在生成的 assembly 中,经过我的测试,有几项指令应该是 用于调试 debug 使用的
.file 指令 应该表示汇编的文件
.cfi_指令以 cfi_ 开头的指令貌似用于描述 stack frame 栈帧信息,比较常见的就是
.cfi_startproc 方法的开始
.cfi_endproc 方法的结束
.cfi_def_cfa_offset 栈偏移
.cfi_endproc 栈结束
.section 描述段信息
.ident 构建信息
当然我测过了的,它们可以删除,删除后的样子如下:
.text
.globl add // -- Begin function add
.p2align 2
.type add,@function
add: // @add
// %bb.0:
sub sp, sp, #16
str w0, [sp, #12]
str w1, [sp, #8]
ldr w8, [sp, #12]
ldr w9, [sp, #8]
add w9, w8, w9
adrp x8, glob_num
ldr x8, [x8, :lo12:glob_num]
add x8, x8, w9, sxtw
mov w0, w8
add sp, sp, #16
ret
.Lfunc_end0:
.size add, .Lfunc_end0-add
// -- End function
.globl main // -- Begin function main
.p2align 2
.type main,@function
main: // @main
// %bb.0:
sub sp, sp, #48
stp x29, x30, [sp, #32] // 16-byte Folded Spill
add x29, sp, #32
stur wzr, [x29, #-4]
stur w0, [x29, #-8]
str x1, [sp, #16]
b .LBB1_1
.LBB1_1: // =>This Inner Loop Header: Depth=1
mov w8, #10
str w8, [sp, #12]
mov w8, #30
str w8, [sp, #8]
ldr w0, [sp, #12]
ldr w1, [sp, #8]
bl add
str w0, [sp, #4]
adrp x8, glob_str
ldr x0, [x8, :lo12:glob_str]
ldr w1, [sp, #4]
bl printf
bl getchar
b .LBB1_1
.Lfunc_end1:
.size main, .Lfunc_end1-main
// -- End function
.type glob_num,@object // @glob_num
.data
.globl glob_num
.p2align 3
glob_num:
.xword 100 // 0x64
.size glob_num, 8
.type .L.str,@object // @.str
.L.str:
.asciz "sum:%d\n"
.size .L.str, 8
.type glob_str,@object // @glob_str
.data
.globl glob_str
.p2align 3
glob_str:
.xword .L.str
.size glob_str, 8
add
我们先来分析 add 方法 以及它用到的指令
截取出来是这样的
.text
.globl add // -- Begin function add
.p2align 2
.type add,@function
add: // @add
// %bb.0:
sub sp, sp, #16
str w0, [sp, #12]
str w1, [sp, #8]
ldr w8, [sp, #12]
ldr w9, [sp, #8]
add w9, w8, w9
adrp x8, glob_num
ldr x8, [x8, :lo12:glob_num]
add x8, x8, w9, sxtw
mov w0, w8
add sp, sp, #16
ret
.Lfunc_end0:
.size add, .Lfunc_end0-add
我们依次从上往下分析
text section
.text 表示代码开始的 代码段
global symbol
.global 代表声明全局符号 可提供外部使用
.globl add 代表定义 add 的全局symbol
p2align
表示内存对齐
p2align 2 表示对齐 2*2 = 4 字节的内存
.type
定义符号类型
.type add,home.php?mod=space&uid=1435519表示 add symbol 是一个 方法
add arm
接下来是分析 当中的 arm 汇编。里面存在 伪指令引用 全局符号 glob_num 看我的注释意思即可
add:
# 开辟局部变量的栈空间
sub sp, sp, #16
# 将 a 和 b 的参数 放入 栈空间
str w0, [sp, #12]
str w1, [sp, #8]
# 获取 a 和 b
ldr w8, [sp, #12]
ldr w9, [sp, #8]
# a + b
add w9, w8, w9
# 伪指令 获取基于 pc + glob_num 的 4kb 内存页对齐
adrp x8, glob_num
# :lo12: 表示取 低12位内的glob_num地址 也就是4kb内的偏移
ldr x8, [x8, :lo12:glob_num]
# 将 glob_num + a + b (sxtw 表示 x8 转 w8 也就是 word 32 位)
add x8, x8, w9, sxtw
# 剩下就是 返回值 和 恢复栈了
mov w0, w8
add sp, sp, #16
ret
对应 c 的代码:
int add(int a,int b){
return a + b + glob_num;
}
方法结束
.Lfunc_end0: 表示结束方法的标签,这个标签名字可以自定义
.size 表示这个方法的大小
.Lfunc_end0:
# add 方法的大小 = Lfunc_end0 - add开始地址
.size add, .Lfunc_end0-add
main
至于 main 方法 指令都见过,我就直接分析了
# 定义main symbol
.globl main // -- Begin function main
# 4 字节 内存对齐
.p2align 2
# main symbol 类型是 方法
.type main,@function
main: // @main
# 分配栈空间
sub sp, sp, #48
# x29(FP) x30(LR) 入栈 方法结束时候恢复
stp x29, x30, [sp, #32] // 16-byte Folded Spill
# 表示 x29 以栈低开始
add x29, sp, #32
# wzr(zero) 用于清零 [x29 - 4] = 0
stur wzr, [x29, #-4]
# [x29 -8] = w0
stur w0, [x29, #-8]
# [sp + 16] = x1
str x1, [sp, #16]
# 跳入死循环
b .LBB1_1
.LBB1_1: // =>This Inner Loop Header: Depth=1
# a = 10
mov w8, #10
str w8, [sp, #12]
# b = 30
mov w8, #30
str w8, [sp, #8]
# add方法 传参 a b
ldr w0, [sp, #12]
ldr w1, [sp, #8]
# 调用 add
bl add
# w0 = 返回值
str w0, [sp, #4]
# 获取 glob_str 4kb 内存页地址
adrp x8, glob_str
# printf方法 传参
# 获取 glob_str 低12(4kb 内) 位的偏移
ldr x0, [x8, :lo12:glob_str]
# add方法 返回值
ldr w1, [sp, #4]
bl printf
bl getchar
# 重复循环
b .LBB1_1
.Lfunc_end1:
# main 方法结束 size = Lfunc_end1-main
.size main, .Lfunc_end1-main
全局变量
定义全局变量一般格式为:
# 符号类型为 @object
.type 符号,@object
# 数据段 还有一个就是 text 代码段
.data
# 全局符号
.globl glob_num
# 内存对齐 看数据长度
.p2align 3
glob_num
# glob_num 符号是 对象类型
.type glob_num,@object // @glob_num
# 数据段
.data
# 定义 glob_num 全局符号
.globl glob_num
# 2^3 = 8 字节内存对齐
.p2align 3
# glob_num 符号的数据定义
glob_num:
# 使用 64 位 整数 值是 100
# 当然还有 个 word 是 32位
.xword 100 // 0x64
# 数据大小是 8字节
.size glob_num, 8
glob_str
这个比较麻烦了,因为它是个字符串。
# 定义本地标签数据段 不会被外部引用
.L.str:
# ascii 字符串数据
.asciz "sum:%d\n"
# 长度是8
.size .L.str, 8
# 定于 glob_str 符号类型是 @object
.type glob_str,@object // @glob_str
# 数据段
.data
# 全局 symol
.globl glob_str
# 8 字节对齐
.p2align 3
glob_str:
# 引用 .L.str 数据
.xword .L.str
# 长度是 8
.size glob_str, 8
到这里分析就差不多了。接下来我们就尝试手动写 add assembly 方法,并提供给 c/c++ 使用
手写 gas 文件
为了方便,您先下载 gnu assembler 插件 可以帮助您完成部分 intellSence
首先我创建一个 assembly_add.S 的 gas 汇编文件
并写入 add 方法的汇编
.text
.globl add
.p2align 2
.type add,@function
add:
sub sp,sp,#16
// 第一个参数
str w0,[sp,#12]
// 第二个参数
str w1,[sp,#8]
ldr w0,[sp,#12]
ldr w1,[sp,#8]
// a + b
add w0,w0,w1
// 还原栈
add sp,sp,#16
ret
.Lfunc_end0:
//方法结束 size = 当前 - add
.size add, .-add
在写个 assembly_add_demo1.cpp
#include "stdio.h"
// extern C 表示方法是标准C的
// 且 方法是从外部链接过来的
extern "C"{
int add(int a,int b);
}
int main(int argc, char const *argv[])
{
int a = 99;
int b = 1;
int ret = add(a,b);
printf("ret:%d",ret);
return 0;
}
接下来在 CMakeLists.txt 中加入如下片段:
set(assmbly_add_demo1
./source/assembly/assembly_add.S
./source/assembly/aseembly_add_demo1.cpp
)
# 测试 gas 汇编文件与 c++ 交互
add_executable(assmbl-add-demo1 ${assmbly_add_demo1})
然后修改 ndb_builder.py 和 adb_execute_arm64.bat 方法进行构建和运行
至此,您已经会 如何在 c/c++ 中写 arm 了
下一章就开始讲解手写 inline hook 了