gcc - 间接函数调用使用奇数地址

标签 gcc arm cortex-m

当 ARM Cortex-M3 的 GCC 4.7.3 (20121207) 获取函数地址时,它不会获得该函数的确切地址。我可以在该指针中看到一个相差一的值。

// assume at address 0x00001204;
int foo() {
  return 42;
}

void bar() {
  int(*p)() = &foo;  // p = 0x1205;
  p();               // executed successfully
  foo();             // assembly: "bl 0x00001204;"
}

虽然指针指向奇数地址,但执行成功。我预计此时会有异常(exception)。为什么它需要那个奇怪的地址,为什么它不会造成伤害。

编辑

  • SO article描述拇指模式和 ARM 模式之间的区别。为什么在CPU处于相同模式下直接调用函数时该偏移量不可见?
  • 应该保留奇数地址还是重置位 0 会导致困难? (直到现在我还看不到)

最佳答案

我从我的一个示例中拼凑出一些东西来快速演示正在发生的事情。

vectors.s:

/* vectors.s */
.cpu cortex-m3
.thumb

.word   0x20002000  /* stack top address */
.word   _start      /* 1 Reset */
.word   hang        /* 2 NMI */
.word   hello       /* 3 HardFault */
.word   hang        /* 4 MemManage */
.word   hang        /* 5 BusFault */
.word   hang        /* 6 UsageFault */
.word   hang        /* 7 RESERVED */
.word   hang        /* 8 RESERVED */
.word   hang        /* 9 RESERVED*/
.word   hang        /* 10 RESERVED */
.word   hang        /* 11 SVCall */
.word   hang        /* 12 Debug Monitor */
.word   hang        /* 13 RESERVED */
.word   hang        /* 14 PendSV */
.word   hang        /* 15 SysTick */
.word   hang        /* 16 External Interrupt(0) */
.word   hang        /* 17 External Interrupt(1) */
.word   hang        /* 18 External Interrupt(2) */
.word   hang        /* 19 ...   */

.thumb_func
.global _start
_start:
    /*ldr r0,stacktop */
    /*mov sp,r0*/
    bl notmain
    ldr r0,=notmain
    mov lr,pc
    bx r0
    b hang

.thumb_func
hang:   b .

hello: b .

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.end

blinker01.c:

extern void PUT32 ( unsigned int, unsigned int );

int notmain ( void )
{
    PUT32(0x12345678,0xAABBCCDD);
    return(0);
}

生成文件:

#ARMGNU = arm-none-eabi
ARMGNU = arm-none-linux-gnueabi

AOPS = --warn --fatal-warnings 
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding 


all : blinker01.gcc.thumb.bin 

vectors.o : vectors.s
    $(ARMGNU)-as vectors.s -o vectors.o

blinker01.gcc.thumb.o : blinker01.c
    $(ARMGNU)-gcc $(COPS) -mthumb -c blinker01.c -o blinker01.gcc.thumb.o

blinker01.gcc.thumb2.o : blinker01.c
    $(ARMGNU)-gcc $(COPS) -mthumb -mcpu=cortex-m3 -march=armv7-m -c blinker01.c -o blinker01.gcc.thumb2.o

blinker01.gcc.thumb.bin : memmap vectors.o blinker01.gcc.thumb.o
    $(ARMGNU)-ld -o blinker01.gcc.thumb.elf -T memmap vectors.o blinker01.gcc.thumb.o
    $(ARMGNU)-objdump -D blinker01.gcc.thumb.elf > blinker01.gcc.thumb.list
    $(ARMGNU)-objcopy blinker01.gcc.thumb.elf blinker01.gcc.thumb.bin -O binary

反汇编:

Disassembly of section .text:

08000000 <_start-0x50>:
 8000000:   20002000    andcs   r2, r0, r0
 8000004:   08000051    stmdaeq r0, {r0, r4, r6}
 8000008:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 800000c:   0800005e    stmdaeq r0, {r1, r2, r3, r4, r6}
 8000010:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000014:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000018:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 800001c:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000020:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000024:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000028:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 800002c:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000030:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000034:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000038:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 800003c:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000040:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000044:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 8000048:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}
 800004c:   0800005d    stmdaeq r0, {r0, r2, r3, r4, r6}

08000050 <_start>:
 8000050:   f000 f80a   bl  8000068 <notmain>
 8000054:   4803        ldr r0, [pc, #12]   ; (8000064 <PUT32+0x4>)
 8000056:   46fe        mov lr, pc
 8000058:   4700        bx  r0
 800005a:   e7ff        b.n 800005c <hang>

0800005c <hang>:
 800005c:   e7fe        b.n 800005c <hang>

0800005e <hello>:
 800005e:   e7fe        b.n 800005e <hello>

08000060 <PUT32>:
 8000060:   6001        str r1, [r0, #0]
 8000062:   4770        bx  lr
 8000064:   08000069    stmdaeq r0, {r0, r3, r5, r6}

08000068 <notmain>:
 8000068:   b508        push    {r3, lr}
 800006a:   4803        ldr r0, [pc, #12]   ; (8000078 <notmain+0x10>)
 800006c:   4903        ldr r1, [pc, #12]   ; (800007c <notmain+0x14>)
 800006e:   f7ff fff7   bl  8000060 <PUT32>
 8000072:   2000        movs    r0, #0
 8000074:   bd08        pop {r3, pc}
 8000076:   46c0        nop         ; (mov r8, r8)
 8000078:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
 800007c:   aabbccdd    bge 6ef33f8 <_start-0x110cc58>

首先请注意 hanghello ,这是一种 gnuism,您需要在汇编中将标签声明为拇指函数,以便它能够真正用于此类事情。 hang 已正确声明,向量表正确使用奇数地址,hello 未正确声明,偶数地址放入其中。 C 编译代码会自动正确执行此操作。

这是您所要求的一个主要示例,C 函数 blnotmain 不会、不能使用奇数地址。但是要使用bx,您需要提供函数main的地址,并且该地址作为地址0x8000068处的函数的0x8000069提供给代码,如果您在ARMvsometingT上对0x800068执行bx,它将切换到arm模式并最终崩溃如果它在 cortex-m 上达到拇指模式(希望崩溃而不是绊倒),则 bx 到偶数地址应该立即出错。

08000050 <_start>:
 8000050:   f000 f80a   bl  8000068 <notmain>
 8000054:   4803        ldr r0, [pc, #12]   ; (8000064 <PUT32+0x4>)
 8000056:   46fe        mov lr, pc
 8000058:   4700        bx  r0
 800005a:   e7ff        b.n 800005c <hang>
 8000064:   08000069    stmdaeq r0, {r0, r3, r5, r6}

为什么 bl 不能是奇数?看看上面的 bl 从 0x8000050 到 0x8000068 的编码,pc 领先 2 个字节,所以 4 个字节,所以将 0x8000068 - 0x8000054 = 0x14 除以 2,得到 0x00A。这是 pc 的偏移量,也是指令中编码的内容(指令后半部分的 0A)。除以二是基于拇指指令总是 2 字节(当时是这样)的知识,因此如果将偏移量放在 2 字节指令而不是字节中,它们可以达到两倍的距离。因此 lsbit 丢失了两者之间的增量,因此由硬件控制。

您的代码所做的是在一个地方询问拇指函数的地址,该函数给出奇数地址,另一种情况是查看始终为偶数的分支链接的反汇编。

关于gcc - 间接函数调用使用奇数地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15764833/

相关文章:

c - 重置 gcc 的 asm 内联输入

c - 为什么我们像这样在微 Controller 编程中寻址寄存器

embedded - Linux 上的 ARM 开发

c - 从 Cortex M0+ 上的硬故障中恢复

c++ - gcc 堆栈优化

c - 实现动态内存管理功能

gcc - ARM gentoo crossdev 与 uclibc : need OABI rather than EABI

macos - Android Studio 能否在配备 ARM 处理器的 Mac 上运行?

c - 在 Keil uVision 中使用 STM32F429 探索板的 IIR 低通滤波器

gcc - 如何在没有共享库的情况下使用 GMP、MPFR、MPC、ELF 逐个安装 GCC?