1

我想在我的代码中看到一个函数的地址,所以我写了一个这样的 hello world:

#include <stdio.h>

void myfn() {
  printf("I am myfn1\n");
  printf("I am myfn2\n");
  printf("I am myfn3\n");
  printf("I am myfn4\n");
  printf("I am myfn5\n");
}

typedef void (*MYFN)();

int main() {
  MYFN fn = (MYFN)myfn;
  printf("addr of fn: 0x%08X\n", (unsigned int)fn);
  fn();
  printf("just for %s\n", "test");
  return 0;
}

结果是:

# ./test
addr of fn: 0x00008461
I am myfn1
I am myfn2
I am myfn3
I am myfn4
I am myfn5
just for test

那么,myfn 的地址是0x00008461吗?

然后我使用 objdump 转储它:

84ae:   f7ff efb8   blx 8420 <printf@plt>
84b2:   f7ff ffd5   bl  8460 <printf@plt+0x40>
84b6:   4807        ldr r0, [pc, #28]   ; (84d4 <printf@plt+0xb4>)
84b8:   4907        ldr r1, [pc, #28]   ; (84d8 <printf@plt+0xb8>)
84ba:   4478        add r0, pc
84bc:   4479        add r1, pc
84be:   f7ff efb0   blx 8420 <printf@plt>

从那, myfn 的地址是0x8460?附近:

8460:   480a        ldr r0, [pc, #40]   ; (848c <printf@plt+0x6c>)
8462:   b510        push    {r4, lr}
8464:   4478        add r0, pc
8466:   f7ff efd6   blx 8414 <puts@plt>
846a:   4809        ldr r0, [pc, #36]   ; (8490 <printf@plt+0x70>)
846c:   4478        add r0, pc
846e:   f7ff efd2   blx 8414 <puts@plt>
8472:   4808        ldr r0, [pc, #32]   ; (8494 <printf@plt+0x74>)
8474:   4478        add r0, pc
8476:   f7ff efce   blx 8414 <puts@plt>
847a:   4807        ldr r0, [pc, #28]   ; (8498 <printf@plt+0x78>)
847c:   4478        add r0, pc
847e:   f7ff efca   blx 8414 <puts@plt>
8482:   4806        ldr r0, [pc, #24]   ; (849c <printf@plt+0x7c>)

我想知道真实地址是0x8460还是0x8461还是0x8462?请帮我...

4

1 回答 1

1

这是拇指密码。阅读 ARM ARM 和 TRM(架构参考手册和技术参考手册)。

特别是 BX 和 BLX 指令。当分支到使用 thumb 指令(和/或 thumb2 扩展)的代码时,使用 bx 或 blx 指令,特别是在这里,因为编译器在编译时不知道 printf() 函数是 thumb 还是 arm 模式,所以它有要使用 bx 或 blx 进行编码,如果它正在分支到当时正在编译的东西,它可以使用例如条件分支。当使用 bx 或 blx 时,lsbit 告诉指令它是调用 ARM 指令(lsbit 为 0)还是 thumb 指令(lsbit 为 1)。在拇指模式下,程序计数器不会保持 lsbit 设置,它会被 bx/blx 指令剥离。

链接器通过并知道哪些功能是哪种模式,并将填写适当的地址。因此,该函数在内存中从地址 0x8460 开始,但是要使用 bx 或 blx 进行分支(调用),您需要使用地址 0x8461,因为这些是拇指模式指令。

编译器不知道为什么你需要函数的地址,几乎每个地方链接器都需要填写它需要根据模式控制 lsbit 的地址,所以显然它将它设置为一个。

有问题的地址是 0x8460。如果您有某些理由需要真实地址而不是地址调用,只需去掉 lsbit。

printf("addr of fn: 0x%08X\n", (unsigned int)(fn&(~1)));
于 2013-09-12T02:08:24.637 回答