裸机编程Raspberry Pi 3. [英] Bare metal programming Raspberry Pi 3.

查看:119
本文介绍了裸机编程Raspberry Pi 3.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读一些裸机编程教程.在阅读有关C代码执行的知识时,我知道我们需要设置C执行环境,例如初始化堆栈清零bss等.

在某些情况下,您必须将数据复制到ram中,并且还需要为此提供启动代码. 链接,其中说在RAM中复制数据. 现在我有两个疑问.

如果我们需要在RAM中复制数据,那为什么不复制代码(即文本段)呢?如果我们不复制文本段,则表示在Raspberry pi 3(Arm嵌入式处理器)的情况下,代码是从SD卡本身执行的.

当我们按如下所示指定链接描述文件时,是否建议将这些部分复制到RAM中,否则这些部分将映射到RAM地址中? 对不起,我真的很困惑.

MEMORY
{
   ram : ORIGIN = 0x8000, LENGTH = 0x1000
}

SECTIONS
{
   .text : { *(.text*) } > ram
   .bss : { *(.bss*) } > ram
}

感谢您的帮助.

解决方案

vectors.s

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
    b .

notmain.c

unsigned int x;
unsigned int y=0x12345678;

void notmain ( void )
{
    x=y+7;
}

内存映射

MEMORY
{
    bob : ORIGIN = 0x80000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .bss : { *(.bss*) } > ted
    .data : { *(.data*) } > ted
}

构建

arm-none-eabi-as --warn --fatal-warnings vectors.s -o vectors.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c notmain.c -o notmain.o
arm-none-eabi-ld vectors.o notmain.o -T memmap -o notmain.elf
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf -O binary kernel.img

您可以添加/删除选项,并将其命名为正确的kernelX.img(如果您尝试使用64位,请使用aarch64-whatever-gcc代替arm-whatever-gcc ...

看着反汇编

Disassembly of section .text:

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

0000800c <notmain>:
    800c:   e59f3010    ldr r3, [pc, #16]   ; 8024 <notmain+0x18>
    8010:   e5933000    ldr r3, [r3]
    8014:   e59f200c    ldr r2, [pc, #12]   ; 8028 <notmain+0x1c>
    8018:   e2833007    add r3, r3, #7
    801c:   e5823000    str r3, [r2]
    8020:   e12fff1e    bx  lr
    8024:   00008030    andeq   r8, r0, r0, lsr r0
    8028:   0000802c    andeq   r8, r0, r12, lsr #32

Disassembly of section .bss:

0000802c <x>:
    802c:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

00008030 <y>:
    8030:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

并将其与kernelX.img文件进行比较

hexdump -C kernel.img 
00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 10 30 9f e5  |.............0..|
00000010  00 30 93 e5 0c 20 9f e5  07 30 83 e2 00 30 82 e5  |.0... ...0...0..|
00000020  1e ff 2f e1 30 80 00 00  2c 80 00 00 00 00 00 00  |../.0...,.......|
00000030  78 56 34 12                                       |xV4.|
00000034

请注意,因为我在链接器脚本中将.data放在.bss之后,所以将它们按图像中的顺序放置. .text中的最后一个字和.data的0x12345678之后有四个零字节

如果您在链接描述文件中交换.bss和.data的位置

0000802c <y>:
    802c:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

Disassembly of section .bss:

00008030 <x>:
    8030:   00000000    andeq   r0, r0, r0

00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 10 30 9f e5  |.............0..|
00000010  00 30 93 e5 0c 20 9f e5  07 30 83 e2 00 30 82 e5  |.0... ...0...0..|
00000020  1e ff 2f e1 2c 80 00 00  30 80 00 00 78 56 34 12  |../.,...0...xV4.|
00000030

糟糕,没有免费赠品.现在.bss没有被清零,您需要在引导程序中将其清零(如果您具有.bss区域,并且作为编程样式,您假设这些项在首次使用时为零).

好的,如何找到.bss在哪里?嗯,这就是本教程和无数其他人向您展示的内容.

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
    b .
linker_stuff:
.word hello_world
.word world_hello

MEMORY
{
    bob : ORIGIN = 0x80000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .data : { *(.data*) } > ted

    hello_world = .;
    .bss : { *(.bss*) } > ted
    world_hello = .;
}

构建和拆卸

Disassembly of section .text:

00008000 <_start>:
    8000:   e3a0d902    mov sp, #32768  ; 0x8000
    8004:   eb000002    bl  8014 <notmain>
    8008:   eafffffe    b   8008 <_start+0x8>

0000800c <linker_stuff>:
    800c:   00008038    andeq   r8, r0, r8, lsr r0
    8010:   0000803c    andeq   r8, r0, r12, lsr r0

00008014 <notmain>:
    8014:   e59f3010    ldr r3, [pc, #16]   ; 802c <notmain+0x18>
    8018:   e5933000    ldr r3, [r3]
    801c:   e59f200c    ldr r2, [pc, #12]   ; 8030 <notmain+0x1c>
    8020:   e2833007    add r3, r3, #7
    8024:   e5823000    str r3, [r2]
    8028:   e12fff1e    bx  lr
    802c:   00008034    andeq   r8, r0, r4, lsr r0
    8030:   00008038    andeq   r8, r0, r8, lsr r0

Disassembly of section .data:

00008034 <y>:
    8034:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

Disassembly of section .bss:

00008038 <x>:
    8038:   00000000    andeq   r0, r0, r0

因此,深入研究特定于工具链的内容,我们现在可以知道.bss的开头和结尾,或者可以在链接描述文件中使用数学运算来获取大小和长度.从中您可以编写一个小循环,将内存归零(当然,在分支到程序的C入口点之前,在引导程序中使用汇编语言,鸡肉和鸡蛋).

现在由于某种原因,您要在其他地址0x10000000上使用.data

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
    b .

MEMORY
{
    bob : ORIGIN = 0x10000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .bss : { *(.bss*) } > ted
    .data : { *(.data*) } > bob
}

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

0000800c <notmain>:
    800c:   e59f3010    ldr r3, [pc, #16]   ; 8024 <notmain+0x18>
    8010:   e5933000    ldr r3, [r3]
    8014:   e59f200c    ldr r2, [pc, #12]   ; 8028 <notmain+0x1c>
    8018:   e2833007    add r3, r3, #7
    801c:   e5823000    str r3, [r2]
    8020:   e12fff1e    bx  lr
    8024:   10000000    andne   r0, r0, r0
    8028:   0000802c    andeq   r8, r0, r12, lsr #32

Disassembly of section .bss:

0000802c <x>:
    802c:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

10000000 <y>:
10000000:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

那么kernel.img或-O二进制格式是什么?它只是一个从最低地址(在本例中为0x8000)开始并填充或填充到最高地址(在本例中为0x10000003)的内存映像,因此它是一个0x10000004-0x8000字节文件.

00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 10 30 9f e5  |.............0..|
00000010  00 30 93 e5 0c 20 9f e5  07 30 83 e2 00 30 82 e5  |.0... ...0...0..|
00000020  1e ff 2f e1 00 00 00 10  2c 80 00 00 00 00 00 00  |../.....,.......|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0fff8000  78 56 34 12                                       |xV4.|
0fff8004

对于该程序而言,这是磁盘空间的巨大浪费,他们为此埋下了伏笔.现在,如果由于某种原因(例如,由于各种原因(通常不适用于pi上的裸机))想要执行类似的操作,则可以执行以下操作:

MEMORY
{
    bob : ORIGIN = 0x10000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .bss : { *(.bss*) } > ted
    .data : { *(.data*) } > bob AT > ted
}



00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 10 30 9f e5  |.............0..|
00000010  00 30 93 e5 0c 20 9f e5  07 30 83 e2 00 30 82 e5  |.0... ...0...0..|
00000020  1e ff 2f e1 00 00 00 10  2c 80 00 00 00 00 00 00  |../.....,.......|
00000030  78 56 34 12                                       |xV4.|
00000034

Disassembly of section .bss:

0000802c <x>:
    802c:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

10000000 <y>:
10000000:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

它所做的是在0x10000000处为.data编译并链接了代码,但是随身携带并加载的二进制文件将.data数据捆绑在一起,这是引导程序的工作,它将数据复制到正确的着陆点为0x10000000,再次必须使用工具链特定的链接器脚本内容

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
    b .

linker_stuff:
.word data_start
.word data_end

MEMORY
{
    bob : ORIGIN = 0x10000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .bss : { *(.bss*) } > ted
    data_start = .;
    .data : { *(.data*) } > bob AT > ted
    data_end = .;
}

0000800c <linker_stuff>:
    800c:   00008038    andeq   r8, r0, r8, lsr r0
    8010:   10000004    andne   r0, r0, r4

显然这并没有奏效,因此您必须做更多的链接器脚本工作才能弄清楚.

对于树莓派,没有充分的理由,最好,如果您有.bss且没有任何.data和/或如果您有很多.bss放在最后,那么您可以要么利用工具链意外填充零并为您解决.bss问题,或者如果二进制太大,那么您可以在上面看到如何找到.bss偏移量和大小,然后将几行代码添加为零(这两种方式最终都会花费加载时间,但不会消耗sd卡空间).

当您选择使用要求.data和/或.bss和以下样式的程序进行编程时,您肯定需要学习此类知识的地方是当您在将非易失性视为只读闪存的微控制器上时假设已实现这些项目,则必须执行特定于工具链的工作,然后链接至零和/或从非易失性闪存复制到读/写ram,然后再分支到应用程序的第一个或唯一的C入口点.

我确定有人会提出不将pi裸机二进制文件打包得整洁的原因,总会有一个例外...但是现在您不必担心那些例外,请先放.bss,然后.data,即使没有使用过,也要确保有.data项.

I was going through some bare metal programming tutorials. While reading about C code execution I came to know that we need to setup C execution environment like initializing stack zeroing bss etc.

In some cases you have to copy data in ram , and need to provide startup code for that as well. Link of tutorial which says copy data in RAM. Now I have two doubts.

If we need to copy data in RAM then why don't we copy code ie text segment. If we don't copy text segment doest it mean code is executed from SD card itself in case of Raspberry pi 3(Arm embedded processor).

When we specify linker script like below, does it suggest to copy those section in RAM or these sections will be mapped in RAM address? Sorry I am really confuse.

MEMORY
{
   ram : ORIGIN = 0x8000, LENGTH = 0x1000
}

SECTIONS
{
   .text : { *(.text*) } > ram
   .bss : { *(.bss*) } > ram
}

Any help is appreciated.

解决方案

vectors.s

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
    b .

notmain.c

unsigned int x;
unsigned int y=0x12345678;

void notmain ( void )
{
    x=y+7;
}

memmap

MEMORY
{
    bob : ORIGIN = 0x80000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .bss : { *(.bss*) } > ted
    .data : { *(.data*) } > ted
}

build

arm-none-eabi-as --warn --fatal-warnings vectors.s -o vectors.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c notmain.c -o notmain.o
arm-none-eabi-ld vectors.o notmain.o -T memmap -o notmain.elf
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf -O binary kernel.img

you can add/remove options, and name it the right kernelX.img (and if you are venturing into 64 bit then use aarch64-whatever-gcc instead of arm-whatever-gcc...

Looking at the dissassembly

Disassembly of section .text:

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

0000800c <notmain>:
    800c:   e59f3010    ldr r3, [pc, #16]   ; 8024 <notmain+0x18>
    8010:   e5933000    ldr r3, [r3]
    8014:   e59f200c    ldr r2, [pc, #12]   ; 8028 <notmain+0x1c>
    8018:   e2833007    add r3, r3, #7
    801c:   e5823000    str r3, [r2]
    8020:   e12fff1e    bx  lr
    8024:   00008030    andeq   r8, r0, r0, lsr r0
    8028:   0000802c    andeq   r8, r0, r12, lsr #32

Disassembly of section .bss:

0000802c <x>:
    802c:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

00008030 <y>:
    8030:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

and comparing that to the kernelX.img file

hexdump -C kernel.img 
00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 10 30 9f e5  |.............0..|
00000010  00 30 93 e5 0c 20 9f e5  07 30 83 e2 00 30 82 e5  |.0... ...0...0..|
00000020  1e ff 2f e1 30 80 00 00  2c 80 00 00 00 00 00 00  |../.0...,.......|
00000030  78 56 34 12                                       |xV4.|
00000034

Note that because I put .data after .bss in the linker script it put them in that order in the image. there are four bytes of zeros after the last word in .text and the 0x12345678 of .data

If you swap the positions of .bss and .data in the linker script

0000802c <y>:
    802c:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

Disassembly of section .bss:

00008030 <x>:
    8030:   00000000    andeq   r0, r0, r0

00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 10 30 9f e5  |.............0..|
00000010  00 30 93 e5 0c 20 9f e5  07 30 83 e2 00 30 82 e5  |.0... ...0...0..|
00000020  1e ff 2f e1 2c 80 00 00  30 80 00 00 78 56 34 12  |../.,...0...xV4.|
00000030

Ooops, no freebie. Now .bss is not zeroed and you would need to zero it in your bootstrap (if you have a .bss area and as a programming style you assume those items are zero when you first use them).

Okay so how do you find where .bss is? well that is what the tutorial and countless others are showing you.

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
    b .
linker_stuff:
.word hello_world
.word world_hello

MEMORY
{
    bob : ORIGIN = 0x80000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .data : { *(.data*) } > ted

    hello_world = .;
    .bss : { *(.bss*) } > ted
    world_hello = .;
}

build and disassemble

Disassembly of section .text:

00008000 <_start>:
    8000:   e3a0d902    mov sp, #32768  ; 0x8000
    8004:   eb000002    bl  8014 <notmain>
    8008:   eafffffe    b   8008 <_start+0x8>

0000800c <linker_stuff>:
    800c:   00008038    andeq   r8, r0, r8, lsr r0
    8010:   0000803c    andeq   r8, r0, r12, lsr r0

00008014 <notmain>:
    8014:   e59f3010    ldr r3, [pc, #16]   ; 802c <notmain+0x18>
    8018:   e5933000    ldr r3, [r3]
    801c:   e59f200c    ldr r2, [pc, #12]   ; 8030 <notmain+0x1c>
    8020:   e2833007    add r3, r3, #7
    8024:   e5823000    str r3, [r2]
    8028:   e12fff1e    bx  lr
    802c:   00008034    andeq   r8, r0, r4, lsr r0
    8030:   00008038    andeq   r8, r0, r8, lsr r0

Disassembly of section .data:

00008034 <y>:
    8034:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

Disassembly of section .bss:

00008038 <x>:
    8038:   00000000    andeq   r0, r0, r0

so digging more into toolchain specific stuff we can now know either the start and end of .bss or can use math in the linker script to get size and length. From which you can write a small loop that zeros that memory (in assembly language of course, chicken and egg, in the bootstrap before you branch to the C entry point of your program).

Now say for some reason you wanted .data at some other address 0x10000000

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
    b .

MEMORY
{
    bob : ORIGIN = 0x10000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .bss : { *(.bss*) } > ted
    .data : { *(.data*) } > bob
}

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

0000800c <notmain>:
    800c:   e59f3010    ldr r3, [pc, #16]   ; 8024 <notmain+0x18>
    8010:   e5933000    ldr r3, [r3]
    8014:   e59f200c    ldr r2, [pc, #12]   ; 8028 <notmain+0x1c>
    8018:   e2833007    add r3, r3, #7
    801c:   e5823000    str r3, [r2]
    8020:   e12fff1e    bx  lr
    8024:   10000000    andne   r0, r0, r0
    8028:   0000802c    andeq   r8, r0, r12, lsr #32

Disassembly of section .bss:

0000802c <x>:
    802c:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

10000000 <y>:
10000000:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

so what is the kernel.img or -O binary format? it is just a memory image starting at the lowest address (0x8000 in this case) and filled OR PADDED to the highest address, in this case 0x10000003, so it is a 0x10000004-0x8000 byte file.

00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 10 30 9f e5  |.............0..|
00000010  00 30 93 e5 0c 20 9f e5  07 30 83 e2 00 30 82 e5  |.0... ...0...0..|
00000020  1e ff 2f e1 00 00 00 10  2c 80 00 00 00 00 00 00  |../.....,.......|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0fff8000  78 56 34 12                                       |xV4.|
0fff8004

That is a massive waste of disk space for this program, they padded the hell out of that. Now if for some reason you wanted to do something like this, various reasons (that generally do not apply to bare metal on the pi), you could do this instead:

MEMORY
{
    bob : ORIGIN = 0x10000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .bss : { *(.bss*) } > ted
    .data : { *(.data*) } > bob AT > ted
}



00000000  02 d9 a0 e3 00 00 00 eb  fe ff ff ea 10 30 9f e5  |.............0..|
00000010  00 30 93 e5 0c 20 9f e5  07 30 83 e2 00 30 82 e5  |.0... ...0...0..|
00000020  1e ff 2f e1 00 00 00 10  2c 80 00 00 00 00 00 00  |../.....,.......|
00000030  78 56 34 12                                       |xV4.|
00000034

Disassembly of section .bss:

0000802c <x>:
    802c:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

10000000 <y>:
10000000:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

what it has done is the code is compiled and linked for .data at 0x10000000 but the binary that you carry around and load has the .data data bundled up tight, it is the job of the bootstrap to copy that data to its correct landing spot of 0x10000000 and again you have to use toolchain specific linker scripty stuff

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
    b .

linker_stuff:
.word data_start
.word data_end

MEMORY
{
    bob : ORIGIN = 0x10000000, LENGTH = 0x1000
    ted : ORIGIN = 0x8000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ted
    .rodata : { *(.rodata*) } > ted
    .bss : { *(.bss*) } > ted
    data_start = .;
    .data : { *(.data*) } > bob AT > ted
    data_end = .;
}

0000800c <linker_stuff>:
    800c:   00008038    andeq   r8, r0, r8, lsr r0
    8010:   10000004    andne   r0, r0, r4

and clearly that didnt quite work so you have to do more linker scripy stuff to figure it out.

there is no good reason to need any of this for the raspberry pi, at best if you have .bss and dont have any .data and/or you put .bss last if you have a lot of it, then you can either take advantage of the toolchain accidentally zero padding and solving the .bss problem for you or if that is too big of a binary then you can see above how to find the .bss offset and size then add the few lines of code to zero it (ultimately costing load time either way, but not costing sd card space).

where you definitely need to learn such things is for when you are on a microcontroller where the non-volatile is treated as read-only flash, if you choose to program with a style that requires .data and/or .bss and you assume those items are implemented then you have to do the toolchain specific work to link then zero and/or copy from non-volatile flash to read/write ram before branching into the first or only C entry point of your application.

I am sure someone could come up with reasons to not pack a pi bare metal binary up nice and neat, there is always an exception...but for now you dont need to worry about those exceptions, put .bss first then .data and always make sure you have a .data item even if it is something you never use.

这篇关于裸机编程Raspberry Pi 3.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆