# 第5章和第6章读后感
第5章介绍GNU汇编器,GCC的编译过程包括预处理、编译、汇编和链接,生成一个可执行文件,二进制可执行文件满足某种格式,例如ELF格式。同时介绍了汇编程序中使用的一些伪指令。思维导图如下。
第6章链接器与链接脚本,在源文件较多的时候通常使用链接脚本的方式对链接过程中的目标文件的代码段、数据段及符号表等进行重新组织。本节中介绍了链接脚本使用了相关命令,加载重定位、链接重定位和链接松弛优化。思维导图如下图。
## 实验验证
### 第5章实验验证
#### 实验5-1:汇编语言联系——查找最大数
汇编代码:
```
.align 3
data:
.word 100, 2, 30, 14, 5, 36, 87, 28, 309
.global find_max
find_max:
addi sp, sp, -16
sd ra, 8(sp)
la t0, data
lw a0, (t0)
li t1, 8
.loop:
/* 指向下一个数据*/
addi t0, t0, 4
/* 获取数据*/
lw t2, (t0)
/* 比较两个数据,如果a0 > t2 跳转到.dayu ,否则将a0赋值为当前值 */
bgt a0, t2, .dayu
lw a0, (t0)
.dayu:
addi t1,t1,-1
bgtz t1, .loop
ld ra, 8(sp)
addi sp, sp, 16
ret
```
实验验证,通过执行程序,正确的挑选除了最大值。
#### 实验5-2:汇编语言联系——通过C语言调用汇编函数
汇编代码:
```
.align 3
.global get_max
get_max:
addi sp, sp, -16
sd ra, 8(sp)
mv t0, a0
/* 比较两个数据,如果t0 > a1 跳转到.dayu ,否则将a0赋值为a1 */
bgt t0, a1, .dayu
mv a0, a1
.dayu:
ld ra, 8(sp)
addi sp, sp, 16
ret
```
C语言调用:
```c
int max;
max = get_max(10,100);
max = get_max(-10,-100);
```
实验验证,通过执行程序,正确的挑选两个输入中较大的一个。
#### 实验5-3:汇编语言联系——通过汇编语言调用C函数
汇编代码:
```
.align 3
.global get_max
get_max:
addi sp, sp, -16
sd ra, 8(sp)
li a0, 10
li a1, 100
call c_max
ld ra, 8(sp)
addi sp, sp, 16
ret
```
C语言调用:
```
int c_max(int a, int b)
{
if ( a > b)
return a;
else
return b;
}
int max;
max = get_max();
```
实验验证,通过执行程序,正确的挑选两个输入中较大的一个。
#### 实验5-4:使用汇编伪操作实现一张表
汇编代码:
```
.align 3
.global func_addr
func_addr:
.quad0x800800
.quad0x800860
.quad0x800880
.align 3
.global func_string
func_string:
.asciz "func_a"
.asciz "func_b"
.asciz "func_c"
.align 3
.global func_num_syms
func_num_syms:
.word3
```
C语言调用:
```
char *p = get_func_name(0x800860);
if(p != 0)
uart_send_string(p);
```
实验验证,通过执行程序,正确的获取了地址对应的函数名称。
#### 实验5-5:汇编宏的使用
汇编代码:
```
.align 3
.macro op_func, lable, a, b
mv a0, \a
mv a1, \b
call\lable\()
.endm
.global add_1
add_1:
add a0, a0, a1
ret
.global add_2
add_2:
sub a0, a0, a1
ret
.global add_op
add_op:
addi sp, sp, -16
sd ra, 8(sp)
li t0, 10
li t1, 100
op_func add_1, t0, t1
li t0, 100
li t1, 10
op_func add_2, t0, t1
ld ra, 8(sp)
addi sp, sp, 16
ret
```
C语言调用:
```
add_op();
```
实验验证,通过执行程序,实现了通过宏调用函数。
### 第6章实验验证
#### 实验6-1:分析链接脚本
```
SECTIONS
{
. = 0x80200000 /*定义当前位置*/
.text.boot : { *(.text.boot) } /*定义.text.boot段,并且将所有需要链接的二进制文件中.text.boot段的内容放在此段中*/
.text: { *(.text) } /*定义.text段,并且将所有需要链接的二进制文件中.text段的内容放在此段中*/
.rodata : { *(.rodata)} /*定义.rodata段,并且将需要链接文件的.rodata段的内容放到此段中*/
.data : { *(.data) } /*定义.data段,并且将需要链接文件的.data段的内容放到此段中*/
. = ALIGN(0x8) /*以下数据按8字节对齐*/
bss_begin = . ; /*定义bss段的开始地址*/
.bss : {*(.bss*) } /*定义.bss段,并且将需要链接文件的.bss开头的内容放到此段中*/
bss_end = . ; /*定义bss段的结束地址*/
}
```
#### 实验6-2:输出每个段的内存分布
1) 链接脚本的调整:
```
SECTIONS
{
. = 0x22000,
_text_boot = .;
.text.boot : { *(.text.boot) }
_etext_boot = .;
_text = .;
.text : { *(.text) }
_etext = .;
_rodata = .;
.rodata : { *(.rodata) }
_erodata = .;
_data = .;
.data : { *(.data) }
_edata = .;
. = ALIGN(0x8);
bss_begin = .;
.bss : { *(.bss*) }
bss_end = .;
}
```
C语言代码:
```
uart_send_string("Benos Image Layout:\n\r");
sys_uart_printf(".text.boot: 0x%x - 0x%x (%d B)\n\r",
(unsigned long)_text_boot, (unsigned long)_etext_boot,
(unsigned long)(_etext_boot - _text_boot));
sys_uart_printf(" .text: 0x%x - 0x%x (%d B)\n\r",
(unsigned long)_text, (unsigned long)_etext,
(unsigned long)(_etext - _text));
sys_uart_printf(" .rodata: 0x%x - 0x%x (%d B)\n\r",
(unsigned long)_rodata, (unsigned long)_erodata,
(unsigned long)(_erodata - _rodata));
sys_uart_printf(" .data: 0x%x - 0x%x (%d B)\n\r",
(unsigned long)_data, (unsigned long)_edata,
(unsigned long)(_edata - _data));
sys_uart_printf(" .bss: 0x%x - 0x%x (%d B)\n\r",
(unsigned long)bss_begin, (unsigned long)bss_end,
(unsigned long)(bss_end - bss_begin));
```
实际执行代码的结果:
实际编译输出的map文件参见附件benos6-2.map 。
benos6-2.map
(7.11 KB, 下载次数: 0)
2) 编写C语言函数把.bss段的内容清零
C语言函数:
```
unsigned short itemp[16];
extern char _text_boot[];
extern char _etext_boot[];
extern char _text[];
extern char _etext[];
extern char _rodata[];
extern char _erodata[];
extern char _data[];
extern char _edata[];
extern char bss_begin[];
extern char bss_end[];
void clear_bss(void)
{
int *p = (int *)bss_begin;
for(;p<(int *)bss_end;p++)
*p = 0;
}
```
实际执行程序后的效果。
#### 实验6-5:链接器松弛优化1
通过在Makefile文件中增加 -mno-relax 取消松弛优化,可见编译结果采用PC指针加偏移的方式来寻址。
#### 实验6-6:链接器松弛优化2
在Makefile中增加 –mrelax,或者不增加,编译中默认增加,开启松弛优化,可见编译结果不在采用PC相对寻址的方式,而是通过jal跳转指令,明显的优化了代码。