开发者

Android NDK produce unreasonable big binaries, how to optimize .so size?

I have notice that Android NDK (r6b in my case) produce unreasonable big resulting .so files. For example, in my case I have ~150-200 lines of C++ code (6 native methods and 3 C++ simplest classes) and this native code produce 60kb (!) .so with enabled exceptions and RTTI or 12kb .so with disabled exceptions and RTTI. Just to check I have compile hello-jni example included in NDK package and get 10kb .so for single-line native method from this example.

In my opinion it is somehow unreasonable overhead for mobile platform (on my desktop the comparable by size code produce ~10-15 times less .so).

  1. Are there tricks I should know to reduce the binaries size ?
  2. Why there is such overhead for C-only code ?
  3. And why there is even bigger overhead for C++ code with enabled exceptions and RTTI ?

Update #1: readelf output for hello-jni example from NDK

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0xc18
  Start of program headers:          52 (bytes into file)
  Start of section headers:          9344 (bytes into file)
  Flags:                             0x5000002, has entry point, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         5
  Size of section headers:           40 (bytes)
  Number of section headers:         19
  Section header string table index: 18

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .hash             HASH            000000d4 0000d4 0001a4 04   A  2   0  4
  [ 2] .dynsym           DYNSYM          00000278 000278 000420 10   A  3   3  4
  [ 3] .dynstr           STRTAB          00000698 000698 0004aa 00   A  0   0  1
  [ 4] .rel.dyn          REL             00000b44 000b44 000048 08   A  2   0  4
  [ 5] .rel.plt          REL             00000b8c 000b8c 000030 08   A  2   6  4
  [ 6] .plt              PROGBITS        00000bbc 000bbc 00005c 04  AX  0   0  4
  [ 7] .text             PROGBITS        00000c18 000c18 001518 00  AX  0   0  4
  [ 8] .rodata           PROGBITS        00002130 002130 000014 01 AMS  0   0  4
  [ 9] .ARM.extab        PROGBITS        00002144 002144 000024 00   A  0   0  4
  [10] .ARM.exidx        ARM_EXIDX       00002168 002168 0000e0 00  AL  7   0  4
  [11] .init_array       INIT_ARRAY      00003248 002248 000008 00  WA  0   0  1
  [12] .fini_array       FINI_ARRAY      00003250 002250 00000c 00  WA  0   0  1
  [13] .dynamic          DYNAMIC         0000325c 00225c 0000e8 08  WA  3   0  4
  [14] .got              PROGBITS        00003344 002344 000040 04  WA  0   0  4
  [15] .bss              NOBITS          00003390 002384 000010 00  WA  0   0 16
  [16] .comment          PROGBITS        00000000 002384 000036 00      0   0  1
  [17] .ARM.attributes   ARM_ATTRIBUTES  00000000 0023ba 000029 00      0   0  1
  [18] .shstrtab         STRTAB          00000000 0023e3 00009b 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x002168 0x00002168 0x00002168 0x000e0 0x000e0 R   0x4
  LOAD           0x000000 0x00000000 0x00000000 0x02248 0x02248 R E 0x1000
  LOAD           0x002248 0x00003248 0x00003248 0x0013c 0x00158 RW  0x1000
  DYNAMIC        0x00225c 0x0000325c 0x0000325c 0x000e8 0x000e8 RW  0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

 Section to Segment mapping:
  Segment Sections...
   00     .ARM.exidx 
   01     .hash .dynsym .dynstr .rel.dyn .rel.plt .plt .text .rodata .ARM.extab .ARM.exidx 
   02     .init_array .fini_array .dynamic .got .bss 
   03     .dynamic 
   04     

Dynamic section at offset 0x225c contains 25 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x0000000e (SONAME)                     Library soname: [libhello-jni.so]
 0x00000010 (SYMBOLIC)                   0x0
 0x00000019 (INIT_ARRAY)                 0x3248
 0x0000001b (INIT_ARRAYSZ)               8 (bytes)
 0x0000001a (FINI_ARRAY)                 0x3250
 0x0000001c (FINI_ARRAYSZ)               12 (bytes)
 0x00000004 (HASH)                       0xd4
 0x00000005 (STRTAB)                     0x698
 0x00000006 (SYMTAB)                     0x278
 0x0000000a (STRSZ)                      1194 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000003 (PLTGOT)                     0x3344
 0x00000002 (PLTRELSZ)                   48 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0xb8c
 0x00000011 (REL)                        0xb44
 0x00000012 (RELSZ)                      72 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x00000016 (TEXTREL)                    0x0
 0x6ffffffa (RELCOUNT)                   7
 0x00000000 (NULL)                       0x0

Relocation section '.rel.dyn' at offset 0xb44 contains 9 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000c24  00000017 R_ARM_RELATIVE   
00003254  00000017 R_ARM_RELATIVE   
00003368  00000017 R_ARM_RELATIVE   
0000336c  00000017 R_ARM_RELATIVE   
00003374  00000017 R_ARM_RELATIVE   
00003378  00000017 R_ARM_RELATIVE   
00003380  00000017 R_ARM_RELATIVE   
00003370  00001015 R_ARM_GLOB_DAT    00000000   __cxa_call_unexpected
0000337c  00003215 R_ARM_GLOB_DAT    00000000   __gnu_Unwind_Find_exid

Relocation section '.rel.plt' at offset 0xb8c contains 6 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00003350  00000d16 R_ARM_JUMP_SLOT   00000000   __cxa_begin_cleanup
00003354  00001216 R_ARM_JUMP_SLOT   00000000   memcpy
00003358  00001416 R_ARM_JUMP_SLOT   00000000   __cxa_finalize
0000335c  00001f16 R_ARM_JUMP_SLOT   00000000   abort
00003360  00002a16 R_ARM_JUMP_SLOT   00000000   __cxa_type_match
00003364  00003216 R_ARM_JUMP_SLOT   00000000   __gnu_Unwind_Find_exid

There are no unwind sections in this file.

Symbol table '.dynsym' contains 66 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000c18     0 SECTION LOCAL  DEFAULT    7 
     2: 00003390     0 SECTION LOCAL  DEFAULT   15 
     3: 00001c64    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_ForcedUnwind
     4: 00001668   164 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_RaiseExcepti
     5: 00001b20     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP
     6: 00001c40    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Resume_or_Rethrow
     7: 00002248     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_end
     8: 000011c0     8 FUNC    GLOBAL DEFA开发者_开发技巧ULT    7 __aeabi_unwind_cpp_pr0
     9: 00001d4c    44 FUNC    GLOBAL DEFAULT    7 _Unwind_GetRegionStart
    10: 00001c40    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Resume_or_Rethr
    11: 000033a0     0 NOTYPE  GLOBAL DEFAULT  ABS _bss_end__
    12: 00001c88    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Backtrace
    13: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_begin_cleanup
    14: 00001b04    20 FUNC    GLOBAL DEFAULT    7 __restore_core_regs
    15: 00001b40     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP_D_1
    16: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_call_unexpected
    17: 00000cf0     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetCFA
    18: 00000000     0 FUNC    GLOBAL DEFAULT  UND memcpy
    19: 00000c8c    76 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Set
    20: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize
    21: 00003250     0 NOTYPE  GLOBAL DEFAULT   12 __FINI_ARRAY__
    22: 00003384     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start__
    23: 00001404   212 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Backtrace
    24: 00003390     4 OBJECT  GLOBAL DEFAULT   15 __dso_handle
    25: 00001b30     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP_D
    26: 00001798   876 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Pop
    27: 000011b0     8 FUNC    WEAK   DEFAULT    7 __aeabi_unwind_cpp_pr2
    28: 00001c88    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Backtrace
    29: 00002168     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_start
    30: 00001bf8    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_RaiseException
    31: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort
    32: 00001c1c    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Resume
    33: 00001b48     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_WMMX
    34: 00001b18     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP
    35: 00001c1c    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Resume
    36: 00000cfc    32 FUNC    GLOBAL DEFAULT    7 _Unwind_DeleteException
    37: 00000cf8     4 FUNC    GLOBAL DEFAULT    7 _Unwind_Complete
    38: 000033a0     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_end__
    39: 00003248     0 NOTYPE  GLOBAL DEFAULT   11 __INIT_ARRAY__
    40: 00001d78   888 FUNC    GLOBAL DEFAULT    7 __gnu_unwind_execute
    41: 00001b28     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP_
    42: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_type_match
    43: 0000172c   108 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Resume
    44: 00001b38     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP_
    45: 00001bf8    36 FUNC    GLOBAL DEFAULT    7 _Unwind_RaiseException
    46: 00003384     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    47: 000033a0     0 NOTYPE  GLOBAL DEFAULT  ABS __end__
    48: 000015f4    28 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_ForcedUnwind
    49: 0000170c    32 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Resume_or_Re
    50: 00000000     0 FUNC    WEAK   DEFAULT  UND __gnu_Unwind_Find_exidx
    51: 00001b04    20 FUNC    GLOBAL DEFAULT    7 restore_core_regs
    52: 00001be4     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_WMMXC
    53: 00001d04     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetTextRelBase
    54: 00000c29    24 FUNC    GLOBAL DEFAULT    7 Java_com_example_hellojni
    55: 00001d14    56 FUNC    GLOBAL DEFAULT    7 _Unwind_GetLanguageSpecif
    56: 00000c40    76 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Get
    57: 00001bd0     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_WMMX
    58: 000020f0    64 FUNC    GLOBAL DEFAULT    7 __gnu_unwind_frame
    59: 00001c64    36 FUNC    GLOBAL DEFAULT    7 _Unwind_ForcedUnwind
    60: 00003384     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    61: 000033a0     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    62: 00001b8c     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_WMMXD
    63: 000011b8     8 FUNC    WEAK   DEFAULT    7 __aeabi_unwind_cpp_pr1
    64: 00001d0c     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetDataRelBase
    65: 00003384     0 NOTYPE  GLOBAL DEFAULT   14 __data_start

Histogram for bucket list length (total of 37 buckets):
 Length  Number     % of total  Coverage
      0  5          ( 13.5%)
      1  14         ( 37.8%)     22.2%
      2  9          ( 24.3%)     50.8%
      3  6          ( 16.2%)     79.4%
      4  2          (  5.4%)     92.1%
      5  1          (  2.7%)    100.0%

No version information found in this file.
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "5TE"
  Tag_CPU_arch: v5TE
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align8_needed: Yes
  Tag_ABI_align8_preserved: Yes, except leaf SP
  Tag_ABI_enum_size: int

You may see the difference from one provided by @Joel F, and especially stuff about stack unwinding (for C++ exceptions ?)

Update #2

The problem is in toolchain included in NDK r6b and in particular it is about of linker. Thanks to @Joel F for glues about previous NDK release. I have installed the NDK r5c alongside of NDK r6b and compare results. Compilers produce the same object files by both toolchains but after linking results are different.


EDIT 3 I was able to reproduce the 10KB hello-jni binary with NDK r6b. I found an awful hack here. Basically put this line in one of your files:

char __aeabi_unwind_cpp_pr0[0];

But your code will be lacking exception handling (which I thought was the point of -fno-exceptions....)

Anyways, this brings libhello-jni.so down to 2228 bytes for me. Still larger than r5c, but a lot less than 10KB.

EDIT 2 Yes it seems like you have a lot of C++ related overhead in your readelf output. Perhaps they changed something between r5c and r6b? There is no C++ code in r5c's hello-jni.c.

I have 160 lines of C code that NDK r5c reduces to a 3.8KB .so. Try the following to see what is responsible for the size:

/path/to/ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/<platform>/bin/arm-linux-androideabi-readelf -a libmylib.so

EDIT I built the hello-jni example with NDK r5c and the resulting libhello-jni.so file is 1588 bytes.

Build command:

ndk-build V=1

Build output: http://pastebin.com/AdRDVbnF (apparently SO has limits on line length or something).

Output from readelf -a libhello-jni.so:

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x2dc
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1108 (bytes into file)
  Flags:                             0x5000002, has entry point, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         5
  Size of section headers:           40 (bytes)
  Number of section headers:         12
  Section header string table index: 11

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .hash             HASH            000000d4 0000d4 00004c 04   A  2   0  4
  [ 2] .dynsym           DYNSYM          00000120 000120 0000e0 10   A  3   2  4
  [ 3] .dynstr           STRTAB          00000200 000200 0000db 00   A  0   0  1
  [ 4] .text             PROGBITS        000002dc 0002dc 00002c 00  AX  0   0  4
  [ 5] .rodata           PROGBITS        00000308 000308 000014 00   A  0   0  4
  [ 6] .ARM.exidx        ARM_EXIDX       0000031c 00031c 000008 00  AL  4   0  4
  [ 7] .dynamic          DYNAMIC         00001324 000324 000088 08  WA  3   0  4
  [ 8] .got              PROGBITS        000013ac 0003ac 00000c 04  WA  0   0  4
  [ 9] .comment          PROGBITS        00000000 0003b8 000012 00      0   0  1
  [10] .ARM.attributes   ARM_ATTRIBUTES  00000000 0003ca 000029 00      0   0  1
  [11] .shstrtab         STRTAB          00000000 0003f3 000061 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x00031c 0x0000031c 0x0000031c 0x00008 0x00008 R   0x4
  LOAD           0x000000 0x00000000 0x00000000 0x00324 0x00324 R E 0x1000
  LOAD           0x000324 0x00001324 0x00001324 0x00094 0x00094 RW  0x1000
  DYNAMIC        0x000324 0x00001324 0x00001324 0x00088 0x00088 RW  0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

 Section to Segment mapping:
  Segment Sections...
   00     .ARM.exidx 
   01     .hash .dynsym .dynstr .text .rodata .ARM.exidx 
   02     .dynamic .got 
   03     .dynamic 
   04     

Dynamic section at offset 0x324 contains 12 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x0000000e (SONAME)                     Library soname: [libhello-jni.so]
 0x00000010 (SYMBOLIC)                   0x0
 0x00000004 (HASH)                       0xd4
 0x00000005 (STRTAB)                     0x200
 0x00000006 (SYMTAB)                     0x120
 0x0000000a (STRSZ)                      219 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000000 (NULL)                       0x0

There are no relocations in this file.

There are no unwind sections in this file.

Symbol table '.dynsym' contains 14 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 000002dc     0 SECTION LOCAL  DEFAULT    4 
     2: 00000324     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_end
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr0
     4: 000013b8     0 NOTYPE  GLOBAL DEFAULT  ABS _bss_end__
     5: 000013b8     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start__
     6: 0000031c     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_start
     7: 000013b8     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_end__
     8: 000013b8     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
     9: 000013b8     0 NOTYPE  GLOBAL DEFAULT  ABS __end__
    10: 000002dd    44 FUNC    GLOBAL DEFAULT    4 Java_com_example_hellojni
    11: 000013b8     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    12: 000013b8     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    13: 000013b8     0 NOTYPE  GLOBAL DEFAULT    8 __data_start

Histogram for bucket list length (total of 3 buckets):
 Length  Number     % of total  Coverage
      0  0          (  0.0%)
      1  0          (  0.0%)      0.0%
      2  1          ( 33.3%)     16.7%
      3  0          (  0.0%)     16.7%
      4  1          ( 33.3%)     50.0%
      5  0          (  0.0%)     50.0%
      6  1          ( 33.3%)    100.0%

No version information found in this file.
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "5TE"
  Tag_CPU_arch: v5TE
  Tag_THUMB_ISA_use: Thumb-1
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align8_needed: Yes
  Tag_ABI_align8_preserved: Yes, except leaf SP
  Tag_ABI_enum_size: int
  Tag_ABI_optimization_goals: Aggressive Debug


The NDK toolchain supports C++ exceptions, since NDK r5, however all C++ sources are compiled with -fno-exceptions support by default, for compatibility reasons with previous releases.

To enable it, use the '-fexceptions' C++ compiler flag. This can be done by adding the following to every module definition in your Android.mk:

LOCAL_CPPFLAGS += -fexceptions More simply, add a single line to your Application.mk, the setting will automatically apply to all your project's NDK modules:

APP_CPPFLAGS += -fexceptions

follow this page

But in NDK r6b, this FLAG is open by default, and can not close.


Don't use c++ exceptions if you want to have a lightweight mobile program. There's a reason c++ features were limited in early versions of the ndk. Android itself uses the subset of c++ that was thought cost effective on a mobile platform, and thats more or less what was originally supported.

Also be sure you are not doing a debug build with symbols included - that could be your hello-jni bloat.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜