• Android技术-修改SO导出符号


    背景

    经常在使用第三方SDK的时候会莫名其妙报错,其中最常见的一种就是SO符号冲突,比如libA.so静态链接了libC.a,而libB.so动态链接了libC.so。这样便会导致符号冲突。又或者在使用不同版本的动态库,也会造成符号冲突。

    报错案例

    • 案例1
      DEBUG : Abort message: 'terminating with unexpected exception of type std::bad_cast: std::bad_cast'
    • 案例2
    A/DEBUG: Softversion: PD1911C_A_1.9.7
    A/DEBUG: Time: 2020-06-12 15:30:46
    A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    A/DEBUG: Build fingerprint: 'vivo/PD1911/PD1911:9/PKQ1.181030.001/compiler02261735:user/release-keys'
    A/DEBUG: Revision: '0'
    A/DEBUG: ABI: 'arm'
    A/DEBUG: pid: 18115, tid: 19792, name: Thread-158  >>> com.renderer.app <<<
    A/DEBUG: signal 11 (SIGSEGV), code 0 (SI_USER), fault addr --------
    A/DEBUG:     r0  c3f7e350  r1  ffffffff  r2  c3f7e31c  r3  00000014
    A/DEBUG:     r4  c3f7e25c  r5  c3f7e308  r6  f2deed8c  r7  c3f7e250
    A/DEBUG:     r8  c3f7e350  r9  c204a028  r10 00000000  r11 00000001
    A/DEBUG:     ip  d1011f04  sp  c3f7e248  lr  d0ff766f  pc  d0ff7856
    A/DEBUG: backtrace:
    A/DEBUG:     #00 pc 0006e856  /data/app/com.renderer.app-Ibeyey3dw8tSdgPla9iLpg==/lib/arm/libc++_shared.so
    A/DEBUG:     #01 pc 0006e66b  /data/app/com.renderer.app-Ibeyey3dw8tSdgPla9iLpg==/lib/arm/libc++_shared.so
    A/DEBUG:     #02 pc 0006bffd  /data/app/com.renderer.app-Ibeyey3dw8tSdgPla9iLpg==/lib/arm/libc++_shared.so
    A/DEBUG:     #03 pc 0006beaf  /data/app/com.renderer.app-Ibeyey3dw8tSdgPla9iLpg==/lib/arm/libc++_shared.so (__gxx_personality_v0+78)
    A/DEBUG:     #04 pc 000e4bfc  /data/app/com.renderer.app-Ibeyey3dw8tSdgPla9iLpg==/lib/arm/libpg_sdk.so
    A/DEBUG:     #05 pc 000e5740  /data/app/com.renderer.app-Ibeyey3dw8tSdgPla9iLpg==/lib/arm/libpg_sdk.so
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    解决方案

    基于以上背景,可能存在一下几种解决方案

    1. 源代码重编译SO,添加动态依赖或者-fPIC等
    2. 使用dlopen等函数进行动态调用
    3. 修改导出符号表,将冲突的函数隐藏掉
    4. 修改导出符号表,将冲突的函数重命名

    具体执行

    1. 如果没有源代码,这种方案就行不通
    2. 可行方案。如果动态库没有导出 C 接口,直接使用 dlopen 和 dlsym 调用 C++ 接口不现实。需要再封装一个动态库 lib_wapper.so,包装 C++ 方法调用,导出 C 接口。程序里使用 dlopen 和 dlsym 调用 lib_wapper.so 导出的 C 接口。
    3. 不可行。使用 ELFkickers 里的 rebind 工具更改符号表,将方法可见性从DEFAULT修改为HIDDEN,运行时仍然与系统库冲突。查阅资料发现,rebind 主要是用于修改 .o 目标文件。可以修改 .o 目标文件的绑定属性和符号可见性,修改 .so 无效
    4. 可行。使用Python的LIEF模块最为方便

    实际案例

    解决跟libc++_shared.so符号冲突的so重命名

    import lief
    import sys
     
    if __name__ == '__main__':
        cpp_shared_so_path = "libc++_shared.so"
        cpp_shared_so = lief.parse(cpp_shared_so_path)
        cpp_shared_so_exported_symbols = cpp_shared_so.exported_symbols
        cpp_shared_so_exported_symbols_list = [sym.name for sym in cpp_shared_so_exported_symbols]
    
        targetElf = sys.argv[1]
        elf = lief.parse(targetElf)
        exported_symbols = elf.exported_symbols #dynamic_symbols
    
        for symbol in exported_symbols:
            if symbol.name in cpp_shared_so_exported_symbols_list:
                sym_export = elf.get_symbol(symbol.name)
                sym_export.name = symbol.name.replace("_Z","_M")
        elf.write(targetElf.replace(".so","_out.so"))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    参考

    - 安卓符号冲突解决方案,修改动态库函数符号表
    - 修改so导出函数名称

  • 相关阅读:
    Android字体大小dp,sp,px系统设置字体大小变化表现
    Unity的BuildPlayerProcessor:深入解析与实用案例
    redis的解决分布式锁的bug 和 redis面试题
    【EndNote X9.1 汉化版使用指南】
    「MySQL高级篇」MySQL之MVCC实现原理&&事务隔离级别的实现
    LVS负载均衡集群和NAT模式群集部署
    前端知识之CSS(1)-css语法、css选择器(属性、伪类、伪元素、分组与嵌套)、css组合器
    Linux本地搭建yum源,及局域网共享yum源
    分形网络(FractalNet)----学习笔记
    缓存篇—缓存雪崩
  • 原文地址:https://blog.csdn.net/qq_30135181/article/details/134271433