Search code examples
androidffmpegandroid-ndkclangandroid-ffmpeg

Why do I get "error: invalid instruction mnemonic" when compiling FFmpeg for Android?


I'm trying to compile FFmpeg 4.0 for Android using clang from the NDK (r18.1.5063045). My host architecture is Mac OS.

Here's my configure command:

./configure \
--target-os=android \
--arch=armv7-a \
--enable-cross-compile \
--cc=${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang \
--enable-shared \
--disable-static 

This completes successfully. So I run make -j8 and get the following error:

In file included from libavfilter/vf_mestimate.c:22:
In file included from ./libavcodec/mathops.h:40:
./libavcodec/arm/mathops.h:90:26: error: invalid instruction mnemonic 'itt'
    "cmp   %1, %2  \n\t"
                     ^
<inline asm>:3:2: note: instantiated into assembly here
    itt   gt      
    ^~~

Any idea what could be causing this?


Solution

  • I suspect Clang is targeting the host OS rather than arm32. Using a standalone toolchain fixes that problem. I noticed that the build system was still using my host strip tool (which can't handle the arm binaries), so I fixed that by passing --strip to ffmpeg's configure script.

    ${ANDROID_NDK}/build/tools/make_standalone_toolchain.py \
        --arch arm --api 16 --install-dir /tmp/standalone-toolchain
    
    ./configure \
    --target-os=android \
    --arch=armv7-a \
    --enable-cross-compile \
    --cc=/tmp/standalone-toolchain/bin/clang \
    --strip=/tmp/standalone-toolchain/bin/arm-linux-androideabi-strip \
    --enable-shared \
    --disable-static 
    
    make -j...
    

    The build then failed with an error in libavdevice/v4l2.c:

    CC  libavdevice/v4l2.o
    libavdevice/v4l2.c:135:9: error: assigning to 'int (*)(int, unsigned long, ...)' from incompatible type
          '<overloaded function type>'
            SET_WRAPPERS();
            ^~~~~~~~~~~~~~
    libavdevice/v4l2.c:121:17: note: expanded from macro 'SET_WRAPPERS'
        s->ioctl_f  = prefix ## ioctl;      \
                    ^           ~~~~~
    /tmp/standalone-toolchain/bin/../sysroot/usr/include/bits/ioctl.h:56:5: note: candidate function has type mismatch at 2nd
          parameter (expected 'unsigned long' but has 'unsigned int')
    int ioctl(int __fd, unsigned __request, ...) __overloadable __enable_if(1, "") __RENAME(ioctl);
        ^
    /tmp/standalone-toolchain/bin/../sysroot/usr/include/bits/ioctl.h:36:5: note: candidate function has type mismatch at 2nd
          parameter (expected 'unsigned long' but has 'int')
    int ioctl(int __fd, int __request, ...);
        ^
    

    There's disagreement about the type of ioctl's second parameter. glibc declares it with type unsigned long:

    int ioctl(int fd, unsigned long request, ...);
    

    POSIX and Bionic (and musl) declare it with int:

    int ioctl(int fildes, int request, ... /* arg */);
    

    A similar problem happened with PulseAudio and musl, so I applied the same kind of fix to ffmpeg:

    diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c
    index 10a0ff0dd6..1b9c6e760b 100644
    --- a/libavdevice/v4l2.c
    +++ b/libavdevice/v4l2.c
    @@ -95,7 +95,11 @@ struct video_data {
         int (*open_f)(const char *file, int oflag, ...);
         int (*close_f)(int fd);
         int (*dup_f)(int fd);
    +#ifdef __GLIBC__
         int (*ioctl_f)(int fd, unsigned long int request, ...);
    +#else
    +    int (*ioctl_f)(int fd, int request, ...);
    +#endif
         ssize_t (*read_f)(int fd, void *buffer, size_t n);
         void *(*mmap_f)(void *start, size_t length, int prot, int flags, int fd, int64_t offset);
         int (*munmap_f)(void *_start, size_t length);
    

    Afterwards, the project built successfully.

    FWIW: The project also built for arm64 using these changes:

    • standalone toolchain: --arch arm64
    • standalone toolchain: --api 21
    • configure: --arch=aarch64
    • configure: --strip=/.../aarch64-linux-android-strip