androidcmacosassemblyarm

How to elegantly support ARM assembly on both MacOS and Android?


I am currently writing a project using c language and arm assembly directly.

And I compiled the project for Android, everything was fine.

But when I compiled the project on MacOS (with Apple silicon), it failed. And I realized that the codes compiled on MacOS will be added a prefix _ to all symbols except for ASM codes.

I previously thought only ASM codes will be added a prefix, and after I took a deep look into the compiled codes, I found out that I was wrong.

For example:

int test_func();

int main() {
    printf("%d\n", test_func());
    return 0;
}
.global test_func
test_func:
    mov x0, x1
    ret

When compiling this code on MacOS, the main function is actually calling to _test_func() instead of test_func().

When compiling this code for Android, the test_func symbol is still itself.

So I am wondering if there is an elegant way to support both platforms without changing too much source code.


Solution

  • Sorry, I made a mistake about this problem.

    The problem is, on MacOS, it will automatically add _ prefix to all function, except our own assembly code.

    So when calling test_func() in the main(), it is actually calling to _test_func().

    I will edit this to previous question.

    Then we can keep all the function in c file unchanged, and define the assembly code like this:

    #ifdef __APPLE__
    #define DEFINE_FUNC(func) \
        .global _##func; \
        _##func
    #define END_FUNC(...) /*_*/
    #else
    #define DEFINE_FUNC(func)\
        .global func;   \
        .type func,%function; \
        func
    #define END_FUNC(func)\
        .size func,.-func;
    #endif
    
    DEFINE_FUNC(test_func):
        mov x0, x1
        ret
    END_FUNC(test_func)
    

    This #define macro seems good but it still failed when compiling.

    Then I realized that the multi-line #define will be extended to only one line (at least does so on MacOS and Linux). And the ; is treated as comment on MacOS, so only the first line in #define will be added to the compiled object.

    So the compiled object for previous example is like:

    // After extended
    .global _test_func; _test_func:
        mov x0, x1
        ret
    
    // After compiled
    .global _test_func
        mov, x0, x1
        ret
    

    So there is no symbol _test_func defined at all!

    Then I found another way to solve this -- using .macro instead of #define. The problem of #define is that we can't pass multi-line instruction, but .macro works fine:

    .macro define_func func
        .global _\func
        _\func:
    .endm
    
    #ifdef __APPLE__
    #define DEFINE_FUNC(func) define_func func
    #define END_FUNC(...) /*_*/
    #else
    #define DEFINE_FUNC(func)\
        .global func;   \
        .type func,%function; \
        func:
    #define END_FUNC(func)\
        .size func,.-func;
    #endif
    
    DEFINE_FUNC(test_func)
        mov x0, x1
        ret
    END_FUNC(test_func)
    

    So now I think I solved this problem elegantly. If there is a better way, please post your idea without any hesitation.