Prepare Mono for Android NDK with unified headers (#5680)
authorMarek Habersack <grendel@twistedcode.net>
Tue, 3 Oct 2017 18:17:26 +0000 (20:17 +0200)
committerGitHub <noreply@github.com>
Tue, 3 Oct 2017 18:17:26 +0000 (20:17 +0200)
Up until NDK release 14 Android SDK included a separate set of C header
files for each supported platform. NDK 14 introduced a new set of those
headers (they are NOT the same as the old ones) which unifies support
for all of the platforms so that bug fixes are available for all the
API levels without having to backport etc. With NDK 15 the unified headers
become the default ones (including when creating standalone toolchains)
and with NDK 16 (in beta currently) the old per-platform headers are removed.

Unfortunately, the new headers introduce breaking changes which made it
impossible to build Mono with NDK configured to use them. This commit makes
a handful of changes to Mono which make it build with unified headers.

16 files changed:
configure.ac
mono/metadata/w32socket-unix.c
mono/utils/mono-compiler.h
mono/utils/mono-complex.h
mono/utils/mono-threads-posix-signals.c
support/fcntl.c
support/libm/complex.h
support/libm/math_private.h
support/macros.c
support/mph.h
support/pwd.c
support/sys-mman.c
support/sys-sendfile.c
support/sys-stat.c
support/sys-statvfs.c
support/unistd.c

index bad8ecc8e3c8d08b5978acdf663d81535eb076be..9299a874b30fca75beed15d5bad9b629d81ba5cc 100644 (file)
@@ -554,6 +554,73 @@ AC_CHECK_FUNCS(_finite, , AC_MSG_CHECKING(for _finite in math.h)
 # for Linux statfs support
 AC_CHECK_HEADERS(linux/magic.h)
 
+# For Android NDK unified headers
+if test x$platform_android = xyes; then
+       AC_CHECK_HEADERS(machine/endian.h sys/endian.h)
+       AC_CHECK_HEADERS(android/legacy_signal_inlines.h, [have_android_signal_inlines=yes], [have_android_signal_inlines=no])
+
+       # Make sure SIGRT{MIN,MAX} work - they will fail to work with unified headers if building for
+       # API level < 21 *and* android/legacy_signal_inlines.h doesn't declare (and define) the required
+       # libc APIs to obtain values for SIGRT{MIN,MAX}. We perform the check only if android/legacy_signal_inlines.h
+       # is found because in other cases the macros will either work (for NDK < 14) or fail if the legacy header
+       # doesn't contain the required definitions (NDK 14)
+       if test x$have_android_signal_inlines = xyes; then
+               AC_MSG_CHECKING([Whether Android SIGRTMIN/SGRTMAX macros are valid])
+               AC_COMPILE_IFELSE([
+                       AC_LANG_PROGRAM([#include <android/legacy_signal_inlines.h>],[
+                               int i;
+                               for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
+                               }
+                       ])],[
+                               AC_MSG_RESULT(yes)
+                               android_sigrtminmax_work=yes
+                       ],[
+                               AC_MSG_RESULT(no)
+                               android_sigrtminmax_work=no
+                       ]
+               )
+
+               if test x$android_sigrtminmax_work = xno; then
+                       AC_MSG_ERROR([Android SIGRTMIN/SIGRTMAX macros don't work in this NDK])
+               fi
+       fi
+
+       # Attempt to detect whether we're using Android NDK unified headers
+       AC_CHECK_HEADERS(android/api-level.h, [have_android_api_level=yes], [have_android_api_level=no])
+       AC_CHECK_HEADERS(android/versioning.h, [have_android_versioning=yes], [have_android_versioning=no])
+
+       android_unified_headers=no
+       if test x$have_android_api_level = xyes; then
+               if test x$have_android_versioning = xyes; then
+                       AC_MSG_CHECKING([whether using Android NDK unified headers])
+
+                       # Both macros are defined only in the NDK unified headers
+                       AC_COMPILE_IFELSE([
+                               AC_LANG_PROGRAM([
+                                       #include <android/api-level.h>
+                                       #include <android/versioning.h>
+                               ],[
+                                       #if __ANDROID_API_O__ == 26 && defined(__INTRODUCED_IN)
+                                               return 0
+                                       #else
+                                               #error __ANDROID_API_O__ != 26 or the __INTRODUCED_IN macro not defined
+                                       #endif
+                               ])],[
+                                       AC_MSG_RESULT(yes)
+                                       android_unified_headers=yes
+                               ],[
+                                       AC_MSG_RESULT(no)
+                                       android_unified_headers=no
+                               ]
+                       )
+               fi
+       fi
+
+       if test x$android_unified_headers = xyes; then
+               AC_DEFINE(ANDROID_UNIFIED_HEADERS, 1, [Whether Android NDK unified headers are used])
+       fi
+fi # Android
+
 # not 64 bit clean in cross-compile
 if test "x$enable_wasm" = "xyes"; then
 AC_DEFINE(SIZEOF_VOID_P,4)
index 74e1fcceab52e3ea2d17a1eb0bdee60f2d4c98fc..28a5a63a5b42c892dd703e7d1f6f5a4f88d2e167 100644 (file)
@@ -54,6 +54,7 @@
 #include "fdhandle.h"
 #include "utils/mono-logger-internals.h"
 #include "utils/mono-poll.h"
+#include "utils/mono-compiler.h"
 
 typedef struct {
        MonoFDHandle fdhandle;
index 3e1b4108b4e2a3c9e27df66843593c7fdea0aff4..50a91886fc17690f5e4ed2df3e7458bd8f10c227 100644 (file)
@@ -10,6 +10,9 @@
  * compiler behaviours.
  */
 #include <config.h>
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
 
 #ifdef __GNUC__
 #define MONO_ATTR_USED __attribute__ ((__used__))
@@ -130,5 +133,37 @@ typedef SSIZE_T ssize_t;
 #define MONO_NO_SANITIZE_THREAD
 #endif
 
+/* Used when building with Android NDK's unified headers */
+#if defined(HOST_ANDROID)
+#if __ANDROID_API__ < 21
+
+typedef int32_t __mono_off32_t;
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#if !defined(mmap)
+/* Unified headers before API 21 do not declare mmap when LARGE_FILES are used (via -D_FILE_OFFSET_BITS=64)
+ * which is always the case when Mono build targets Android. The problem here is that the unified headers
+ * map `mmap` to `mmap64` if large files are enabled but this api exists only in API21 onwards. Therefore
+ * we must carefully declare the 32-bit mmap here without changing the ABI along the way. Carefully because
+ * in this instance off_t is redeclared to be 64-bit and that's not what we want.
+ */
+void* mmap (void*, size_t, int, int, int, __mono_off32_t);
+#endif /* !mmap */
+
+#ifdef HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
+
+#if !defined(sendfile)
+/* The same thing as with mmap happens with sendfile */
+ssize_t sendfile (int out_fd, int in_fd, __mono_off32_t* offset, size_t count);
+#endif /* !sendfile */
+
+#endif /* __ANDROID_API__ < 21 */
+#endif /* HOST_ANDROID */
+
 #endif /* __UTILS_MONO_COMPILER_H__*/
 
index b7488c7cf5214036b9720920751de1634391cce4..385eee9b1592d5fd88e81f0c161dff4783879948 100644 (file)
@@ -12,7 +12,7 @@
 #include <config.h>
 #include <glib.h>
 
-#if !defined (HAVE_COMPLEX_H)
+#if !defined (HAVE_COMPLEX_H) || (defined (ANDROID_UNIFIED_HEADERS) && __ANDROID_API__ < 23)
 #include <../../support/libm/complex.h>
 #else
 #include <complex.h>
index 5f762c83abe784c9b66e1457287e3e34ea1a2ac1..b39277393f5d87a979793202fcae936459086a32 100644 (file)
 #include <errno.h>
 #include <signal.h>
 
+#ifdef HAVE_ANDROID_LEGACY_SIGNAL_INLINES_H
+#include <android/legacy_signal_inlines.h>
+#endif
+
 #include "mono-threads-debug.h"
 
 gint
index 900a58f8104d724335b33e5cbdebb0490a64c79b..1fd324e399f1a2b684b81ab99425096f9f3b181c 100644 (file)
@@ -22,8 +22,8 @@
 #include <corecrt_io.h>
 #endif
 
+#include "mph.h" /* Don't remove or move after map.h! Works around issues with Android SDK unified headers */
 #include "map.h"
-#include "mph.h"
 
 G_BEGIN_DECLS
 
index b8a16a8b4bfb77384f2f9e38c75790ada32a9e22..2fc2284d63e8663df356a6da0826edb9e6063e0c 100644 (file)
 #ifndef _COMPLEX_H
 #define        _COMPLEX_H
 
+#include <config.h>
 #include <sys/cdefs.h>
 
+#if HOST_ANDROID && !defined(__pure2)
+/* NDK unified headers don't define __pure2 */
+#define __pure2 __attribute__((__const__))
+#endif
+
 #ifdef __GNUC__
 #if __STDC_VERSION__ < 199901
 #define        _Complex        __complex__
index b33cf60271a90b8a34c6e2893181d356379b0684..ddb615e15cdefe0ec8aab74cbbc89b8b74411d28 100644 (file)
 #define        _MATH_PRIVATE_H_
 
 #include <sys/types.h>
+
+#if HAVE_MACHINE_ENDIAN_H
 #include <machine/endian.h>
+#elif HAVE_SYS_ENDIAN_H && HOST_ANDROID
+/* Android unified headers don't have machine/endian.h */
+#include <sys/endian.h>
+#endif
 
 /*
  * The original fdlibm code used statements like:
index 8d455889d7ca4c30ea10c8a6e7c2471afcec7179..e15fa76b7edb0a35774956c4e1070f10e366fd35 100644 (file)
@@ -9,6 +9,7 @@
 #include <dirent.h>
 #include <string.h>
 #include <glib.h>
+#include "mph.h" /* Don't remove or move after map.h! Works around issues with Android SDK unified headers */
 #include "map.h"
 
 int wifexited (int status)
index e5992e5e71f03357080123ffb7a460016db8c488..2fbae6a27cddd04bcbc592ac70a1a501256ce27e 100644 (file)
 #include <errno.h>              /* for ERANGE */
 #include <glib.h>               /* for g* types, etc. */
 
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
 #ifdef HAVE_STDINT_H
 #include <stdint.h>             /* for SIZE_MAX */
 #endif
 
+#ifdef ANDROID_UNIFIED_HEADERS
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#undef st_atime_nsec
+#undef st_mtime_nsec
+#undef st_ctime_nsec
+
+#ifndef L_cuserid
+#define L_cuserid       9       /* size for cuserid(); UT_NAMESIZE + 1 */
+#endif
+
+/* NDK unified headers will define fpos_t to be 64-bit if large files support is
+ * enabled (which is the case with Mono) so we need to make sure the offsets here
+ * are actually 32-bit for Android APIs before API24 which did NOT have the 64-bit
+ * versions.
+ */
+#if !defined(fgetpos) && __ANDROID_API__ < 24
+int fgetpos(FILE*, fpos_t*);
+#endif
+
+#if !defined(fsetpos) && __ANDROID_API__ < 24
+int fsetpos(FILE*, const fpos_t*);
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+/* Unified headers define 'pw_gecos' to be an alias for 'pw_passwd` on 32-bit Android which
+ * results in two fields named 'pw_passwd' in map.h's 'struct passwd'
+ */
+#if !defined(__LP64__) && defined(pw_gecos)
+#undef pw_gecos
+#undef HAVE_STRUCT_PASSWD_PW_GECOS
+#endif
+
+#endif
+
 #if __APPLE__ || __BSD__ || __FreeBSD__ || __OpenBSD__
 #define MPH_ON_BSD
 #endif
index 6e3b68a208e9bc2340facb98262023033f9a2527..0e73af22bf9dac7825528da3a77a4f7e34ae4746 100644 (file)
@@ -13,8 +13,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "mph.h" /* Don't remove or move after map.h! Works around issues with Android SDK unified headers */
 #include "map.h"
-#include "mph.h"
 
 G_BEGIN_DECLS
 
index 143148f8ca3c0e345657eb62bd406252a157f935..ad74ef2ad02f4ec8e35e55ae0ed8a86ae1860e98 100644 (file)
@@ -31,6 +31,7 @@
 #include <sys/mman.h>
 #include <errno.h>
 
+#include "mono/utils/mono-compiler.h"
 #include "map.h"
 #include "mph.h"
 
index 7b6797acb04160a9b17fdc038389dfaece9dae5a..794341b58999b7bb363e97daa99633d53675b661 100644 (file)
@@ -12,6 +12,7 @@
 #include <sys/types.h>
 #include <errno.h>
 
+#include "mono/utils/mono-compiler.h"
 #include "map.h"
 #include "mph.h"
 
index 075563b0cf4d87681cce80a5126290ede1b8a97f..d6abce4a1facdfafdad71bd2f6022d217bfc8db0 100644 (file)
@@ -19,8 +19,8 @@
 #include <fcntl.h>
 #include <errno.h>
 
+#include "mph.h" /* Don't remove or move after map.h! Works around issues with Android SDK unified headers */
 #include "map.h"
-#include "mph.h"
 
 G_BEGIN_DECLS
 
index e03152a773d95d4f12fe1fc7ad78b240b1cb8f60..128baaa03b34c76cfc22dfdd02f732ad8bcb55d9 100644 (file)
@@ -33,6 +33,8 @@
 #include <unistd.h>     /* for pathconf */
 #endif /* def HAVE_GETFSSTAT */
 
+#include "mono/utils/mono-compiler.h"
+
 G_BEGIN_DECLS
 
 #ifdef HAVE_SYS_STATVFS_H
@@ -129,9 +131,11 @@ Mono_Posix_Syscall_fstatvfs (gint32 fd, struct Mono_Posix_Statvfs *buf)
  * BSD-compatible definitions.
  *
  * Linux also provides these, but are deprecated in favor of (f)statvfs.
+ * Android NDK unified headers define HAVE_FSTATFS but also HAVE_SYS_STATVFS_H
+ * which makes these duplicates of the functions defined above
  */
 
-#if (defined (HAVE_STATFS) || defined (HAVE_FSTATFS)) && !defined (HAVE_STATVFS)
+#if (defined (HAVE_STATFS) || defined (HAVE_FSTATFS)) && !defined (HAVE_STATVFS) && !defined(ANDROID_UNIFIED_HEADERS)
 int
 Mono_Posix_ToStatvfs (void *_from, struct Mono_Posix_Statvfs *to)
 {
@@ -197,7 +201,7 @@ set_fnamemax (int fd, struct Mono_Posix_Statvfs *buf)
 }
 #endif /* (def HAVE_STATFS || def HAVE_FSTATFS) && !def HAVE_STATVFS */
 
-#if !defined (HAVE_STATVFS) && defined (HAVE_STATFS)
+#if !defined (HAVE_STATVFS) && defined (HAVE_STATFS) && (!defined(ANDROID_UNIFIED_HEADERS) || __ANDROID_API__ >= 19)
 gint32
 Mono_Posix_Syscall_statvfs (const char *path, struct Mono_Posix_Statvfs *buf)
 {
@@ -218,7 +222,7 @@ Mono_Posix_Syscall_statvfs (const char *path, struct Mono_Posix_Statvfs *buf)
 }
 #endif /* !def HAVE_STATVFS && def HAVE_STATFS */
 
-#if !defined (HAVE_STATVFS) && defined (HAVE_STATFS)
+#if !defined (HAVE_STATVFS) && defined (HAVE_STATFS) && (!defined(ANDROID_UNIFIED_HEADERS) || __ANDROID_API__ >= 19)
 gint32
 Mono_Posix_Syscall_fstatvfs (gint32 fd, struct Mono_Posix_Statvfs *buf)
 {
index 0f560e28d00afcb0eaefd31c415b2ca018bdfc29..145de5192eb07e34b7893c457b926ee8508b1383 100644 (file)
@@ -21,8 +21,8 @@
 #include <limits.h>
 #include <string.h>     /* for swab(3) on Mac OS X */
 
+#include "mph.h" /* Don't remove or move after map.h! Works around issues with Android SDK unified headers */
 #include "map.h"
-#include "mph.h"
 
 G_BEGIN_DECLS