arm - linkscript - 不同的链接地址和加载地址

标签 arm ld raspberry-pi mmu

我正在为我的 raspberry pi 拧一个玩具操作系统并尝试设置 MMU。我想在 3G:1G 之间分割虚拟内存,所以我认为我的代码应该在 0xC0008000 处链接,而在执行时加载到 0x8000。 (0x8000 是当前引导加载程序希望找到内核的地址 - 因为它们是为 linux 构建的)。

我认为通过使用 objdump 可以很好地设置一切,但它不起作用。使用 qemu 进行一些调试后,我认为引导加载程序根本找不到我的代码。

我相信问题出在我的链接脚本上,因为如果我将起始代码移动到它自己的部分,该部分在 0x8000 处链接并加载,则内核启动正常。

我已经提取了脚本和最少的代码。如下,

$ cat kernel.ld 
ENTRY(_start)

SECTIONS
{
    /* must == KERNLINK */
    .  = 0xC0008000;

    .text : AT(0x8000) {
        *(.text)
    }

    .bss : {
        *(.bss)
    }

    .data : {
        *(.data)
    }

    .rodata : {
        *(.rodata)
    }
}

——
$ cat source/entry.S 
#include "mem.h"

.globl _start
_start = V2P(entry)

.globl entry
entry:
    loop$:
    b loop$

(“b loop$”将不起作用,因为它生成为“b·c0008000”而不是使用相对分支。但别管那部分,问题是它永远不会到达入口)。
$ cat source/mem.h 
#define KERNLOAD 0x8000

#define KERNBASE 0xC0000000
#define KERNLINK (KERNBASE+KERNLOAD)

#define V2P(va) ((va) - KERNBASE)

这些是仅有的三个源文件。 Makefile 中应该没有什么有趣的东西,但是 make 的输出是,
$ make
arm-none-eabi-gcc -g -Wall -c -o build/entry.o source/entry.S
arm-none-eabi-ld --no-undefined -T kernel.ld build/entry.o  -Map kernel.map -o build/output.elf
arm-none-eabi-objcopy build/output.elf -O binary kernel.img

和 objdump,
$ arm-none-eabi-objdump -h build/output.elf  

build/output.elf:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00000004  c0008000  00008000  00008000  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .ARM.attributes 00000014  00000000  00000000  00008004  2**0
                  CONTENTS, READONLY
  2 .debug_line   0000003c  00000000  00000000  00008018  2**0
                  CONTENTS, READONLY, DEBUGGING
  3 .debug_info   00000054  00000000  00000000  00008054  2**0
                  CONTENTS, READONLY, DEBUGGING
  4 .debug_abbrev 00000014  00000000  00000000  000080a8  2**0
                  CONTENTS, READONLY, DEBUGGING
  5 .debug_aranges 00000020  00000000  00000000  000080c0  2**3
                  CONTENTS, READONLY, DEBUGGING

我开始相信我忽略了一些明显但宝贵的细节。

====更新====

正如我在下面自己的回答中所指出的,困惑是由 qemu 中的调试引起的。断点由虚拟地址设置。 “b entry”不起作用,因为 gdb 正在考虑虚拟地址,而 MMU 尚未启用并且我们正在按物理地址运行。

所以在启用 MMU 之前,我们必须使用“b *0x8000”。这会设置一个正确命中的断点。不过 GDB 似乎仍然很困惑,因为它没有显示任何调试信息(没有源代码,比如 0x00008004 in ?? ())。这不是什么大问题,因为我有“objdump -D”生成的列表。

启用 MMU 并分支到 main 后,gdb 完美运行。关键是使用绝对分支跳转到虚拟地址。 b/bl会发出相对跳跃。所以我使用 ldr pc =main . bx也有效。

最佳答案

终于解决了...

代码和链接脚本没有问题。只是调试在我使用的 qemu 上似乎不起作用。当 VMA==LMA 时它确实工作,但在这种情况下内核已经运行并且 qemu 不知道它。或者,断点丢失并且永远不会被 gdb 捕获。

无论如何,通过重新开始并一点一点地添加位,我让它在我的 rpi(真实硬件)上运行,然后它也可以在 qemu 上运行(gdb 仍然不会捕获断点)。

所以我的问题应该是如何使用 qemu/gdb 调试这样的场景。

(我原来的问题是通过将“域”权限更改为“经理”,而不是使用“客户端”来解决的。是的,我知道这可能是暂时的,我应该在其他地方设置一个变量)。

关于arm - linkscript - 不同的链接地址和加载地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15705636/

相关文章:

MacOS——如何使用 gcc/ld 将动态库链接到相对路径

raspberry-pi - 如何使用 Raspberry Pi 控制灯泡 - 要求

python - 让 TensorFlow 在 ARM Mac 上使用 GPU

arm - 为什么ARM有16个寄存器?

C - Externs - 监视 LD_PRELOAD 库中值的安全方法

linux - 无法停止 mpd 服务(没有发现/usr/bin/mpd 正在运行;没有被杀死)

c++ - 使用 Makefile 编译失败但在命令行中成功

assembly - 优化 ARM 的汇编

java - 在带有 ARM11 的设备中安装 JVM

linux - 使用 Rust 和 Amethyst 在 Linux 上缺少库