【问题标题】:questions on address after disassembler反汇编后地址问题
【发布时间】:2017-04-29 11:42:01
【问题描述】:

我是使用 arm 的 gnu asm 的新手,并且对一些代码感到困惑。我写了这样的代码:

.code 16

.text

vectors:

    .word STACKINIT
    .word _start + 1
    ..... (defines vectors)

_start:

    mov r0, #0xAB
    .... (other code)

用 arm-none-linux-gnueabi-objdump 反汇编后,我得到这些代码:

00008060 <vectors>:

    8060:   20005000    .word   0x20005000
    8064:   000080d1    .word   0x000080d1
    8068:   000080f9    .word   0x000080f9
  ........

000080d0 <_start>:

    80d0:   20ab        movs    r0, #171    ; 0xab

让我感到困惑的是地址。为什么向量的起始地址是 00008060,而不是 0x0000,为什么 _start 的起始地址是 000080d0,而不是 0x00d0?谢谢。

【问题讨论】:

  • 1) 您没有提供足够的信息。 2) _start+1 不是一个好习惯,使用 .thumb_func 它是地址 ORRed 与 1 而不是 PLUS 1,如果工具生成正确的地址,总有一天你会遇到麻烦。 3)是的,你的向量表搞砸了......

标签: assembly arm cortex-m bare-metal thumb


【解决方案1】:

您没有提供足够的信息。使用加一而不是或一是不好的,因为当您获得正确的地址时,它会被加一修改并且是错误的。无论如何,您永远不需要做加一。这是一个非常简单的工作示例,您可以填补现有与此之间的空白。

所以.s

.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang

.thumb_func
reset:
    @bl notmain
    b hang

.thumb_func
hang:   b .

so.ld

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

构建

arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -o so.elf -T so.ld so.o
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf so.bin -O binary

arm-whatever-linux-whatever 也可以...

00000000 <_start>:
   0:   20001000
   4:   00000041
   8:   00000043
   c:   00000043
  10:   00000043
  14:   00000043
  18:   00000043
  1c:   00000043
  20:   00000043
  24:   00000043
  28:   00000043
  2c:   00000043
  30:   00000043
  34:   00000043
  38:   00000043
  3c:   00000043

00000040 <reset>:
  40:   e7ff        b.n 42 <hang>

00000042 <hang>:
  42:   e7fe        b.n 42 <hang>

将 _start 移到最前面并不是使它与零对齐的原因,在 ld 命令行上首先使用 so.o 就是这样做的,因为没有其他对象,它是命令行上唯一的对象,但是如果链接器脚本没有调用特定的文件名,然后它属于命令行排序。对于这种工作,你不关心 _start 在哪里,你关心向量表在哪里。入口点由芯片定义,您不是从使用 _start 作为入口点的操作系统加载这些程序。

.thumb_func 是告诉汇编程序下一个标签是一个函数的最便宜的方法(因此地址与 1 或),您可以使用更详细的语法来声明一个过程或函数,检查程序集C 编译函数的输出,以查看更详细的执行方式并选择您喜欢的方式。

.thumb 与 .code 16 应该可以互换。最终,如果使用 cortex-m3 或 m4 或(更大的数字,不是 cortex-m0 或 m1),您将需要指定

.cpu cortex-m3

-mcpu=cortex-m3

在 gas 和 gcc 命令行上。

似乎您的链接器脚本移动了 _start,或者您的二进制文件中有其他内容您没有向我们展示,或者您可能有多个文件来创建您向我们展示的内容,但不知道您没有提供足够的信息。我们应该能够接受您提供的内容并能够重复...

编辑

对于一个使用 cortex-m 芯片的特定品牌,用户闪存实际上位于 0x08000000,当您以该模式启动时,他们将其映射到 0x00000000。窗口不知道有多大,所以你可以为 .text 地址空间为 0x00000000 或 0x08000000 构建它,这显示了更改链接描述文件如何更改输出。

flash.s

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang @ NMI
.word hang @ HardFault
.word hang @ MemManage
.word hang @ BusFault
.word hang @ UsageFault
.word hang @ 7
.word hang @ 8
.word hang @ 9
.word hang @ 10
.word hang @ SVCall
.word hang @ DebugMonitor
.word hang @ Reserved
.word hang @ PendSV
.word hang @ SysTick
.word hang @ External interrupt 0
.word hang @ External interrupt 1
.word hang @ External interrupt 2

.thumb_func
reset:
    bl notmain
    b hang

.thumb_func
hang:   b .

.thumb_func
.globl dummy
dummy:
    bx lr

notmain.c

extern void dummy ( unsigned int );
void notmain ( void )
{
    unsigned int ra;
    for(ra=0;ra<100;ra++) dummy(ra);
}

flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

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

构建

arm-none-eabi-gcc -Wall -O2 -nostdlib -nostartfiles -ffreestanding  -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -o notmain.elf -T flash.ld flash.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary

此外,它还包含一些链接的 C 代码。

因为 flash.o 在链接器命令行上位于 notmain.o 之前,所以它首先进入 .text(使用这种通用的链接器脚本)。

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   0800004d    stmdaeq r0, {r0, r2, r3, r6}
 8000008:   08000053    stmdaeq r0, {r0, r1, r4, r6}
 800000c:   08000053    stmdaeq r0, {r0, r1, r4, r6}
 8000010:   08000053    stmdaeq r0, {r0, r1, r4, r6}
 8000014:   08000053    stmdaeq r0, {r0, r1, r4, r6}
 8000018:   08000053    stmdaeq r0, {r0, r1, r4, r6}
 800001c:   08000053    stmdaeq r0, {r0, r1, r4, r6}
 8000020:   08000053    stmdaeq r0, {r0, r1, r4, r6}
 8000024:   08000053    stmdaeq r0, {r0, r1, r4, r6}
 8000028:   08000053    stmdaeq r0, {r0, r1, r4, r6}

请注意,.text 现在从链接描述文件的 0x08000000 开始。这很好,因为它会被这个硬件映射到 0x00000000,它将读取地址 0x00000004 处的值,即 0x0800004d 并跳转到 0x0800004c

0800004c <reset>:
 800004c:   f000 f804   bl  8000058 <notmain>
 8000050:   e7ff        b.n 8000052 <hang>

08000052 <hang>:
 8000052:   e7fe        b.n 8000052 <hang>

08000054 <dummy>:
 8000054:   4770        bx  lr
    ...

08000058 <notmain>:
 8000058:   b510        push    {r4, lr}
 800005a:   2400        movs    r4, #0
 800005c:   1c20        adds    r0, r4, #0
 800005e:   3401        adds    r4, #1
 8000060:   f7ff fff8   bl  8000054 <dummy>
 8000064:   2c64        cmp r4, #100    ; 0x64
 8000066:   d1f9        bne.n   800005c <notmain+0x4>
 8000068:   bd10        pop {r4, pc}
 800006a:   46c0        nop         ; (mov r8, r8)

现在,如果您删除 hang 和 dummy 前面的 .thumb_func,那么使用 dummy 我们很幸运,它可以解决

8000060: f7ff fff8 bl 8000054

因为使用了分支链接,所以假设是相同的模式,但是 hang 前面的那个导致向量表错误

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   0800004d    stmdaeq r0, {r0, r2, r3, r6}
 8000008:   08000052    stmdaeq r0, {r1, r4, r6}
 800000c:   08000052    stmdaeq r0, {r1, r4, r6}
 8000010:   08000052    stmdaeq r0, {r1, r4, r6}

在 gcc 命令行上使用 -c 并将我自己与我自己的链接器脚本链接和/或使用 -Xlinker gcc 选项为链接器提供链接器脚本(并注意 gcc 命令行上的文件排序)你可以控制您必须在此处执行的链接器脚本,因为在我看来,您的目标是针对 cortex-m 处理器而不是编写 arm linux 应用程序。可以对编译器和汇编器进行一般处理,但您希望避免使用默认引导程序(gnu 世界中的 crt0.o)和链接器脚本,除非您的 gnu 构建直接针对您的特定平台(默认适用于您的平台)。如果需要,如果您使用 C 库,则可以在构建 C 库时执行此操作。使用裸机,您没有一个系统来调用如此多的 C 库崩溃。

【讨论】:

  • 实际上看到您使用 arm-none-linux-gnueabi 您可能没有制作自己的链接器脚本,因此使用了默认的 0x8000 起始地址,然后您拥有了向量,因此 _start 位于末尾,所以这一切都说得通。但不会工作,您需要使用自己的链接器脚本,用您的代码重复您的实验,但使用我的链接器脚本或自己制作,用 arm-linux-gnueabi 替换 arm-none-eabi 并使用相同的命令,您的汇编语言我的链接器脚本。
  • 您好,非常感谢您的回复。我正在研究您发布的内容,可能需要更多时间来理解它。但我请客 so.ld 可能是小费。我没有创建这个文件。你能告诉我它的功能是什么吗?谢谢
  • 在这种情况下,它是链接器的“链接器脚本”。通常有一个默认的,毫无疑问,arm-linux - 无论有一个链接器脚本创建在 arm linux 上运行的应用程序,这意味着入口点位于 0x8000 或 0x80000,某处有一个默认的链接器脚本。当您调用 gcc 时,它会在编译后调用汇编器,然后调用链接器,除非您告诉它不要这样做。
  • 如果您在 rom 原点行中将地址 0x00000000 更改为 0x00003000,那么在这种情况下,向量表的文本开头将位于 0x3000 而不是我们想要的 0x0000。
  • 同样,如果我有一些全局变量,它们将在编译/链接时分配以从地址 0x20000000(基本上是 .data)开始,因为链接器脚本告诉链接器这样做。您可以不使用链接器脚本并将 -Ttext=0x00000000 -Tdata=0x20000000 传递给链接器,但是我发现这样做有一些缺点,而链接器脚本却没有。由于该程序员所需的原因,您发现的大多数链接器脚本都会更加复杂,我几乎不需要任何这些。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多