how to use mkfifo using Android's NDK

Recently I upgraded the NDK and now my app crashes with missing symbol mkfifo:

E/dalvikvm(2031): dlopen("/data/app-lib/...mylib.so") failed: Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol "mkfifo" referenced by "mylib.so"...

The older platforms mkfifo was defined inline in sys/stat.h

static __inline__ int mkfifo(const char *__p, mode_t __m) {
    return mknod(__p, (__m & ~S_IFMT) | S_IFIFO, (dev_t)0);
}

But in platform version 21 it was changed to just an extern decleration:

extern int mkfifo(const char*, mode_t);

So that explains the missing symbol exception... my question is how do I fix it?


This happens if you build against the android-21 platform headers. Set APP_PLATFORM in jni/Application.mk to an older version, to build using the old headers, to make sure you only link to functions available earlier.

(Before android-21, the C library features and headers didn't really change significantly, so for the normal C library functions, it doesn't matter if you build targeting android-3 or android-20.)

This has been reported and is intentional behavior, see e.g. https://code.google.com/p/android/issues/detail?id=73725.

If you don't need to use new features from android-21, just build using older headers. (It doesn't matter that you're targeting an older platform version if you want to try to build for e.g. arm64-v8a or x86_64 that didn't exist before; ndk-build would build the 32 bit parts using the older target, and the 64 bit ones using the oldest target that supports them.)

In case you want to try to use new features from the android-21 platform conditionally if running on such a platform, you probably need to use dlopen and dlsym to load it conditionally anyway, so then you need to duplicate the other definitions from the new headers as well, to allow you to build using the older platform headers.


I've tried mstorsjo's fix and it seems to work, however I was a bit concerned that from the link he posted it seems that Google doesn't really think this is a good idea. As a result I did a bit more digging and it seems like the 'proper' fix is to ship multiple APKs, with (at least) one targeting android-20 and below, and another targeting android-21 and above.

The problem arises from a change in the NDK to force the use of the 'fPIE' option when performing an NDK build. From the NDK 10d release notes:

Introduced the requirement, starting from API level 21, to use -fPIE -pie when building. In API levels 16 and higher, ndk-build uses PIE when building. This change has a number of implications, which are discussed in Developer Preview Issue 888. These implications do not apply to shared libraries.

If you look at the Developer Preview Issue 888, it states the following:

Bionic commit 76e289c026f and its predecessor, 2aebf5429bb, require all dynamically linked native executables to be built with -fPIE -pie. This has a number of possibly-unintended side effects, including:

  • Old/unmaintained apps that worked correctly under KitKat may now fail to run under "L". This could affect even simple apps that do not use the network, handle untrusted data, or target the new "L" SDK API.

  • Actively maintained apps that wish to target "L" must ship PIE executables or static executables. If an app wants to target ICS or below, it must also ship non-PIE executables or static executables. Even a simple "Hello world" program built with -fPIE -pie will segfault on ICS and GB.

Obviously you may prefer to go with the previous solution but just thought it was worth noting.