32位处理器上的程序集标签地址不正确

时间:2014-03-29 15:59:06

标签: c assembly 32bit-64bit

我有一些简单的代码可以找到两个程序集标签之间的区别:

#include <stdio.h>


static void foo(void){
    __asm__ __volatile__("_foo_start:");
    printf("Hello, world.\n");
    __asm__ __volatile__("_foo_end:");
}


int main(void){
    extern const char foo_start[], foo_end[];
    printf("foo_start: %p, foo_end: %p\n", foo_start, foo_end);
    printf("Difference = 0x%tx.\n", foo_end - foo_start);
    foo();
    return 0;
}

现在,这段代码在64位处理器上完全 ,就像你期望的那样。但是,在32位处理器上,foo_start的地址与foo_end相同。

我确定它与32到64位有关。在i386上,它导致0x0,而x86_64导致0x7。在ARMv7(32位)上,它产生0x0,而在ARM64上,它产生0xC。 (64位结果是正确的,我用反汇编程序检查了它们)

我正在使用Clang + LLVM进行编译。

我想知道它是否与非惰性指针有关。在上面提到的两个32位处理器拱的汇编输出中,它们在最后有类似的东西:

L_foo_end$non_lazy_ptr:
    .indirect_symbol    _foo_end
    .long   0
L_foo_start$non_lazy_ptr:
    .indirect_symbol    _foo_start
    .long   0

但是,x86_64和ARM64的程序集输出中存在 not 。我昨天直接删除了非懒惰的指针并直接寻址标签,但无济于事。关于为什么会发生这种情况的任何想法?

编辑:

当为32位处理器编译时,foo_start []和foo_end []似乎指向 main 。我....我很困惑。

2 个答案:

答案 0 :(得分:1)

我没有检查真实代码,但怀疑你是指令重新排序的受害者。只要你没有定义适当的内存障碍,编译器就可以自由地在函数中移动代码,因为标签和printf()调用之间没有相互依赖。

尝试将::: "memory"添加到您的asm语句中,这些语句应该将它们钉在您编写它们的位置。

答案 1 :(得分:0)

我终于找到了解决方案(或者,替代方案,我想)。显然,&amp;&amp;运算符可用于获取C标签的地址,根本不需要我使用内联汇编。我不认为它符合C标准,但看起来Clang支持它,而且我也听说GCC也支持它。

#include <stdio.h>

int main(void){

foo_start:
    printf("Hello, world.\n");

foo_end:
    printf("Foo has ended.");

    void* foo_start_ptr = &&foo_start;
    void* foo_end_ptr = &&foo_end;

    printf("foo_start: %p, foo_end: %p\n", foo_start_ptr, foo_end_ptr);
    printf("Difference: 0x%tx\n", (long)foo_end_ptr - (long)foo_start_ptr);

    return 0;
}

现在,这仅适用于标签属于同一功能的情况,但对于我打算使用它的情况,它是完美的。不再是ASM,它不会留下任何符号。它似乎工作正常我需要它。 (未在ARM64上测试)