• Mp4V2与ffmpeg静态库符号冲突问题解决


    一、为什么静态符号会冲突

      无论macho二进制类型,还是Windows上的PE格式,还是Linux上的ELF格式,里面都是按照特定格式存放的一个程序的代码和数据

      比如Linux下的可执行文件格式,大致分为下面这些段

      参考:https://www.ibm.com/developerworks/cn/linux/l-excutff/    

      不同操作系统支持的可执行文件格式不一样,但是不同平台上面可执行文件执行的流程是一样的。读取可执行文件,分别解析不同段,并解析之后,映射到进程中,

      进程中的内存分布也是分段,这样方便数据组织和权限管理。

      不同的静态库文件,映射到进程之后,还存在一个软连接的过程,想象一下,一个函数在其他B库中实现,A要使用的时候,是怎么调用的。

      怎么知道B中方法的地址呢。

      在B库映射到进程中后,A方法调用这个函数(符号)之前,操作系统已经找到这个符号在进程中的地址,建立符号->之际函数入口点的过程,就是符号表映射的过程,

      这也是fishhook的原理,以上是我的理解。

    二、分析工具介绍

      有一个工具nm,能够用来分析二进制文件,可以扫描到可执行文件中的符号、以及相关信息。

    NAME
           nm - list symbols from object files
    
    SYNOPSIS
           nm [-A|-o|--print-file-name] [-a|--debug-syms]
              [-B|--format=bsd] [-C|--demangle[=style]]
              [-D|--dynamic] [-fformat|--format=format]
              [-g|--extern-only] [-h|--help]
              [-l|--line-numbers] [-n|-v|--numeric-sort]
              [-P|--portability] [-p|--no-sort]
              [-r|--reverse-sort] [-S|--print-size]
              [-s|--print-armap] [-t radix|--radix=radix]
              [-u|--undefined-only] [-V|--version]
              [-X 32_64] [--defined-only] [--no-demangle]
              [--plugin name] [--size-sort] [--special-syms]
              [--synthetic] [--target=bfdname]
              [objfile...]
    
    DESCRIPTION
           GNU nm lists the symbols from object files objfile....  If no object
           files are listed as arguments, nm assumes the file a.out.
    
           For each symbol, nm shows:
            # ... run man nm for detials.
    

      不同符号的不同类型

    ValueDescripitionNote
    A The symbol's value is absolute, and will not be changed by further linking. 符号绝对,链接过程不会改变
    B/b The symbol is in the uninitialized data section (known as BSS). 非初始化符号
    C The symbol is common. 公有符号,链接时会被同名符号覆盖
    D/d The symbol is in the initialized data section. 初始化符号
    G/g The symbol is in an initialized data section for small objects. 初始化符号,面向小数据访问优化
    I The symbol is an indirect reference to another symbol. 其它符号的间接引用
    N The symbol is a debugging symbol. 调试符号
    P The symbols is in a stack unwind section. 栈区符号(清空)
    R/r The symbol is in a read only data section. 符号只读
    S/s The symbol is in an uninitialized data section for small objects. 非初始化符号,面向小数据访问优化
    T/t The symbol is in the text (code) section. 代码区符号
    U The symbol is undefined. 未定义或在外部定义的符号
    u The symbol is a unique global symbol. 全局唯一,GNU保留符
    V/v The symbol is a weak object. 弱定义符(详见C++强弱符号定义)
    W/w The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. emm...绕口令符号
    - The symbol is a stabs symbol in an a.out object file. stabs格式符号
    ? The symbol type is unknown, or object file format specific. NM也不认识的符号

      比如:(参考 https://www.jianshu.com/p/a86bd1b8e4a5
      
    rew@rew:/usr/lib64$ nm libpthread.a
    
    nptl-init.o:
                     U __default_pthread_attr
                     U __default_pthread_attr_lock
                     U _dl_cpuclock_offset
                     U _dl_get_tls_static_info
                     U _dl_init_static_tls
                     U _dl_pagesize
                     U _dl_wait_lookup_done
                     U __fork_generation
                     U __getrlimit
                     U __is_smp
                     U __libc_fatal
    0000000000000008 C __libc_multiple_threads_ptr
                     U __libc_pthread_init
                     U __libc_setup_tls
                     U __libc_sigaction
                     U __libc_stack_end
                     U __lll_lock_wait_private
                     U __lll_unlock_wake_private
    0000000000000000 b __nptl_initial_report_events
    00000000000001b0 T __nptl_set_robust
                     U __nptl_setxid_error
    0000000000000000 r nptl_version
    00000000000004b0 T __pthread_get_minstack
    00000000000001d0 T __pthread_initialize_minimal
    00000000000001d0 T __pthread_initialize_minimal_internal
    

      

    三、问题解决过程

      最近发现,米家App中新引入了一个Mp4V2的.a静态库后,ffmpeg工作不正常了,Xcode调试过程中没有任何信息,只能看到crash  

      

      经过一番思索,猜测可能是这个库和ffmpeg本来的库存在符号冲突,使用nm工具导出所有符号

      在静态库libmp4v2.a同级目录下面执行 命令

    nm -U libmp4v2.a  | grep  -v ' t ' | grep -v ' s ' | grep -v ' d ' |  grep -v ' b ' | awk '{print $3}' | tr -s '
    ' > symbol.txt
    

      看下symbol.txt的结果

     cat symbol.txt
    

      

      生成的符号结果很多,其中因为.a是fat结构,很多符号是冲突的。

      一些符号明显带有mp4v2的标记,不可能跟ffmpeg冲突,所以,要对结果过滤下

      编写一段脚本代码,过滤这个文本

      过滤掉Mp4相关,并且去重

    #!/usr/bin/env python3
    # _*_ coding:utf-8 _*_
    #
    # @Version : 1.0
    # @Time    : 2019/6/29 3:13
    # @Author  : ddyt
    # @File    : LessSymbol.py
    #
    # 符号表处理
    
    import cxxfilt
    
    if __name__ == '__main__':
        s = set()
        file = open("symbol.txt")
        fileLines = file.readlines()
        for line in fileLines:
            if "mp4" not in line and line.rstrip() not in s and "MP4" not in line:
                print(cxxfilt.demangle(line.rstrip()))
                s.add(line.rstrip())

     

     过滤结果  

       

      部分符号看起来很复杂,这个是C++类的方法经过编译器mangle之后生成的

      将所有内容进行demangle,打开 https://demangler.com/,输入脚本生成的结果,demangle

      可以看到一些复杂的符号,是C++ string的符号产生的,忽略掉这些符号

      

      可以看到一些特殊的方法

    _av_free
    _av_freep
    _av_log2
    _av_log2_16bit
    _av_malloc
    _av_mallocz
    _av_realloc
    _av_realloc_array
    _av_realloc_f
    _av_reallocp
    _av_reallocp_array
    _avio_w8
    _avio_wb16
    _avio_wb32
    _avio_write
    _ff_avc_find_startcode
    _ff_avc_parse_nal_units
    _ff_avc_parse_nal_units_buf
    _ff_golomb_vlc_len
    _ff_interleaved_dirac_golomb_vlc_code
    _ff_interleaved_golomb_vlc_len
    _ff_interleaved_se_golomb_vlc_code
    _ff_interleaved_ue_golomb_vlc_code
    _ff_isom_write_hvcc
    _ff_se_golomb_vlc_code
    _ff_ue_golomb_len
    _ff_ue_golomb_vlc_code
    _mov_assm_hvcc_data
    _mov_hvcc_add_nal_unit
    _mov_write_hev1_tag
    _mov_write_hvcc_tag
    

      这些带有ff前缀和av前缀的,就是来自ffmpeg,这些符号就是冲突的根源

    再看下crash时候的堆栈,最后一个方法avio_write在上面的符号中

      现在冲突的原因找到了,处理思路分为两种

      1、修改冲突的函数的名字

      2、使用宏定义来修改函数的名字

      这里采用第一种,使用Visual Code打开代码目录

      

      然后,针对可能冲突的符号,逐一全局查找替换

      

      

      到此,工作基本完成

      修改完毕之后,合并测试

  • 相关阅读:
    LeetCode偶尔一题 —— 617. 合并二叉树
    《剑指offer》 —— 链表中倒数第k个节点
    《剑指offer》 —— 青蛙跳台阶问题
    《剑指offer》—— 二维数组中的查找
    《剑指offer》—— 替换空格
    《剑指offer》—— 合并两个排序的链表
    《剑指offer》—— 礼物的最大价值
    生成Nuget 源代码包来重用你的Asp.net MVC代码
    Pro ASP.Net Core MVC 6th 第四章
    Pro ASP.NET Core MVC 6th 第三章
  • 原文地址:https://www.cnblogs.com/doudouyoutang/p/11106725.html
Copyright © 2020-2023  润新知