【问题标题】:What am I doing wrong when compiling C code to bare metal (raspberry pi)?将 C 代码编译为裸机(覆盆子 pi)时我做错了什么?
【发布时间】:2017-05-22 22:28:29
【问题描述】:

我花了好几天的时间试图弄清楚这一点,但我做不到。我有一些 C 代码。我已经为这个 C 程序制作了汇编代码,将汇编复制粘贴到其他人的项目(仅包含一个汇编文件)并组装它。在这些情况下,事情会奏效。但是如果我尝试直接从 C 编译来生成二进制文件,它就行不通了。即使其他一切都应该相同。这是我的 C 代码:

 #include <stdint.h>

#define REGISTERS_BASE 0x3F000000
#define MAIL_BASE 0xB880  // Base address for the mailbox registers
// This bit is set in the status register if there is no space to write into the mailbox
#define MAIL_FULL 0x80000000
// This bit is set in the status register if there is nothing to read from the mailbox
#define MAIL_EMPTY 0x40000000

struct Message
{
  uint32_t messageSize;
  uint32_t requestCode;
  uint32_t tagID;
  uint32_t bufferSize;
  uint32_t requestSize;
  uint32_t pinNum;
  uint32_t on_off_switch;
  uint32_t end;
};

struct Message m =
{
  .messageSize = sizeof(struct Message),
  .requestCode =0,
  .tagID = 0x00038041,
  .bufferSize = 8,
  .requestSize =0,
  .pinNum = 130,
  .on_off_switch = 1,
  .end = 0,
};

/** Main function - we'll never return from here */
int _start(void)
{
  uint32_t mailbox = MAIL_BASE + REGISTERS_BASE + 0x18;
  volatile uint32_t status;

  do
  {
    status = *(volatile uint32_t *)(mailbox);
  }
  while((status & 0x80000000));

  *(volatile uint32_t *)(MAIL_BASE + REGISTERS_BASE + 0x20) = ((uint32_t)(&m) & 0xfffffff0) | (uint32_t)(8);

  while(1);
}

这是我从成功方法中复制的链接器文件:

/*
 * Very simple linker script, combing the text and data sections
 * and putting them starting at address 0x800.
 */
SECTIONS {
  /* Put the code at 0x80000, leaving room for ARM and
   * the stack. It also conforms to the standard expecations.
   */
  .init 0x8000 : {
    *(.init)
  }

  .text : {
    *(.text)
  }

  /* Put the data after the code */
  .data : {
    *(.data)
  }
}

这就是我编译和链接所有内容的方式:

arm-none-eabi-gcc -O0 -march=armv8-a PiTest.c -nostartfiles -o kernel.o
arm-none-eabi-ld kernel.o -o kernel.elf -T kernel.ld
arm-none-eabi-objcopy kernel.elf -O binary kernel.img

我的目标架构是 armv8,因为这是 pi model 3 使用的。 我不知道生成的程序集是如何工作的,但 C 代码直接不知道。请帮助我处于疯狂的边缘。

编辑:预期的行为是 pi 的灯打开。它使用我描述的第一种方法。使用第二种方法时,灯仍然关闭。

EDIT4:对文件进行了一些更改,使用过时的信息删除了以前的编辑以减小帖子大小

  kernel.elf:     file format elf32-littlearm


Disassembly of section .init:

00008000 <_start>:
    8000:   e3a0dd7d    mov sp, #8000   ; 0x1f40
    8004:   eaffffff    b   8008 <kernel_main>

Disassembly of section .text:

00008008 <kernel_main>:
    8008:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
    800c:   e28db000    add fp, sp, #0
    8010:   e24dd00c    sub sp, sp, #12
    8014:   e30b3898    movw    r3, #47256  ; 0xb898
    8018:   e3433f00    movt    r3, #16128  ; 0x3f00
    801c:   e50b3008    str r3, [fp, #-8]
    8020:   e51b3008    ldr r3, [fp, #-8]
    8024:   e5933000    ldr r3, [r3]
    8028:   e50b300c    str r3, [fp, #-12]
    802c:   e51b300c    ldr r3, [fp, #-12]
    8030:   e3530000    cmp r3, #0
    8034:   bafffff9    blt 8020 <kernel_main+0x18>
    8038:   e30b38a0    movw    r3, #47264  ; 0xb8a0
    803c:   e3433f00    movt    r3, #16128  ; 0x3f00
    8040:   e3082050    movw    r2, #32848  ; 0x8050
    8044:   e3402001    movt    r2, #1
    8048:   e3c2200f    bic r2, r2, #15
    804c:   e3822008    orr r2, r2, #8
    8050:   e5832000    str r2, [r3]
    8054:   eafffffe    b   8054 <kernel_main+0x4c>

Disassembly of section .data:

00008058 <__data_start>:
    8058:   00000020    andeq   r0, r0, r0, lsr #32
    805c:   00000000    andeq   r0, r0, r0
    8060:   00038041    andeq   r8, r3, r1, asr #32
    8064:   00000008    andeq   r0, r0, r8
    8068:   00000000    andeq   r0, r0, r0
    806c:   00000082    andeq   r0, r0, r2, lsl #1
    8070:   00000001    andeq   r0, r0, r1
    8074:   00000000    andeq   r0, r0, r0

Disassembly of section .ARM.attributes:

00000000 <_stack-0x80021>:
   0:   00002e41    andeq   r2, r0, r1, asr #28
   4:   61656100    cmnvs   r5, r0, lsl #2
   8:   01006962    tsteq   r0, r2, ror #18
   c:   00000024    andeq   r0, r0, r4, lsr #32
  10:   412d3805            ; <UNDEFINED> instruction: 0x412d3805
  14:   070e0600    streq   r0, [lr, -r0, lsl #12]
  18:   09010841    stmdbeq r1, {r0, r6, fp}
  1c:   14041202    strne   r1, [r4], #-514 ; 0xfffffdfe
  20:   17011501    strne   r1, [r1, -r1, lsl #10]
  24:   1a011803    bne 46038 <__bss_end__+0x3dfc0>
  28:   2a012201    bcs 48834 <__bss_end__+0x407bc>
  2c:   Address 0x000000000000002c is out of bounds.


Disassembly of section .comment:

00000000 <.comment>:
   0:   3a434347    bcc 10d0d24 <_stack+0x1050d03>
   4:   35312820    ldrcc   r2, [r1, #-2080]!   ; 0xfffff7e0
   8:   392e343a    stmdbcc lr!, {r1, r3, r4, r5, sl, ip, sp}
   c:   732b332e            ; <UNDEFINED> instruction: 0x732b332e
  10:   33326e76    teqcc   r2, #1888   ; 0x760
  14:   37373131            ; <UNDEFINED> instruction: 0x37373131
  18:   2029312d    eorcs   r3, r9, sp, lsr #2
  1c:   2e392e34    mrccs   14, 1, r2, cr9, cr4, {1}
  20:   30322033    eorscc  r2, r2, r3, lsr r0
  24:   35303531    ldrcc   r3, [r0, #-1329]!   ; 0xfffffacf
  28:   28203932    stmdacs r0!, {r1, r4, r5, r8, fp, ip, sp}
  2c:   72657270    rsbvc   r7, r5, #112, 4
  30:   61656c65    cmnvs   r5, r5, ror #24
  34:   00296573    eoreq   r6, r9, r3, ror r5

【问题讨论】:

  • “它不起作用”不是错误描述。究竟会发生什么?你预计会发生什么?
  • 我添加了一个编辑,因为你是对的,这是一个重要的细节。问题是打开 pi 的灯。
  • 您是否刚刚创建了一个可引导的内核?
  • 根据 pi 的文档,在 sd 卡中包含几个固件文件后,在启动过程后在 pi 上执行的第一个二进制文件将是 kernel.img。
  • pi3 的入口点是 0x80000,旧的入口点是 0x8000。

标签: c assembly raspberry-pi


【解决方案1】:
kernel8.img

12345678
00000800
00080264
00000000
12345678

kernel8-32.img

12345678
00008320
00008224
200001DA
12345678

kernel7.img

12345678
00000700
00008224
200001DA
12345678

kernel.img

12345678
00000000
00008224
200001DA
12345678

当我编写并发布此代码时,这就是我得到的,所以如果您将文件命名为 kernel.img,那么 0x8000 是您的入口点,我在您的其他 SO 问题中给出的答案是完整的树莓派起点。您可以简单地添加您的邮箱内容,但如果您对此感到苦恼,邮箱和视频不是您应该开始 IMO 的地方。

如果您将文件命名为 kernel8.img,则入口点是 0x80000,更改链接描述文件以匹配。

我有一个基于串行端口的引导加载程序,您可以使用它来保存 sd 卡舞蹈,可以在很长一段时间内使用它,然后只需在应用程序工作后使用您正在创建的二进制版本写入闪存。

编辑

好吧,这太恶心了,在这里发布它可能意味着你不能在你的课堂作业中使用它……你应该正确地做这件事,而不是在你的引导程序中使用内联汇编……

so.c

asm(
".globl _start\n"
"_start:\n"
"mov sp,#0x8000\n"
"bl centry\n"
"b .\n"
);


unsigned int centry ( void )
{
    return(5);
}

构建

arm-none-eabi-gcc -O2 -c so.c -o so.o
arm-none-eabi-ld -Ttext=0x8000 so.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf -O binary kernel.img

检查

Disassembly of section .text:

00008000 <_start>:
    8000:   e3a0d902    mov sp, #32768  ; 0x8000
    8004:   eb000000    bl  800c <centry>
    8008:   eafffffe    b   8008 <_start+0x8>

0000800c <centry>:
    800c:   e3a00005    mov r0, #5
    8010:   e12fff1e    bx  lr

一个完整的 raspberry pi C 和 bootstrap 示例,适用于任何 pi 风格(据我所知,他们可能在过去几个月更改了 GPU 引导加载程序,但假设没有)。

【讨论】:

    【解决方案2】:

    这里有几件事我认为是错误的。最明显的是:

    • 您不会在地址 0 处留下任何内容,因此 CPU 在启动时会执行空白内存。您需要在0x0 处添加一些内容(例如分支指令!)。

    • 在 ARM Cortex-A 上,堆栈指针在启动时未初始化。您必须自己在 _start 中初始化它——这意味着您需要在汇编中编写该函数。

    【讨论】:

    • 首先,我认为 ram 中的第一个地址是为中断分支功能保留的。第二,为什么组装工作?我不应该在两个版本的代码中都遇到这个问题吗?
    • 它们不是精确地保留的——您可以在设置 NVIC 时重新定位向量表。但是,无论如何,向量表的第一个字是未使用的,因此您可以在其中放置一条指令。
    • 至于汇编代码,我不看也说不出来。不过,它可能不使用堆栈,这可以解释它为什么工作(但一旦你尝试做更复杂的事情就会停止工作)。
    • 我添加了汇编代码,您可以看到它确实使用了堆栈指针,尽管堆栈指针尚未初始化。我还进行了制作 boot.s 程序集文件的实验,该文件设置堆栈指针,然后分支到 main(我将 main 函数重命名为 main,以便代码在 boot.s 开始执行,现在具有 _start 标签)但是导致还是打不开。
    • 在没有 config.txt 的情况下,pi 上的裸机入口点可能会有所不同,pi1 为 0x8000,pi3 为 0x80000,不确定但认为 pi2 为 0x8000。引导加载程序在 gpu 中并将代码放在 0x0000 以将内核切换到 hyp 模式(对于四核),然后分支到 0x8000 或 0x80000
    【解决方案3】:

    首先,感谢老前辈的耐心帮助我。 错误是:

    错误的程序入口点,通过创建带有标签 _start 的汇编文件来设置堆栈指针并使用链接器将 init 部分放置在地址 0x8000 来修复

    编译行本身也是错误的,它缺少 -c 参数

    【讨论】:

    • 自答没问题,但还是:接受答案。当您发现其他答案有帮助时,请接受。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-17
    • 2020-11-21
    • 1970-01-01
    • 2020-05-16
    相关资源
    最近更新 更多