I've been working to port an established C/C++ library and daemon to the Android platform. The library uses C++ exceptions, which aren't supported by default on the Android platform. Currently, I'm running off 1.5 code, using the prebuilt arm-eabi-4.2.1 gcc suite distributed with the code.
The Android build scripts add the "-fno-rtti -fnoexceptions -Bsymbolic" options when compiling code in g++. These are all incompatible with exceptions in C++. I've modified the build the build scripts to disable these options on a per-module basis. I then ripped the exception code out of the GCC suite's libsupc++ C++ support library and built it as a stand-alone library, libsupc--.so. The net result of this was successful compilation and linking of code. However, once the library was built without these options, some of the
resultant dynamic symbols had the type R_ARM_REL32. Upon execution, the linker complained it was an unknown symbol type. Adding support for
these into the linker was relatively simple:
@@ -1230,10 +1230,17 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count)
case R_ARM_COPY:
COUNT_RELOC(RELOC_COPY);
MARK(rel->r_offset);
INFO("%5d RELO COPY %08x <- %d @ %08x %s\n", pid,
reloc, s->st_size, sym_addr, sym_name);
memcpy((void*)reloc, (void*)sym_addr, s->st_size);
break;
+ case R_ARM_REL32:
+ COUNT_RELOC(RELOC_RELATIVE);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "%5d RELO REL32 %08x <- %08x - %08x %s\n", pid,
+ reloc, sym_addr, rel->r_offset, sym_name);
+ *((unsigned*)reloc) += sym_addr - rel->r_offset;
+ break;
case R_ARM_NONE:
break;
However, the executable crashed as soon as it attempted to throw an exception. Further investigation isolated the it as a linker error when relocating R_ARM_COPY symbols in the executable. From the linker's TRACE statements (In all the output below, I decoded the mangled C++ names for clarity):
755 RELO COPY 0000d460 <- 44 @ 0000d460 [vtable for __cxxabiv1::__si_class_type_info]
755 RELO COPY 0000d490 <- 8 @ 0000d490 [typeinfo for std::exception]
755 RELO COPY 0000d498 <- 44 @ 0000d498 [vtable for __cxxabiv1::__class_type_info]
755 RELO COPY 0000d4c8 <- 8 @ 0000d4c8 [typeinfo for int]
755 RELO COPY 0000d4d0 <- 44 @ 0000d4d0 [vtable for __cxxabiv1::__class_type_info]
Inspecting these symbols shows they're allocated in both the libsupc--.so library and oilbath executable:
nathan@sandleford:~/cupcake/out/target/product/dream/system$ arm-eabi-readelf -rW lib/libsupc--.so
...
00007434 0000b502 R_ARM_ABS32 00007448 [typeinfo for int]
00007444 0000b502 R_ARM_ABS32 00007448 [typeinfo for int]
...
nathan@sandleford:~/cupcake/out/target/product/dream/system$ arm-eabi-readelf -rW bin/oilbath
...
0000d2ec 00006215 R_ARM_GLOB_DAT 0000d4c8 [typeinfo for int]
0000d4c8 00006214 R_ARM_COPY 0000d4c8 [typeinfo for int]
...
By looking at the TRACE statements it's plain that the symbols are being relocated from themselves, onto themselves--the memcpy() does nothing, and the vtables are full of null function pointers. When loading a dynamic object, /system/bin/linker looks up symbol names using the _do_lookup() function. It has the following section in it:
/* Look for symbols in the local scope first (the object who is
* searching). This happens with C++ templates on i386 for some
* reason. */
if (user_si) {
s = _do_lookup_in_so(user_si, name, &elf_hash);
if (s != NULL)
*base = user_si->base;
}
This is exactly what's happening...and in this case, it's exactly what we don't want to do. The symbol exists in the local context because we're about to copy into it, so we'll always find it. Thus we return the target symbol as the source symbol for the copy relocation.
The affected call is from within the reloc_library() function. My first attempt to fix this was to pass a null user_si pointer, however this caused the linking procedure to fail. Instead, I worked around this by adding a boolean check_local parameter to _do_lookup(). If this parameter is false it won't check user_si for the symbol location. Then, within reloc_library() the code becomes:
@@ -1121,7 +1121,11 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count)
DEBUG("%5d Processing '%s' relocation at index %d\n", pid,
si->name, idx);
if(sym != 0) {
- s = _do_lookup(si, strtab + symtab[sym].st_name, &base);
+#if defined(ANDROID_ARM_LINKER)
+ s = _do_lookup(si, strtab + symtab[sym].st_name, &base, type != R_ARM_COPY);
+#else
+ s = _do_lookup(si, strtab + symtab[sym].st_name, &base, 1);
+#endif
if(s == 0) {
ERROR("%5d cannot locate '%s'...\n", pid, sym_name);
return -1;
Suddenly, success! TRACE statements confirmed the symbols were being copied from libsupc--.so
777 RELO COPY 0000d460 <- 44 @ 801070a0 [vtable for __cxxabiv1::__si_class_type_info]
777 RELO COPY 0000d490 <- 8 @ 80107064 [typeinfo for std::exception]
777 RELO COPY 0000d498 <- 44 @ 801070d0 [vtable for __cxxabiv1::__class_type_info]
777 RELO COPY 0000d4c8 <- 8 @ 80107448 [typeinfo for int]
777 RELO COPY 0000d4d0 <- 44 @ 80107070 [vtable for __cxxabiv1::__class_type_info]
This seems like a slightly hacky fix, nor have I attempted to verify whether the prelinker is correct.
--nathan