Fix data roots registration for Android/x86 and NDK ARM 'gold' linker
authorIvan Maidanski <ivmai@mail.ru>
Thu, 26 Dec 2013 21:41:05 +0000 (01:41 +0400)
committerIvan Maidanski <ivmai@mail.ru>
Thu, 26 Dec 2013 21:41:05 +0000 (01:41 +0400)
* include/gc.h (_etext, __dso_handle): Declare weak symbol (only if
Android).
* os_dep.c (_etext, __dso_handle): Likewise.
* include/gc.h (GC_INIT_CONF_ROOTS): If __data_start is incorrect
(less than _etext or missing but __dso_handle (which is typically
located at data section start) is greater than _etext as observed for
code produced by "gold" linker of Android NDK r9b) then use
__dso_handle as the lowest bound of registered data root instead of
__data_start (only if Android); update comment.
* os_dep.c (GC_init_linux_data_start): Likewise.
* os_dep.c (__data_start, data_start): Reformat code.

include/gc.h
os_dep.c

index ff8ff3293093a1bef18d42b12450517fea316d27..9af396eb012f87a274b725bb77643413c8e07ff6 100644 (file)
@@ -1614,11 +1614,23 @@ GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void);
 # define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND)
 #elif (defined(PLATFORM_ANDROID) || defined(__ANDROID__)) \
       && !defined(GC_NOT_DLL)
-  /* Required if GC is built as shared lib with -D IGNORE_DYNAMIC_LOADING. */
 # pragma weak __data_start
   extern int __data_start[], _end[];
-# define GC_INIT_CONF_ROOTS (void)((GC_word)(__data_start) != 0 ? \
-                                (GC_add_roots(__data_start, _end), 0) : 0)
+# pragma weak _etext
+# pragma weak __dso_handle
+  extern int _etext[], __dso_handle[];
+  /* Explicitly register caller static data roots (__data_start points  */
+  /* to the beginning typically but NDK "gold" linker could provide it  */
+  /* incorrectly, so the workaround is to check the value and use       */
+  /* __dso_handle as an alternative data start reference if provided).  */
+  /* It also works for Android/x86 target where __data_start is not     */
+  /* defined currently (regardless of linker used).                     */
+# define GC_INIT_CONF_ROOTS \
+                (void)((GC_word)__data_start < (GC_word)_etext \
+                        && (GC_word)_etext < (GC_word)__dso_handle ? \
+                            (GC_add_roots(__dso_handle, _end), 0) : \
+                       (GC_word)__data_start != 0 ? \
+                            (GC_add_roots(__data_start, _end), 0) : 0)
 #else
 # define GC_INIT_CONF_ROOTS /* empty */
 #endif
index 2730205dcf20e0570f20bed8da5b3c5f1ede08b3..7fbeda3e5d912729f72853a44540155dacde67be 100644 (file)
--- a/os_dep.c
+++ b/os_dep.c
@@ -441,11 +441,15 @@ GC_INNER char * GC_get_maps(void)
     /* define data_start as a weak symbol.  The latter is technically   */
     /* broken, since the user program may define data_start, in which   */
     /* case we lose.  Nonetheless, we try both, preferring __data_start.*/
-    /* We assume gcc-compatible pragmas.        */
+    /* We assume gcc-compatible pragmas.                                */
 #   pragma weak __data_start
-    extern int __data_start[];
 #   pragma weak data_start
-    extern int data_start[];
+    extern int __data_start[], data_start[];
+#   ifdef PLATFORM_ANDROID
+#     pragma weak _etext
+#     pragma weak __dso_handle
+      extern int _etext[], __dso_handle[];
+#   endif
 # endif /* LINUX */
   extern int _end[];
 
@@ -457,6 +461,19 @@ GC_INNER char * GC_get_maps(void)
   {
 #   if (defined(LINUX) || defined(HURD)) && !defined(IGNORE_PROG_DATA_START)
       /* Try the easy approaches first: */
+#     ifdef PLATFORM_ANDROID
+        /* Workaround for "gold" (default) linker (as of Android NDK r9b).      */
+        if ((word)__data_start < (word)_etext
+            && (word)_etext < (word)__dso_handle) {
+          GC_data_start = (ptr_t)(__dso_handle);
+#         ifdef DEBUG_ADD_DEL_ROOTS
+            GC_log_printf(
+                "__data_start is wrong; using __dso_handle as data start\n");
+#         endif
+          GC_ASSERT((word)GC_data_start <= (word)_end);
+          return;
+        }
+#     endif
       if ((ptr_t)__data_start != 0) {
           GC_data_start = (ptr_t)(__data_start);
           GC_ASSERT((word)GC_data_start <= (word)_end);