arm体系架构与实践第9章习题

实验9-1

解释每一条语句的含义

SECTIONS { // 链接地址放到0x80000 与运行地址一致 . = 0x80000, // 将每个文件对应的段放到一起 .text.boot : {*(.text.boot)} .text : {*(.text)} .rodata : {*(.rodata)} .data : { *(.data)} // 8字节对齐 . = ALIGN(0x8); // 记录bss的起始地址和终止地址,应该是可以方便后续的清空操作 bss_begin = .; .bss : {*(.bss*)} bss_end = .; // 4kb对齐 按页分配 . = ALIGN(4096) init_gp_dir = .; . += 4096; }

实验9-2

使用链接脚本中的地址

如果想使用链接脚本中的标识需要使用

1. extern char 标识[]; 标识 = 链接地址 2. extern char* 标识; &标识 = 链接地址 标识 = 链接地址处的内容
  • 数组:标识一定是第一个参数的地址,也就是链接脚本中的地址
  • 指针:标识会是地址的内容(指令),而标识的地址才是链接脚本中地址

示例:

extern char *text_boot_end; ... printk(" 指针 %x 内容 %x\n", &text_boot_end, text_boot_end);

结果是:

gdb的结果

但是如果是数组

extern char text_boot_begin[]; extern char text_boot_end[]; ... printk(" %x - %x\n", text_boot_begin, text_boot_end);

结果是:

所以如果想用链接脚本中的标识需要使用数组的声明方式

实现打印函数

实现不定长的参数函数调用

  1. 首先需要改变Makefile中的参数
-COPS += -g -Wall -nostdlib -nostdinc -Iinclude +# COPS += -g -Wall -nostdlib -nostdinc -Iinclude +COPS += -g -Wall -nostdlib -Iinclude

-nostdinc:开启后不会主动去寻找系统头文件的路径
由于需要使用到 stdarg.h 所以需要将这个参数删除
2. 实现printk

#include "uart.h" #include <stdarg.h> void printk(const char *format, ...) { va_list args; va_start(args, format); for (const char *p = format; *p; p++) { if (*p == '%') { p++; switch (*p) { case 'd': { int i = va_arg(args, int); uart_send_int(i); break; } case 's': { char *s = va_arg(args, char *); uart_send_string(s); break; } case 'x': { unsigned int i = va_arg(args, int); uart_send_hex(i); break; } default: uart_send('%'); uart_send(*p); break; } } else { uart_send(*p); } } va_end(args); }

实现对应的uart的函数

输出十进制
void uart_send_int(int num) { if (num < 0) { uart_send('-'); num = -num; } if (num == 0) { uart_send('0'); return; } char buffer[10]; // 假设整数不超过10位数 int i = 0; // 逐位取出整数的每一位数字 while (num > 0) { buffer[i++] = (num % 10) + '0'; // 转换成字符 num /= 10; } // 反向输出 i = 8; while (i > 0) { uart_send(buffer[--i]); } }
实现输出十六进制
// 将整数转换为十六进制并输出 void uart_send_hex(unsigned int num) { if (num == 0) { uart_send('0'); return; } char buffer[16]; // 假设32位整型数不超过8位十六进制 for (int i = 0; i < 16; ++i) { buffer[i] = '0'; } int i = 0; // 十六进制字符表,小写或大写由参数决定 const char *digits = "0123456789abcdef"; while (num > 0) { buffer[i++] = digits[num % 16]; num /= 16; } // 逆序输出 i = 16; while (i > 0) { uart_send(buffer[--i]); } }

输出实现

void ShowImageLayout() { // 其他和这个一样 printk(".text.boot: 0x%x - 0x%x ( %d B)\n", text_boot_begin, text_boot_end, text_boot_end - text_boot_begin); }

9-3

链接脚本:

diff --git a/src/linker.ld b/src/linker.ld index 10b7c10..874bc01 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -1,10 +1,26 @@ +TEXT_ROM = 0x90000; + SECTIONS { /*. = 0xffff000000080000,*/ . = 0x80000, + _text_boot = .; .text.boot : { *(.text.boot) } - .text : { *(.text) } - .rodata : { *(.rodata) } + _etext_boot = .; + + _text = .; + .text : AT(TEXT_ROM) { + *(.text) + } + _etext = .; + + . = ALIGN(0x8); + _rodata = .; + .rodata : AT(0x80000 + SIZEOF(.text) + SIZEOF(.text.boot)) { + *(.rodata) + } + _erodata = .; + .data : { *(.data) } . = ALIGN(0x8); bss_begin = .;
  1. 注意text段和rodata段的对齐,否则后续搬移代码的时候会出问题
  2. 19行将text放置到0x90000处,但是链接的地址是按照text_boot后面的位置来的。
  3. 26行:rodata会默认放置在text的加载地址的后面(0x90xxx),但是kernel中有字符串常量,字符串常量在rodata段中,但是后续搬移如果不搬移rodata段的话,这个字符串就无法正常打印,所以可以直接再链接脚本中将rodata段的加载地址设置在原本的链接地址上。

boot.S

proc_hang: b proc_hang +.align 3 master: + adr x0, TEXT_ROM + adr x1, _text + adr x2, _etext +1: + ldr x4, [x0], #8 + str x4, [x1], #8 + cmp x1, x2 + b.cc 1b +

678设置需要拷贝的地址

9-13进行逐8字节拷贝,从0x90000拷贝到text的链接地址


__EOF__

本文作者alan
本文链接https://www.cnblogs.com/alanli07/p/18493506.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   LIalan  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
点击右上角即可分享
微信分享提示