一、爲什麼靜態符號會衝突
無論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.
不同符號的不同類型
Value | Descripition | Note |
---|---|---|
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 '\n' > 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打開代碼目錄
然後,針對可能衝突的符號,逐一全局查找替換
到此,工作基本完成
修改完畢之後,合併測試