最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux動(dòng)態(tài)庫(kù).so找不到符號(hào)表的排查指南

 更新時(shí)間:2026年05月20日 09:07:20   作者:江南皮俠客  
本文詳細(xì)探討了Linux下C/C++開(kāi)發(fā)中動(dòng)態(tài)庫(kù)符號(hào)找不到(undefinedsymbol)問(wèn)題的排查思路與最佳實(shí)踐,從原理到工具逐一分析,通過(guò)梳理常見(jiàn)錯(cuò)誤形態(tài)、系統(tǒng)化排查流程及典型案例,幫助開(kāi)發(fā)者快速定位并解決實(shí)際問(wèn)題,需要的朋友可以參考下

在 Linux 下開(kāi)發(fā) C/C++ 項(xiàng)目時(shí),動(dòng)態(tài)庫(kù)(.so)相關(guān)的符號(hào)找不到(undefined symbol)是最常見(jiàn)也最令人頭疼的問(wèn)題之一。本文從原理到實(shí)踐,系統(tǒng)梳理排查思路與典型場(chǎng)景,幫助快速定位并解決問(wèn)題。

1. 背景知識(shí)

1.1 什么是符號(hào)表

符號(hào)表(Symbol Table)是 ELF(Executable and Linkable Format)文件中的一個(gè)關(guān)鍵段(Section),記錄了程序中定義和引用的所有符號(hào)(函數(shù)名、全局變量名等)及其屬性。

一個(gè)符號(hào)的典型屬性包括:

屬性說(shuō)明
名稱符號(hào)的字符串標(biāo)識(shí)
綁定類型LOCAL(本文件可見(jiàn))/ GLOBAL(全局可見(jiàn))/ WEAK(弱符號(hào))
類型FUNC(函數(shù))/ OBJECT(變量)/ NOTYPE
符號(hào)的地址或偏移
大小符號(hào)占據(jù)的字節(jié)數(shù)
所在段符號(hào)定義在哪個(gè) Section 中

圖:ELF 文件結(jié)構(gòu)示意,標(biāo)注了 .dynsym(動(dòng)態(tài)符號(hào)表)、.symtab(完整符號(hào)表)、.dynstr(動(dòng)態(tài)字符串表)等符號(hào)相關(guān)段的位置關(guān)系。紅色方括號(hào)標(biāo)記的區(qū)域?yàn)榉?hào)相關(guān)段,.dynsym / .dynstrstrip 后保留,.symtab / .strtabstrip 后被刪除。

關(guān)鍵概念

  • .symtab:完整符號(hào)表,包含所有符號(hào),strip 后會(huì)被刪除
  • .dynsym:動(dòng)態(tài)符號(hào)表,僅包含動(dòng)態(tài)鏈接需要的符號(hào),strip 后仍保留
  • .dynstr:動(dòng)態(tài)符號(hào)字符串表,存儲(chǔ)符號(hào)名稱字符串

1.2 動(dòng)態(tài)鏈接過(guò)程

程序加載動(dòng)態(tài)庫(kù)的過(guò)程分為兩個(gè)階段:

編譯/鏈接期(Link Time)
鏈接器(ld)檢查所有未定義符號(hào)是否能從指定的共享庫(kù)中找到定義,生成可執(zhí)行文件。

運(yùn)行期(Run Time)
動(dòng)態(tài)鏈接器(ld-linux.so)加載程序時(shí),按照依賴關(guān)系加載所需的 .so 文件,完成符號(hào)重定位(Relocation),將符號(hào)引用綁定到實(shí)際地址。

*圖:動(dòng)態(tài)鏈接完整流程——從用戶執(zhí)行程序(execve)到內(nèi)核加載 ELF、啟動(dòng)動(dòng)態(tài)鏈接器 ld-linux.so,遞歸加載依賴庫(kù)并映射到內(nèi)存,然后遍歷重定位表查找符號(hào)定義并填入 GOT/PLT。紅色虛線框標(biāo)注的**步驟 8(符號(hào)查找)*是 undefined symbol 錯(cuò)誤的發(fā)生點(diǎn),如果所有已加載庫(kù)中都找不到該符號(hào)定義,動(dòng)態(tài)鏈接器即報(bào)錯(cuò)終止。

1.3 符號(hào)綁定的本質(zhì)

當(dāng)程序引用一個(gè)外部符號(hào)時(shí),ELF 文件中會(huì)記錄一個(gè)重定位條目(Relocation Entry),指明"地址 X 處需要填入符號(hào) Y 的實(shí)際地址"。動(dòng)態(tài)鏈接器的工作就是遍歷這些重定位條目,找到符號(hào)定義,把實(shí)際地址填入。

如果找不到符號(hào)定義,鏈接器就會(huì)報(bào) undefined symbol 錯(cuò)誤。

2. 常見(jiàn)錯(cuò)誤形態(tài)

編譯/鏈接期報(bào)錯(cuò)

# 鏈接時(shí)找不到符號(hào)定義
/usr/bin/ld: main.o: in function `main':
main.cpp:(.text+0x2a): undefined reference to `foo()'
collect2: error: ld returned 1 exit status

運(yùn)行期報(bào)錯(cuò)

# dlopen 時(shí)找不到符號(hào)
./app: symbol lookup error: ./libplugin.so: undefined symbol: _Z3foov

# 程序啟動(dòng)時(shí)找不到符號(hào)
./app: /usr/lib/libmylib.so: undefined symbol: bar

區(qū)分兩種錯(cuò)誤

特征編譯/鏈接期運(yùn)行期
報(bào)錯(cuò)時(shí)機(jī)gcc/g++ 編譯時(shí)程序啟動(dòng)或 dlopen 時(shí)
報(bào)錯(cuò)關(guān)鍵詞undefined referenceundefined symbol / symbol lookup error
常見(jiàn)原因鏈接順序、缺少庫(kù)文件庫(kù)版本不一致、dlopen 標(biāo)志不對(duì)

3. 排查工具詳解

3.1 nm — 查看目標(biāo)文件中的符號(hào)

nm 是排查符號(hào)問(wèn)題的第一工具,可以列出目標(biāo)文件和 .so 中的所有符號(hào)。

# 查看動(dòng)態(tài)庫(kù)中的所有符號(hào)
nm -D libmylib.so

# 常用選項(xiàng)組合:按符號(hào)名排序,顯示動(dòng)態(tài)符號(hào)
nm -CD libmylib.so | grep foo

# 輸出含義:
# T / t  — 代碼段中的符號(hào)(大寫(xiě)=全局,小寫(xiě)=局部)
# D / d  — 數(shù)據(jù)段中的符號(hào)
# U      — 未定義符號(hào)(Undefined,需要從其他庫(kù)中解析)
# W      — 弱符號(hào)(Weak)
# A      — 絕對(duì)符號(hào)

典型輸出解讀

$ nm -CD libmylib.so
                 w __gmon_start__
                 U __printf_chk@@GLIBC_2.17    # U = 這個(gè)符號(hào)在本庫(kù)中未定義,需要外部提供
0000000000000690 T my_function                  # T = 這個(gè)符號(hào)在本庫(kù)中定義,全局可見(jiàn)
0000000000000750 T my_class::do_work()          # C++ 方法(已 demangle)
0000000000002010 D my_global_var                # D = 全局變量定義

技巧nm -D 只查看動(dòng)態(tài)符號(hào)表(.dynsym),這是運(yùn)行時(shí)鏈接器使用的符號(hào)表。不加 -D 會(huì)查看完整符號(hào)表(.symtab),但 strip 后該表可能不存在。

3.2 readelf — 解析 ELF 文件信息

readelfnm 更底層,可以查看 ELF 文件的任意段。

# 查看動(dòng)態(tài)符號(hào)表
readelf -s libmylib.so

# 查看所有段頭(定位符號(hào)表段是否存在)
readelf -S libmylib.so

# 查看動(dòng)態(tài)段(NEEDED 條目 = 運(yùn)行時(shí)依賴的庫(kù))
readelf -d libmylib.so

# 查看符號(hào)版本信息
readelf -V libmylib.so

# 查看重定位條目(哪些符號(hào)需要被重定位)
readelf -r libmylib.so

典型輸出解讀

$ readelf -s libmylib.so

Symbol table '.dynsym' contains 15 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __printf_chk@GLIBC_2.17
     2: 0000000000000690   120 FUNC    GLOBAL DEFAULT   11 my_function
     3: 0000000000000750    56 FUNC    GLOBAL DEFAULT   11 _ZN8my_class7do_workEv
                                                                     ↑ 這是 C++ mangled name
$ readelf -d libmylib.so | grep NEEDED
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

3.3 objdump — 反匯編與符號(hào)查看

# 查看符號(hào)表
objdump -T libmylib.so

# 查看所有段頭
objdump -x libmylib.so | head -50

# 反匯編特定函數(shù)(需要非 strip 的庫(kù))
objdump -d libmylib.so | grep -A 20 '<my_function>'

3.4 ldd — 查看動(dòng)態(tài)庫(kù)依賴

# 查看程序運(yùn)行時(shí)依賴的所有 .so
ldd ./myapp

# 查看某個(gè) .so 的依賴
ldd libmylib.so

# 典型輸出
$ ldd ./myapp
    linux-vdso.so.1 (0x00007ffc12bfe000)
    libmylib.so => /usr/local/lib/libmylib.so (0x00007f8a1c200000)  # ? 找到了
    libfoo.so => not found                                            # ? 找不到!
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8a1be00000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f8a1c600000)

注意ldd 對(duì)于交叉編譯場(chǎng)景可能不可靠,可用 readelf -d + LD_LIBRARY_PATH 替代。

3.5 LD_DEBUG — 運(yùn)行時(shí)調(diào)試動(dòng)態(tài)鏈接器

這是排查運(yùn)行期符號(hào)問(wèn)題最強(qiáng)大的工具,無(wú)需重新編譯。

# 查看符號(hào)綁定過(guò)程(最常用)
LD_DEBUG=symbols ./myapp

# 查看庫(kù)文件查找過(guò)程
LD_DEBUG=libs ./myapp

# 查看重定位過(guò)程
LD_DEBUG=reloc ./myapp

# 查看所有調(diào)試信息
LD_DEBUG=all ./myapp

# 輸出到文件而非 stderr
LD_DEBUG=symbols LD_DEBUG_OUTPUT=/tmp/ld_debug ./myapp

典型輸出

$ LD_DEBUG=symbols ./myapp 2>&1 | grep foo
     10567: symbol=foo;  lookup in file=./myapp [0]
     10567: symbol=foo;  lookup in file=libmylib.so [0]
     10567: symbol=foo;  lookup in file=libc.so.6 [0]
     10567: symbol=foo;  lookup in file=ld-linux-x86-64.so.2 [0]
     10567: symbol=foo;  error: symbol not found          # ← 所有庫(kù)都找遍了,沒(méi)找到

3.6 c++filt — C++ 符號(hào) demangle

C++ 編譯器會(huì)對(duì)函數(shù)名進(jìn)行 name mangling,c++filt 用于還原可讀名稱。

# 還原 mangled 名稱
$ echo '_ZN8my_class7do_workEv' | c++filt
my_class::do_work()

# 結(jié)合 nm 使用
nm libmylib.so | c++filt

# 結(jié)合 grep 使用
nm -D libmylib.so | c++filt | grep 'my_class::do_work'

4. 系統(tǒng)化排查流程

遇到 undefined symbol 錯(cuò)誤時(shí),按以下流程逐步排查:

Step 1:確認(rèn)缺失的符號(hào)名

從錯(cuò)誤信息中提取符號(hào)名,注意區(qū)分 mangled name 和 demangled name:

# 如果是 mangled name(以 _Z 開(kāi)頭),先 demangle
$ c++filt _Z3foov
foo()

Step 2:確認(rèn)該符號(hào)應(yīng)該由誰(shuí)提供

# 在所有相關(guān)庫(kù)中搜索該符號(hào)
nm -CD libprovider1.so | grep foo
nm -CD libprovider2.so | grep foo

# 或者用 readelf
readelf -s libprovider1.so | grep foo
readelf -s libprovider2.so | grep foo

# 系統(tǒng)范圍搜索(需要 locate 或 find)
# 方法 1:用 locate 快速定位
locate libfoo.so | xargs -I{} sh -c 'nm -CD {} 2>/dev/null | grep -l foo && echo {}'

# 方法 2:在已知目錄下搜索
for lib in /usr/lib/*.so /usr/local/lib/*.so; do
    nm -CD "$lib" 2>/dev/null | grep -q 'T foo' && echo "$lib"
done

Step 3:確認(rèn)提供者庫(kù)是否被正確加載

# 檢查依賴鏈
ldd ./myapp | grep libprovider

# 檢查 RPATH/RUNPATH
readelf -d ./myapp | grep -E 'RPATH|RUNPATH'

# 檢查 LD_LIBRARY_PATH
echo $LD_LIBRARY_PATH

Step 4:確認(rèn)符號(hào)可見(jiàn)性

# 檢查符號(hào)是否被導(dǎo)出
nm -CD libprovider.so | grep foo
# 如果沒(méi)有 T/D 類型的條目,說(shuō)明符號(hào)未導(dǎo)出

# 檢查符號(hào)是否被 strip 掉
readelf -S libprovider.so | grep -E 'symtab|dynsym'
# 如果 .symtab 不存在但 .dynsym 存在,說(shuō)明被 strip 了(正常)
# 如果 .dynsym 也沒(méi)有,說(shuō)明編譯時(shí)就沒(méi)有導(dǎo)出

Step 5:確認(rèn)符號(hào)版本

# 查看符號(hào)版本要求
readelf -V libprovider.so
objdump -T libprovider.so | grep foo

5. 典型案例

5.1 案例一:C++ name mangling 導(dǎo)致找不到符號(hào)

場(chǎng)景:C 語(yǔ)言寫(xiě)的庫(kù),C++ 程序調(diào)用時(shí)找不到符號(hào)。

復(fù)現(xiàn)

// libcalc.c — 純 C 庫(kù)
int add(int a, int b) {
    return a + b;
}
// main.cpp — C++ 程序
#include <cstdio>
// ? 缺少 extern "C" 聲明
int add(int a, int b);
int main() {
    printf("%d\n", add(1, 2));
    return 0;
}
# 編譯 C 庫(kù)
gcc -shared -fPIC -o libcalc.so libcalc.c

# 編譯 C++ 程序
g++ main.cpp -L. -lcalc -o main

# 運(yùn)行報(bào)錯(cuò)
$ ./main
./main: symbol lookup error: ./main: undefined symbol: _Z3addii

排查

# 看庫(kù)中導(dǎo)出的符號(hào)名
$ nm -D libcalc.so | grep add
0000000000000690 T add                # C 庫(kù)導(dǎo)出的是 "add"

# 看程序需要的符號(hào)名
$ nm main | grep add
                 U _Z3addii           # C++ 程序找的是 "_Z3addii"(mangled name)

# demangle 確認(rèn)
$ c++filt _Z3addii
add(int, int)

結(jié)論:C++ 編譯器對(duì) add(int, int) 做了 name mangling,生成 _Z3addii,而 C 庫(kù)導(dǎo)出的符號(hào)名是 add,兩者不匹配。

修復(fù)

// main.cpp — 正確寫(xiě)法
#include <cstdio>
extern "C" {           // ? 告訴編譯器按 C 的方式查找符號(hào)
    int add(int a, int b);
}
int main() {
    printf("%d\n", add(1, 2));
    return 0;
}

或者更常見(jiàn)的頭文件寫(xiě)法:

// libcalc.h — 兼容 C 和 C++ 的頭文件
#ifdef __cplusplus
extern "C" {
#endif
int add(int a, int b);
#ifdef __cplusplus
}
#endif

5.2 案例二:編譯時(shí)缺少 -fPIC

場(chǎng)景:編譯動(dòng)態(tài)庫(kù)時(shí)未加 -fPIC,鏈接時(shí)出現(xiàn)重定位錯(cuò)誤。

復(fù)現(xiàn)

// libfoo.c
int foo() { return 42; }
# ? 編譯 .o 時(shí)沒(méi)有 -fPIC
gcc -c libfoo.c
gcc -shared -o libfoo.so libfoo.o

# 可能出現(xiàn)的警告或錯(cuò)誤
/usr/bin/ld: libfoo.o: relocation R_X86_64_PC32 against symbol `foo' can not be used when making a shared object; recompile with -fPIC

在某些架構(gòu)(如 x86_64)上,缺少 -fPIC 會(huì)直接報(bào)錯(cuò);在另一些架構(gòu)上可能只是性能下降或運(yùn)行時(shí)異常。

排查

# 檢查 .o 文件的重定位類型
readelf -r libfoo.o | head

# 如果看到 R_X86_64_PC32 而非 R_X86_64_PLT32 / R_X86_64_GOTPCREL,
# 說(shuō)明編譯時(shí)沒(méi)有使用 -fPIC

修復(fù)

# ? 編譯 .o 時(shí)加 -fPIC
gcc -fPIC -c libfoo.c
gcc -shared -o libfoo.so libfoo.o

# 或者一步完成
gcc -shared -fPIC -o libfoo.so libfoo.c

5.3 案例三:鏈接順序錯(cuò)誤

場(chǎng)景:編譯時(shí)庫(kù)的鏈接順序?qū)е路?hào)找不到。

復(fù)現(xiàn)

// main.cpp
#include "liba.h"    // liba 中的函數(shù)依賴 libb
int main() {
    func_from_a();   // 該函數(shù)內(nèi)部調(diào)用了 func_from_b()
    return 0;
}
# ? 錯(cuò)誤的鏈接順序:liba 在 libb 之前
g++ main.cpp -la -lb -o main
# 報(bào)錯(cuò):undefined reference to `func_from_b()'

# ? 正確的鏈接順序:被依賴的庫(kù)放后面
g++ main.cpp -la -lb -o main    # 如果 a 依賴 b,應(yīng)該把 b 放在 a 后面

關(guān)鍵規(guī)則:GCC 鏈接器是從左到右單遍掃描的。如果庫(kù) A 依賴庫(kù) B 中的符號(hào),那么在命令行上 A 必須出現(xiàn)在 B 之前。即 g++ main.o -lA -lB。

排查

# 確認(rèn)庫(kù)之間的依賴關(guān)系
nm -D liba.so | grep ' U '      # 查看 liba 的未定義符號(hào)
nm -D libb.so | grep ' T '      # 查看 libb 定義了哪些符號(hào)

# 交叉對(duì)比
nm -D liba.so | awk '$1=="U"{print $2}' | while read sym; do
    nm -D libb.so | grep -q " T $sym" && echo "libb provides: $sym"
done

5.4 案例四:符號(hào)版本不匹配

場(chǎng)景:編譯時(shí)使用的庫(kù)版本與運(yùn)行時(shí)加載的庫(kù)版本不同,符號(hào)版本對(duì)不上。

復(fù)現(xiàn)

# 編譯時(shí)鏈接了新版本的 libfoo(有 foo@@VER_2.0)
g++ main.cpp -lfoo -o main

# 運(yùn)行時(shí)加載了舊版本的 libfoo(只有 foo@@VER_1.0)
$ LD_LIBRARY_PATH=/old/lib ./main
./main: /old/lib/libfoo.so: version `FOO_2.0' not found

排查

# 查看程序需要的符號(hào)版本
$ objdump -T main | grep FOO
0000000000000000      DF *UND*  0000000000000000  FOO_2.0  foo

# 查看庫(kù)提供的符號(hào)版本
$ objdump -T /old/lib/libfoo.so | grep FOO
0000000000000690 g    DF .text  000000000000001a  FOO_1.0  foo
                                                    ↑ 只有 1.0

$ objdump -T /new/lib/libfoo.so | grep FOO
0000000000000690 g    DF .text  000000000000001a  FOO_2.0  foo
                                                    ↑ 有 2.0

# 查看庫(kù)的版本定義
$ readelf -V /old/lib/libfoo.so
Version definition section '.gnu.version_d' contains 2 entries:
  Addr: 0x00000000000002d8  Offset: 0x0002d8  Link: 3 (.dynstr)
    00000000: Rev: 1  Flags: BASE   Index: 1  Cnt: 1  Name: libfoo.so
    0x001c9880: Rev: 1  Flags: none  Index: 2  Cnt: 1  Name: FOO_1.0

修復(fù)

# 方法 1:確保運(yùn)行時(shí)使用正確版本的庫(kù)
LD_LIBRARY_PATH=/new/lib ./main

# 方法 2:使用 LD_PRELOAD 強(qiáng)制加載特定版本
LD_PRELOAD=/new/lib/libfoo.so ./main

# 方法 3:設(shè)置 RPATH 使可執(zhí)行文件記住庫(kù)路徑
g++ -Wl,-rpath,/new/lib main.cpp -lfoo -o main

5.5 案例五:dlopen 加載時(shí)缺少 RTLD_GLOBAL

場(chǎng)景:插件系統(tǒng)使用 dlopen 加載 .so,插件中引用了主程序或其他插件的符號(hào),但 dlopen 時(shí)沒(méi)有設(shè)置 RTLD_GLOBAL。

復(fù)現(xiàn)

// main.cpp — 主程序
#include <dlfcn.h>
#include <cstdio>
void host_function() {       // 主程序中定義的函數(shù)
    printf("host_function called\n");
}
int main() {
    // ? 只用了 RTLD_LAZY,沒(méi)有 RTLD_GLOBAL
    void* handle = dlopen("./libplugin.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        return 1;
    }
    typedef void (*plugin_init_t)();
    auto plugin_init = (plugin_init_t)dlsym(handle, "plugin_init");
    plugin_init();
    dlclose(handle);
    return 0;
}
// plugin.cpp — 插件
#include <cstdio>
extern void host_function();  // 引用主程序的符號(hào)
extern "C" void plugin_init() {
    host_function();          // ← 運(yùn)行時(shí)報(bào) undefined symbol
}
# 編譯
g++ -shared -fPIC -o libplugin.so plugin.cpp
g++ -rdynamic -o main main.cpp -ldl    # -rdynamic 讓主程序?qū)С龇?hào)

# 運(yùn)行
$ ./main
./libplugin.so: undefined symbol: host_function

排查

# 確認(rèn)主程序確實(shí)導(dǎo)出了該符號(hào)
$ nm -D main | grep host_function
0000000000001179 T host_function       # ? 主程序?qū)С隽?

# 確認(rèn)插件需要該符號(hào)
$ nm -D libplugin.so | grep host_function
                 U host_function       # U = 未定義,需要外部提供

# 問(wèn)題在于 dlopen 的默認(rèn)作用域

根因分析

dlopen 默認(rèn)使用 RTLD_LOCAL,意味著新加載的庫(kù)的符號(hào)不會(huì)添加到全局符號(hào)表中。當(dāng)插件引用主程序的符號(hào)時(shí),默認(rèn)搜索范圍可能不包含主程序的符號(hào)。

修復(fù)

// 方法 1:使用 RTLD_LAZY | RTLD_GLOBAL(推薦)
void* handle = dlopen("./libplugin.so", RTLD_LAZY | RTLD_GLOBAL);

// 方法 2:主程序編譯時(shí)加 -rdynamic(已加),并確保 dlopen 之前符號(hào)可用

5.6 案例六:靜態(tài)庫(kù)中未引用的符號(hào)被丟棄

場(chǎng)景:將靜態(tài)庫(kù)(.a)鏈接到動(dòng)態(tài)庫(kù)(.so)時(shí),靜態(tài)庫(kù)中未被直接引用的符號(hào)被鏈接器丟棄。

復(fù)現(xiàn)

// registry.h — 自動(dòng)注冊(cè)模式
#include <map>
#include <string>
struct Registry {
    static std::map<std::string, int>& entries() {
        static std::map<std::string, int> m;
        return m;
    }
};
#define REGISTER(name, val) \
    static bool _reg_##name = (Registry::entries()[#name] = val, true)
// foo_plugin.cpp
#include "registry.h"
REGISTER(foo, 1)    // 全局靜態(tài)變量的構(gòu)造函數(shù)會(huì)執(zhí)行注冊(cè)
// bar_plugin.cpp
#include "registry.h"
REGISTER(bar, 2)
# 編譯為靜態(tài)庫(kù)
g++ -c foo_plugin.cpp -o foo_plugin.o
g++ -c bar_plugin.cpp -o bar_plugin.o
ar rcs libplugins.a foo_plugin.o bar_plugin.o

# 鏈接到動(dòng)態(tài)庫(kù)
g++ -shared -fPIC -o libmyapp.so -L. -lplugins

# 運(yùn)行時(shí)發(fā)現(xiàn)注冊(cè)表中為空!

排查

# 檢查動(dòng)態(tài)庫(kù)中是否有注冊(cè)相關(guān)的符號(hào)
$ nm -D libmyapp.so | grep _reg_
# 空輸出!符號(hào)被丟棄了

# 檢查靜態(tài)庫(kù)中確實(shí)有這些符號(hào)
$ nm libplugins.a | grep _reg_
foo_plugin.o:
0000000000000000 d _reg_foo

bar_plugin.o:
0000000000000000 d _reg_bar

根因:鏈接器在處理靜態(tài)庫(kù)時(shí),只提取那些被其他目標(biāo)文件引用的符號(hào)。由于 _reg_foo_reg_bar 是靜態(tài)變量,沒(méi)有被顯式引用,鏈接器認(rèn)為它們"不需要"而將其丟棄。

修復(fù)

# 方法 1:使用 --whole-archive 強(qiáng)制包含所有符號(hào)
g++ -shared -fPIC -o libmyapp.so \
    -Wl,--whole-archive -L. -lplugins -Wl,--no-whole-archive
# 方法 2:在代碼中顯式引用(不推薦,但簡(jiǎn)單)
# 在某個(gè)會(huì)被引用的函數(shù)中添加:
extern bool _reg_foo;
extern bool _reg_bar;
void force_reference() {
    (void)_reg_foo;
    (void)_reg_bar;
}
# 方法 3:直接用 .o 文件而非靜態(tài)庫(kù)
g++ -shared -fPIC -o libmyapp.so foo_plugin.o bar_plugin.o

5.7 案例七:頭文件與庫(kù)版本不一致

場(chǎng)景:系統(tǒng)安裝了多個(gè)版本的庫(kù),編譯時(shí)使用了新版頭文件,但鏈接時(shí)找到了舊版庫(kù)。

復(fù)現(xiàn)

# /usr/include/mylib.h — 新版本(v2.0),聲明了 new_api()
# /usr/lib/libmylib.so — 舊版本(v1.0),沒(méi)有 new_api()
# /usr/local/lib/libmylib.so — 新版本(v2.0),有 new_api()

# 編譯時(shí)用了新頭文件
g++ main.cpp -I/usr/include -lmylib -o main

# 鏈接時(shí)找到了舊版庫(kù)
$ ldd main | grep mylib
libmylib.so => /usr/lib/libmylib.so    # ← 舊版!

# 運(yùn)行報(bào)錯(cuò)
$ ./main
./main: symbol lookup error: ./main: undefined symbol: new_api

排查

# 1. 確認(rèn)鏈接了哪個(gè)庫(kù)
ldd main | grep mylib

# 2. 確認(rèn)庫(kù)中是否有該符號(hào)
nm -CD /usr/lib/libmylib.so | grep new_api       # 舊版:沒(méi)有
nm -CD /usr/local/lib/libmylib.so | grep new_api  # 新版:有

# 3. 確認(rèn)頭文件版本
grep new_api /usr/include/mylib.h       # 有聲明

修復(fù)

# 方法 1:設(shè)置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

# 方法 2:編譯時(shí)設(shè)置 RPATH
g++ main.cpp -I/usr/local/include -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lmylib -o main

# 方法 3:使用 pkg-config 確保一致性
g++ main.cpp $(pkg-config --cflags --libs mylib) -o main

5.8 案例八:x86 交叉編譯 ARM 動(dòng)態(tài)庫(kù)部署后找不到符號(hào)

場(chǎng)景:在 x86_64 開(kāi)發(fā)機(jī)上使用交叉編譯工具鏈(如 aarch64-linux-gnu-gcc)編譯 ARM64 動(dòng)態(tài)庫(kù),拷貝到 ARM64 目標(biāo)機(jī)器后運(yùn)行,出現(xiàn)找不到動(dòng)態(tài)庫(kù)或符號(hào)的問(wèn)題。這類問(wèn)題在嵌入式開(kāi)發(fā)、邊緣設(shè)備部署中極為常見(jiàn)。

5.8.1 子場(chǎng)景 A:在 x86 開(kāi)發(fā)機(jī)上誤用本地工具排查 ARM 庫(kù)

這是最常見(jiàn)的"偽問(wèn)題"——庫(kù)本身沒(méi)問(wèn)題,但排查方法用錯(cuò)了。

復(fù)現(xiàn)

# 在 x86 開(kāi)發(fā)機(jī)上編譯 ARM64 動(dòng)態(tài)庫(kù)
aarch64-linux-gnu-g++ -shared -fPIC -o libfoo.so foo.cpp

# ? 在 x86 機(jī)器上直接用 ldd 檢查 ARM 庫(kù)
$ ldd libfoo.so
    not a dynamic executable

# ? 在 x86 機(jī)器上直接運(yùn)行 ARM 程序
$ ./myapp
bash: ./myapp: cannot execute binary file: Exec format Error

# ? 用本地 nm 查看(雖然能看符號(hào),但容易忽略架構(gòu)差異)
$ nm -D libfoo.so
# 能輸出符號(hào),但無(wú)法驗(yàn)證運(yùn)行時(shí)依賴鏈?zhǔn)欠裢暾?

正確做法:在交叉編譯場(chǎng)景下,必須使用與目標(biāo)架構(gòu)匹配的工具鏈來(lái)排查,或在目標(biāo)機(jī)器上直接排查。

# ? 方法 1:使用交叉編譯工具鏈自帶的工具
aarch64-linux-gnu-nm -D libfoo.so
aarch64-linux-gnu-readelf -s libfoo.so
aarch64-linux-gnu-readelf -d libfoo.so | grep NEEDED
aarch64-linux-gnu-objdump -T libfoo.so

# ? 方法 2:直接在 ARM64 目標(biāo)機(jī)器上排查(最可靠)
# 拷貝到目標(biāo)機(jī)后:
ssh arm-device
nm -D libfoo.so
readelf -d libfoo.so | grep NEEDED
ldd ./myapp        # 在目標(biāo)機(jī)上 ldd 才有意義

核心原則ldd 本質(zhì)上是執(zhí)行目標(biāo)程序來(lái)獲取依賴信息,因此無(wú)法在 x86 上對(duì) ARM 二進(jìn)制使用。nmreadelf、objdump 是純文件解析工具,可以在 x86 上解析 ARM 二進(jìn)制,但必須使用對(duì)應(yīng)架構(gòu)的版本才能保證行為一致。

5.8.2 子場(chǎng)景 B:交叉編譯時(shí)鏈接了 x86 架構(gòu)的系統(tǒng)庫(kù)

復(fù)現(xiàn)

# 交叉編譯時(shí),未正確指定 sysroot
aarch64-linux-gnu-g++ main.cpp -lfoo -o myapp

# 編譯可能成功(因?yàn)檎业搅?x86 的 libfoo.so),但生成的二進(jìn)制是混合架構(gòu)
# 部署到 ARM 機(jī)器后:
$ ./myapp
./myapp: error while loading shared libraries: libfoo.so: wrong ELF class: ELFCLASS64
# 或者
./myapp: /usr/lib/libfoo.so: cannot open shared object file: Exec format error

排查

# 1. 檢查 ELF 文件的架構(gòu)
$ readelf -h libfoo.so | grep -E 'Machine|Class'
  Class:                             ELF64
  Machine:                           AArch64           # ? ARM64

$ readelf -h /usr/lib/libfoo.so | grep -E 'Machine|Class'
  Class:                             ELF64
  Machine:                           Advanced Micro Devices X86-64  # ? x86_64!鏈接了錯(cuò)誤的庫(kù)

# 2. 檢查可執(zhí)行文件依賴的所有庫(kù)的架構(gòu)
$ for lib in $(ldd myapp | awk '{print $3}' | grep -v '^$'); do
    echo "=== $lib ==="
    readelf -h "$lib" 2>/dev/null | grep Machine
done

# 3. 檢查編譯時(shí)鏈接了哪些路徑
$ aarch64-linux-gnu-g++ main.cpp -lfoo -o myapp -v 2>&1 | grep 'LIBRARY_PATH'
# 如果輸出包含 /usr/lib 而非 /usr/aarch64-linux-gnu/lib,說(shuō)明鏈接了 x86 系統(tǒng)庫(kù)

修復(fù)

# ? 方法 1:使用 --sysroot 指定目標(biāo)架構(gòu)的根文件系統(tǒng)
aarch64-linux-gnu-g++ main.cpp -lfoo -o myapp \
    --sysroot=/usr/aarch64-linux-gnu

# ? 方法 2:顯式指定庫(kù)搜索路徑
aarch64-linux-gnu-g++ main.cpp -lfoo -o myapp \
    -L/usr/aarch64-linux-gnu/lib

# ? 方法 3:使用 CMake 交叉編譯工具鏈文件(推薦)

CMake 交叉編譯工具鏈文件示例:

# toolchain-aarch64.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
# 關(guān)鍵:指定 sysroot 和搜索路徑
set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain-aarch64.cmake ..
make

5.8.3 子場(chǎng)景 C:ARM 目標(biāo)機(jī)上缺少依賴的 .so 或符號(hào)

場(chǎng)景:交叉編譯的庫(kù)在 ARM 目標(biāo)機(jī)上運(yùn)行時(shí),找不到依賴的底層庫(kù)(如 libc、libstdc++ 版本不匹配)。

復(fù)現(xiàn)

# 在 x86 開(kāi)發(fā)機(jī)上用較新的交叉編譯工具鏈編譯
aarch64-linux-gnu-g++ -shared -fPIC -o libmyapp.so myapp.cpp

# 部署到 ARM 目標(biāo)機(jī)(可能是較老的嵌入式系統(tǒng))
$ ./myapp
./myapp: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.29' not found
./myapp: /lib/libc.so.6: version `GLIBC_2.33' not found

排查

# 1. 在 x86 開(kāi)發(fā)機(jī)上檢查交叉編譯的庫(kù)需要哪些符號(hào)版本
$ aarch64-linux-gnu-readelf -V libmyapp.so | grep -E 'GLIBC|GLIBCXX'
Version needs section '.gnu.version_r' contains 3 entries:
  0x00008000: Rev: 1  Flags: none  Index: 2  Cnt: 1  Name: GLIBC_2.33
  0x00008010: Rev: 1  Flags: none  Index: 3  Cnt: 1  Name: GLIBCXX_3.4.29

# 2. 在 ARM 目標(biāo)機(jī)上檢查可用的符號(hào)版本
$ strings /usr/lib/libstdc++.so.6 | grep GLIBCXX
GLIBCXX_3.4
GLIBCXX_3.4.9
...
GLIBCXX_3.4.21      # ← 最高只到 3.4.21,遠(yuǎn)低于需要的 3.4.29

$ strings /lib/libc.so.6 | grep GLIBC
GLIBC_2.17
...
GLIBC_2.31          # ← 最高只到 2.31,低于需要的 2.33

# 3. 列出庫(kù)的所有未定義符號(hào)及其版本要求
$ aarch64-linux-gnu-objdump -T libmyapp.so | grep '*UND*'
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.33  memcpy
0000000000000000      DF *UND*  0000000000000000  GLIBCXX_3.4.29  _ZNSt7__cxx1112basic_string...

修復(fù)

# 方法 1:使用與目標(biāo)系統(tǒng)匹配的交叉編譯工具鏈版本
# 如果目標(biāo)機(jī)是 Ubuntu 20.04(glibc 2.31),就用對(duì)應(yīng)版本的 sysroot
aarch64-linux-gnu-g++ -shared -fPIC -o libmyapp.so myapp.cpp \
    --sysroot=/path/to/ubuntu20.04-aarch64-sysroot

# 方法 2:將交叉編譯工具鏈的運(yùn)行時(shí)庫(kù)一起部署到目標(biāo)機(jī)
# 拷貝交叉編譯器的 libstdc++ 和 libgcc
scp /usr/aarch64-linux-gnu/lib/libstdc++.so.6.0.29 arm-device:/opt/lib/
scp /usr/aarch64-linux-gnu/lib/libgcc_s.so.1 arm-device:/opt/lib/
# 在目標(biāo)機(jī)上設(shè)置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH

# 方法 3:靜態(tài)鏈接 C/C++ 運(yùn)行時(shí)(簡(jiǎn)單但增加體積)
aarch64-linux-gnu-g++ -shared -fPIC -o libmyapp.so myapp.cpp \
    -static-libgcc -static-libstdc++

# 方法 4:使用 Docker 構(gòu)建可控的交叉編譯環(huán)境(推薦,可復(fù)現(xiàn))

Docker 交叉編譯示例:

# Dockerfile.cross-build
FROM ubuntu:20.04

RUN apt-get update && apt-get install -y \
    gcc-aarch64-linux-gnu \
    g++-aarch64-linux-gnu \
    libc6-dev-arm64-cross \
    libstdc++-8-dev-arm64-cross

# 此環(huán)境中的 glibc/libstdc++ 版本與 Ubuntu 20.04 一致
# 確保編譯出的 .so 在目標(biāo)機(jī)上兼容

5.8.4 子場(chǎng)景 D:ARM 目標(biāo)機(jī)上 .so 搜索路徑問(wèn)題

場(chǎng)景:庫(kù)已正確交叉編譯并部署到 ARM 機(jī)器,但動(dòng)態(tài)鏈接器找不到庫(kù)文件。

復(fù)現(xiàn)

# 將 libfoo.so 部署到 ARM 目標(biāo)機(jī)的 /opt/myapp/lib/
$ ./myapp
./myapp: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

# 但庫(kù)確實(shí)存在
$ ls -la /opt/myapp/lib/libfoo.so
-rwxr-xr-x 1 root root 123456 Apr 24 10:00 /opt/myapp/lib/libfoo.so

排查

# 1. 確認(rèn)動(dòng)態(tài)鏈接器搜索了哪些路徑
$ LD_DEBUG=libs ./myapp 2>&1 | head -20
     1234: find library=libfoo.so [0]; searching
     1234:  search cache=/etc/ld.so.cache
     1234:  search path=/usr/lib:/lib        # ← 默認(rèn)路徑,沒(méi)有 /opt/myapp/lib

# 2. 檢查 ld.so.conf 配置
$ cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf

$ ls /etc/ld.so.conf.d/
libc.conf  # 只包含 /usr/lib

# 3. 檢查可執(zhí)行文件的 RPATH
$ readelf -d myapp | grep -E 'RPATH|RUNPATH'
# 空輸出 — 沒(méi)有設(shè)置 RPATH

修復(fù)

# 方法 1:臨時(shí)方案 — 設(shè)置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/opt/myapp/lib:$LD_LIBRARY_PATH
./myapp

# 方法 2:永久方案 — 添加到 ld.so.conf
echo "/opt/myapp/lib" > /etc/ld.so.conf.d/myapp.conf
ldconfig                          # 刷新緩存
ldconfig -p | grep libfoo         # 驗(yàn)證緩存中已有該庫(kù)

# 方法 3:編譯時(shí)嵌入 RPATH(推薦)
aarch64-linux-gnu-g++ main.cpp -lfoo -o myapp \
    -Wl,-rpath,/opt/myapp/lib \
    -L/opt/myapp/lib

# 方法 4:使用 $ORIGIN 實(shí)現(xiàn)相對(duì)路徑 RPATH(部署更靈活)
aarch64-linux-gnu-g++ main.cpp -lfoo -o myapp \
    -Wl,-rpath,'$ORIGIN/lib'      # $ORIGIN = 可執(zhí)行文件所在目錄
# 這樣只要 libfoo.so 在 myapp 同級(jí)的 lib/ 目錄下就能找到

5.8.5 子場(chǎng)景 E:ARM32 與 ARM64 混淆

場(chǎng)景:目標(biāo)設(shè)備是 32 位 ARM(armhf),但交叉編譯時(shí)用了 64 位工具鏈,或反之。

復(fù)現(xiàn)

# 目標(biāo)機(jī)是 ARM32,但用 ARM64 工具鏈編譯
aarch64-linux-gnu-g++ -shared -fPIC -o libfoo.so foo.cpp

# 部署到 ARM32 目標(biāo)機(jī)
$ ./myapp
./myapp: error while loading shared libraries: ./libfoo.so: wrong ELF class: ELFCLASS64

# 反過(guò)來(lái):目標(biāo)機(jī)是 ARM64,但用 ARM32 工具鏈編譯
arm-linux-gnueabihf-g++ -shared -fPIC -o libfoo.so foo.cpp

$ ./myapp
./myapp: error while loading shared libraries: ./libfoo.so: wrong ELF class: ELFCLASS32

排查

# 確認(rèn) .so 的架構(gòu)
$ readelf -h libfoo.so | grep -E 'Class|Machine'
  Class:                             ELF64        # 64 位
  Machine:                           AArch64      # ARM64

# 確認(rèn)目標(biāo)機(jī)的架構(gòu)
$ uname -m
armv7l          # ARM32

# 或者
$ dpkg --print-architecture
armhf           # ARM32 硬浮點(diǎn)

修復(fù)

# ARM32 (armhf) 交叉編譯
arm-linux-gnueabihf-g++ -shared -fPIC -o libfoo.so foo.cpp

# ARM64 (aarch64) 交叉編譯
aarch64-linux-gnu-g++ -shared -fPIC -o libfoo.so foo.cpp

# 始終在部署前驗(yàn)證架構(gòu)一致性
readelf -h libfoo.so | grep Machine
# Machine:                           ARM            → ARM32
# Machine:                           AArch64        → ARM64

5.8.6 交叉編譯場(chǎng)景排查速查

排查項(xiàng)命令說(shuō)明
確認(rèn) .so 架構(gòu)readelf -h libfoo.so | grep MachineARM = 32位,AArch64 = 64位
確認(rèn) ELF 類readelf -h libfoo.so | grep ClassELF32 = 32位,ELF64 = 64位
確認(rèn)依賴庫(kù)(交叉工具)aarch64-linux-gnu-readelf -d libfoo.so | grep NEEDED不依賴 ldd
確認(rèn)符號(hào)版本需求aarch64-linux-gnu-objdump -T libfoo.so | grep '\*UND\*'查看需要的符號(hào)版本
確認(rèn) glibc 版本需求aarch64-linux-gnu-readelf -V libfoo.so | grep GLIBC對(duì)比目標(biāo)機(jī) libc 版本
目標(biāo)機(jī) glibc 版本ldd --versionstrings /lib/libc.so.6 | grep GLIBC在 ARM 目標(biāo)機(jī)上執(zhí)行
目標(biāo)機(jī) libstdc++ 版本strings /usr/lib/libstdc++.so.6 | grep GLIBCXX在 ARM 目標(biāo)機(jī)上執(zhí)行
檢查 RPATHaarch64-linux-gnu-readelf -d myapp | grep -E 'RPATH|RUNPATH'編譯時(shí)嵌入的搜索路徑

6. 預(yù)防措施與最佳實(shí)踐

編譯選項(xiàng)

選項(xiàng)作用
-fPIC生成位置無(wú)關(guān)代碼,編譯動(dòng)態(tài)庫(kù)必須
-rdynamic將所有符號(hào)導(dǎo)出到動(dòng)態(tài)符號(hào)表,主程序使用 dlopen 時(shí)必需
-Wl,-rpath,<path>在可執(zhí)行文件中嵌入運(yùn)行時(shí)庫(kù)搜索路徑
-Wl,--no-undefined鏈接時(shí)檢查所有符號(hào)是否有定義,盡早發(fā)現(xiàn)問(wèn)題
-Wl,--as-needed只鏈接實(shí)際需要的庫(kù),減少不必要的依賴
-z,defs等同于 --no-undefined,創(chuàng)建共享庫(kù)時(shí)檢查未定義符號(hào)

代碼規(guī)范

// 1. C/C++ 兼容的頭文件寫(xiě)法
#ifdef __cplusplus
extern "C" {
#endif
void c_api_function(void);
#ifdef __cplusplus
}
#endif
// 2. 導(dǎo)出宏(控制符號(hào)可見(jiàn)性)
#ifdef MYLIB_EXPORTS
    #define MYLIB_API __attribute__((visibility("default")))
#else
    #define MYLIB_API
#endif
MYLIB_API void exported_function(void);
// 3. 隱藏不需要導(dǎo)出的符號(hào)
__attribute__((visibility("hidden"))) void internal_function(void);

CMake 配置

# 設(shè)置 -fPIC
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# 設(shè)置 RPATH
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}")

# 鏈接時(shí)檢查未定義符號(hào)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")

# 使用 --whole-archive
target_link_libraries(myapp
    PRIVATE
    "-Wl,--whole-archive"
    plugins
    "-Wl,--no-whole-archive"
)

7. 速查表

命令用途
nm -CD libfoo.so查看動(dòng)態(tài)庫(kù)的符號(hào)(demangled)
nm -CD libfoo.so | grep ' U '查看庫(kù)中未定義的符號(hào)
nm -CD libfoo.so | grep ' T '查看庫(kù)中導(dǎo)出的函數(shù)
readelf -s libfoo.so查看完整符號(hào)表
readelf -d libfoo.so | grep NEEDED查看庫(kù)的運(yùn)行時(shí)依賴
readelf -V libfoo.so查看符號(hào)版本信息
readelf -S libfoo.so | grep -E 'symtab|dynsym'檢查符號(hào)表段是否存在
objdump -T libfoo.so查看動(dòng)態(tài)符號(hào)表(含版本)
ldd ./myapp查看程序依賴的動(dòng)態(tài)庫(kù)
c++filt _Z3foovC++ 符號(hào) demangle
LD_DEBUG=symbols ./myapp 2>&1運(yùn)行時(shí)符號(hào)查找調(diào)試
LD_DEBUG=libs ./myapp 2>&1運(yùn)行時(shí)庫(kù)加載調(diào)試
LD_PRELOAD=libfix.so ./myapp強(qiáng)制優(yōu)先加載指定庫(kù)
strip --strip-all -o libfoo_stripped.so libfoo.so去除調(diào)試符號(hào)(保留動(dòng)態(tài)符號(hào))
readelf -h libfoo.so | grep Machine確認(rèn) .so 的目標(biāo)架構(gòu)(ARM/AArch64/x86)
aarch64-linux-gnu-readelf -d libfoo.so | grep NEEDED交叉編譯場(chǎng)景下查看依賴庫(kù)(不依賴 ldd)
aarch64-linux-gnu-objdump -T libfoo.so | grep '\*UND\*'交叉編譯場(chǎng)景下查看未定義符號(hào)及版本
aarch64-linux-gnu-readelf -V libfoo.so交叉編譯場(chǎng)景下查看符號(hào)版本需求

總結(jié):排查 undefined symbol 的核心思路是三步走——確認(rèn)要找什么符號(hào)確認(rèn)誰(shuí)應(yīng)該提供這個(gè)符號(hào)、確認(rèn)提供者是否被正確加載。熟練掌握 nm、readelf、LD_DEBUG 三大工具,結(jié)合本文的排查流程,絕大多數(shù)符號(hào)問(wèn)題都能快速定位。

以上就是Linux動(dòng)態(tài)庫(kù).so找不到符號(hào)表的排查指南的詳細(xì)內(nèi)容,更多關(guān)于Linux動(dòng)態(tài)庫(kù).so找不到符號(hào)表的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Apache?HertzBeat?安裝使用完整指南

    Apache?HertzBeat?安裝使用完整指南

    Apache?HertzBeat?是AI?驅(qū)動(dòng)、無(wú)?Agent、一站式開(kāi)源實(shí)時(shí)觀測(cè)系統(tǒng),支持指標(biāo)/日志統(tǒng)一采集、告警智能分發(fā)、靈活自定義監(jiān)控,兼容多協(xié)議與云原生環(huán)境,本文基于?Gitee?官方倉(cāng)庫(kù)整理最全安裝、配置、使用流程,小白也能快速上手
    2026-04-04
  • ubuntu18.04 安裝qt5.12.8及環(huán)境配置的詳細(xì)教程

    ubuntu18.04 安裝qt5.12.8及環(huán)境配置的詳細(xì)教程

    這篇文章主要介紹了ubuntu18.04 安裝qt5.12.8及環(huán)境配置的教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • Linux下的多線程編程實(shí)例解析

    Linux下的多線程編程實(shí)例解析

    這篇文章主要介紹了Linux下的多線程編程實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Linux實(shí)現(xiàn)查看某一端口是否開(kāi)放

    Linux實(shí)現(xiàn)查看某一端口是否開(kāi)放

    文章介紹了三種檢查端口6379是否開(kāi)放的方法:通過(guò)lsof查看進(jìn)程占用,用netstat區(qū)分TCP/UDP監(jiān)聽(tīng)狀態(tài),以及用telnet測(cè)試遠(yuǎn)程連接可達(dá)性
    2025-08-08
  • linux掛載本地yum源問(wèn)題

    linux掛載本地yum源問(wèn)題

    這篇文章主要介紹了linux掛載本地yum源問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • CentOS 7.x編譯安裝Nginx1.10.3+MySQL5.7.16+PHP5.2 5.3 5.4 5.5 5.6 7.0 7.1多版本全能環(huán)境

    CentOS 7.x編譯安裝Nginx1.10.3+MySQL5.7.16+PHP5.2 5.3 5.4 5.5 5.6

    這篇文章主要介紹了CentOS 7.x編譯安裝Nginx1.10.3+MySQL5.7.16+PHP5.2 5.3 5.4 5.5 5.6 7.0 7.1多版本全能環(huán)境,需要的朋友可以參考下
    2018-01-01
  • Centos7如何備份和還原Redis數(shù)據(jù)的方法

    Centos7如何備份和還原Redis數(shù)據(jù)的方法

    這篇文章主要介紹了Centos7如何備份和還原Redis數(shù)據(jù)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • Linux使用iostat命令監(jiān)控系統(tǒng)磁盤I/O性能和CPU使用情況

    Linux使用iostat命令監(jiān)控系統(tǒng)磁盤I/O性能和CPU使用情況

    iostat(Input/Output Statistics)是一個(gè)用于監(jiān)控系統(tǒng)磁盤I/O(輸入/輸出)性能和CPU使用情況的強(qiáng)大工具,本文給大家介紹了Linux如何使用iostat命令監(jiān)控系統(tǒng)磁盤I/O性能和CPU使用情況,需要的朋友可以參考下
    2025-10-10
  • Centos7環(huán)境安裝Python3的方法

    Centos7環(huán)境安裝Python3的方法

    這篇文章主要介紹了Centos7環(huán)境安裝Python3的方法,簡(jiǎn)單描述了CentOS環(huán)境下安裝Python3的具體步驟、命令與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2018-03-03
  • Apache 解決80端口占用問(wèn)題

    Apache 解決80端口占用問(wèn)題

    今天小編發(fā)現(xiàn)一個(gè)很棘手的問(wèn)題,在安裝mongodb后發(fā)現(xiàn)apache無(wú)法啟動(dòng)問(wèn)題,今天小編給大家?guī)?lái)了Apache 解決80端口占用問(wèn)題 ,感興趣的朋友一起看看吧
    2018-03-03

最新評(píng)論

卓尼县| 扎赉特旗| 大城县| 澄迈县| 尼玛县| 文山县| 莱西市| 图片| 承德县| 双江| 当阳市| 池州市| 安阳市| 南和县| 防城港市| 华容县| 霍山县| 虹口区| 延庆县| 湟中县| 郑州市| SHOW| 京山县| 广宗县| 夹江县| 平和县| 大洼县| 五指山市| 长子县| 大新县| 博野县| 渑池县| 基隆市| 隆回县| 澳门| 浑源县| 揭西县| 南和县| 松江区| 武城县| 永兴县|