C函数调用和汇编代码分析

risc-v C函数调用反汇编示例

首先有一段C程序,其中main函数调用子函数add

1
2
3
4
5
6
7
//main.c 
int add(int a, int b){
return a + b ;
}
int main(void){
return add(2, 3);
}

C语言指针注解

C语言指针实例

指针的类型可以是int,double,float,struct …. 决定指针内部存放的数据大小,
如果定义的是一个数组,类型可以让编译器 在p++是跳多少个大小的地址

RISCV 矢量处理器

RISC-V向量的工作过程

  • setp1 : 首先要确定向量的类型

    比如你要做一个向量运算 Y = a*X + Y ,如果X, Y 都是双精度的浮点的向量,那么就需要申请两个F64类型的向量v0和v1,
    如果是 Z = a*X + Y ,同样X,Y是双精度的浮点的向量,那么就需要至少申请3个F64类型的向量v0和v1,v2分别给X,Y,Z使用
    RV32V 向量寄存器类型的编码如下

Type Floating Poing Signed Interger Unsigned Interger
Width Name vetype Name vetype Name vetype
8bit X8 10100 X8U 11100
16bit F16 01101 X16 10101 X16U 11101
32bits F32 01110 X32 10110 X32U 11110
64bits F64 01111 X64 10111 X64U 11111
  • setp2 : 然后申请向量的个数
    假如向量寄存器总共有1024Byte ,如果只给2个向量使用,那么对半分,每个向量可以分得512Byte的空间,F64=8Byte,所以
    每个向量可以有512/8=64个F64类型的元素,也就是mvl(max_vecotr_length)=64, 一次可以计算的个数是64个元素。
    如果我们的X,Y的长度假设是100,那么完成 Y = a*X + Y 需要2次向量操作,
    第一次vl选64, 计算64个F64向量的运算
    第二次vl选100-64= 36, 计算剩余的36个F64向量的运算

IDEA Scala开发环境配置

IDEA

    1. 下载社区版本是免费的,scala开发够用,专业版面向web部分是收费的
    1. 启动idea ,import project 按钮倒入 .sbt 文件
    1. setting java JDK, File>>Project Structure>>Project>>Project SDK, 按new ,选择JAVA jdk.

      默认会自动识别,否则手动下载指定xxx/1.8.0-openjdk-amd64)(一般要求1.8版本)

Makefile 笔记

规则

伪目标

变量

变量定义的3种方式

  • 1 foo = bar (递归展开式变量)
    会在引用$(foo)地方原地替换,直到不能再替换,这种方式称之为递归展开式变量

    1
    2
    3
    4
    foo = $(bar)
    bar = $(ugh)
    ugh = Huh?
    all @echo $(foo)

    以上例子会在@echo $(foo) 地方递归展开
    缺点一 : 是当嵌套定义 foo = $(foo) bar 时讲进入死循环,导致Make失败
    其他例子
    x = $(y)
    y = $(x) $(z)
    同样会陷入死循环,所以这种定义方法只推荐在不引用变量的时候应用
    缺点二 这种定义中如果使用函数,只会在变量被引用时展开,而不是在定义时展开
    会使得Make的效率降低,另外有可能会在变量函数的引用会出现非预期结果,特别当变量
    定义引用到shell wildcard 函数的情况下,出现不可控的结果

    1
    2
    3
    4
    5
    6
    x = foo
    y = $(x) bar //$(x) 不会被立即替换,只有在$(y)被用到的地方才用x的值替换
    x = later //将会覆盖 原来的 x ,
    等价于
    y = later bar
    x = later

RISCV 编译 Link探究

问题: 如何将代码编译到指定的mem地址?

在用$RISCV/bin/riscv64-unknown-elf-gcc hello.c时生成的目标文件默认的都是从0x0000_1000地址开始,
如何自定义指定到从0x8000_0000地址开始?
那么就得熟悉连接器的原理

默认链接脚本

gcc在编译和链接的时候,如果不指定链接脚本,会指向默认的链接脚本
默认的链接脚本可以用 $RISCV/bin/riscv64-unknown-elf-ld -verbose 来查看

RISCV GDB debug调试

RISCV-GDB启动步骤

hello.c

1
2
3
4
5
6
7
8
#include <stdio.h>
int main(){
long a = 0x876543210 ;
float pi = 3.1415926 ;
printf("Hello %s!\n","World");
a = a*pi;
return a ;
}

如何生成汇编代码

riscv64-unknown-elf-gcc -S hello.c 只会生成汇编代码hello.s 不会生成.out文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    .file   "hello.c"
.option nopic
.text
.section .rodata
.align 3
.LC2:
.string "World"
.align 3
.LC3:
.string "Hello %s!\n"
.text
.align 1
.globl main
.type main, @function
main:
addi sp,sp,-32
sd ra,24(sp)
sd s0,16(sp)
addi s0,sp,32
lui a5,%hi(.LC0)
ld a5,%lo(.LC0)(a5)
sd a5,-24(s0)
lui a5,%hi(.LC1)
flw fa5,%lo(.LC1)(a5)
fsw fa5,-28(s0)
lui a5,%hi(.LC2)
addi a1,a5,%lo(.LC2)
lui a5,%hi(.LC3)
addi a0,a5,%lo(.LC3)
call printf
ld a5,-24(s0)
fcvt.s.l fa4,a5
flw fa5,-28(s0)
fmul.s fa5,fa4,fa5
fcvt.l.s a5,fa5,rtz
sd a5,-24(s0)
ld a5,-24(s0)
sext.w a5,a5
mv a0,a5
ld ra,24(sp)
ld s0,16(sp)
addi sp,sp,32
jr ra
.size main, .-main
.section .rodata
.align 3
.LC0:
.dword 36344967696
.align 2
.LC1:
.word 1078530010
.ident "GCC: (GNU) 8.1.0"

如何反汇编.out文件

首先生成.out文件riscv64-unknown-elf-gcc -o hello.out hello.c

然后反汇编 riscv64-unknown-elf-objdump -d hello.out > hello_disassemble.s

即可查看反汇编代码,搜索main函数如下,当然也包括其他内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
hello.out:     file format elf64-littleriscv
Disassembly of section .text:
00000000000100b0 <_start>:
100b0: 0000d197 auipc gp,0xd
100b4: fd818193 addi gp,gp,-40 # 1d088 <__global_pointer$>
100b8: 84818513 addi a0,gp,-1976 # 1c8d0 <_edata>
100bc: 8d818613 addi a2,gp,-1832 # 1c960 <_end>
.....
...

000000000001038c <printf>:
1038c: 711d addi sp,sp,-96
1038e: f832 sd a2,48(sp)
10390: fc36 sd a3,56(sp)
....
..

0000000000010198 <main>:
10198: 1101 addi sp,sp,-32
1019a: ec06 sd ra,24(sp)
1019c: e822 sd s0,16(sp)
1019e: 1000 addi s0,sp,32
101a0: 67e9 lui a5,0x1a
101a2: a687b783 ld a5,-1432(a5) # 19a68 <__clzdi2+0x54>
101a6: fef43423 sd a5,-24(s0)
101aa: 67e9 lui a5,0x1a
101ac: a707a787 flw fa5,-1424(a5) # 19a70 <__clzdi2+0x5c>
101b0: fef42227 fsw fa5,-28(s0)
101b4: 67e9 lui a5,0x1a
101b6: a5078593 addi a1,a5,-1456 # 19a50 <__clzdi2+0x3c>
101ba: 67e9 lui a5,0x1a
101bc: a5878513 addi a0,a5,-1448 # 19a58 <__clzdi2+0x44>
101c0: 1cc000ef jal ra,1038c <printf>
101c4: fe843783 ld a5,-24(s0)
101c8: d027f753 fcvt.s.l fa4,a5
101cc: fe442787 flw fa5,-28(s0)
101d0: 10f777d3 fmul.s fa5,fa4,fa5
101d4: c02797d3 fcvt.l.s a5,fa5,rtz
101d8: fef43423 sd a5,-24(s0)
101dc: fe843783 ld a5,-24(s0)
101e0: 2781 sext.w a5,a5
101e2: 853e mv a0,a5
101e4: 60e2 ld ra,24(sp)
101e6: 6442 ld s0,16(sp)
101e8: 6105 addi sp,sp,32
101ea: 8082 ret
....
...

gdb 查看CPU寄存器

-print $x0
-p $x0
-p $pc
-info reg
-display $x0 ;每跑一步都会显示寄存器
-ni ;next inst

RISCV C开发调试笔记

hello.c

1
2
3
4
5
6
7
8
#include <stdio.h>
int main(){
long a = 0x876543210 ;
float pi = 3.1415926 ;
printf("Hello %s!\n","World");
a = a*pi;
return a ;
}

RISCV 汇编指令调试

RISCV汇编指令调试,立即数使用分析

新建文件 test.s

1
2
3
4
5
6
.global main

.text
main:
li a5, 0x12345678
ret

riscv.....gcc test.s 编译成汇编代码
riscv.....gdb test.s 用调试器运行