Question

I have some simple code that finds the difference between two assembly labels:

#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;
}

Now, this code works perfectly on 64-bit processors, just like you would expect it to. However, on 32-bit processors, the address of foo_start is the same as foo_end.

I'm sure it has to do with 32 to 64 bit. On i386, it results in 0x0, and x86_64 results in 0x7. On ARMv7 (32 bit), it results in 0x0, while on ARM64, it results in 0xC. (the 64-bit results are correct, I checked them with a disassembler)

I'm using Clang+LLVM to compile.

I'm wondering if it has to do with non-lazy pointers. In the assembly output of both 32-bit processor archs mentioned above, they have something like this at the end:

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

However, this is not present in the assembly output of both x86_64 and ARM64. I messed with removing the non-lazy pointers and addressing the labels directly yesterday, but to no avail. Any ideas on why this happens?

EDIT:

It appears that when compiled for 32 bit processors, foo_start[] and foo_end[] point to main. I....I'm so confused.

Was it helpful?

Solution 2

I finally found the solution (or, alternative, I suppose). Apparently, the && operator can be used to get the address of C labels, removing the need for me to use inline assembly at all. I don't think it's in the C standard, but it looks like Clang supports it, and I've heard GCC does too.

#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;
}

Now, this only works if the labels are in the same function, but for what I intend to use this for, it's perfect. No more ASM, and it doesn't leave a symbol behind. It appears to work just how I need it to. (Not tested on ARM64)

OTHER TIPS

I didn't check on real code but suspect you are a victim of instruction reordering. As long as you do not define proper memory barriers, the compiler ist free to move your code within the function around as it sees fit since there is no interdependency between labels and printf() call.

Try adding ::: "memory" to your asm statements which should nail them where you wrote them.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top