ldd using


手写一个简单项目,制造问题场景用ldd进行排查

条件

  1. 一个自定义共享库 mymath.h, mymath.c编译为共享库
  2. 一个依赖该库的主程序
  3. 创建符号链接,模拟标准库的版本管理
// mymath.h
#ifndef MYMATH_H
#define MYMATH_H

int add(int a, int b);
int multiply(int a, int b);

#endif
// mymath.c
#include "mymath.h"

int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
     return a * b;
}
#!/bin/bash

# 生成位置无关代码
gcc -c -fPIC mymath.c -o mymath.o

# 生成共享库,版本号设为 1.0.0
gcc -shared -Wl,-soname,libmymath.so.1 -o libmymath.so.1.0.0 mymath.o

# 创建符号链接(模拟标准库的版本管理)
ln -s libmymath.so.1.0.0 libmymath.so.1
ln -s libmymath.so.1 libmymath.so
// main.c
#include <stdio.h>
#include "mymath.h"

int main() {
    int a = 10, b = 5;
    printf("Testing custom math library:\n");
    printf("%d + %d = %d\n", a, b, add(a, b));
    printf("%d * %d = %d\n", a, b, multiply(a, b));
    return 0;
}
gcc main.c -L -lmymath -o my_program

制造问题场景

场景1:直接运行

直接运行会报错:找不到库

error while loading shared libraries: libmymath.so.1: cannot open shared object file: No such file or directory

ldd查看会发现libmymath.so.1 => not found,说明动态链接器找不到自定义库

临时解决方案

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

场景2:库版本升级导致符号缺失

升级库,但故意移除一个函数,模拟API不兼容的情况

修改mymath.c,移除multiply

#include "mymath.h"

int add(int a, int b) {
    return a + b;
}

编译新版本并更新符号链接

gcc -c -fPIC mymath.c -o mymath.o
gcc -shared -Wl, -soname,libmymath.so.1 -o libmymath.so.1.1.0 mymath.o

# 更新符号链接
ln -sf libmymath.so.1.1.0 libmymath.so.1
ln -sf libmymath.so.1 libmymath.so

运行程序,仍然用旧的main没有重新编译

$ export LD_LIBRARY_PATH=.:LD_LIBRARY_PATH
$ ./my_program

报错

./my_program: symbol lookup error: ./my_program: undefined symbol: multiply

ldd -r 深入排查

$ LD_LIBRARY_PATH=. ldd -r ./my_program
        linux-vdso.so.1 (0x00007286cb3c5000)
        libmymath.so.1 => ./libmymath.so.1 (0x00007286cb3b3000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007286cb000000)
        /lib64/ld-linux-x86-64.so.2 (0x00007286cb3c7000)
undefined symbol: multiply      (./my_program)

最后一行明确指出,说明程序需要的函数在新版库中不存在

验证库中的符号

$ nm -D libmymath.so.1 | grep ' T '
00000000000010f9 T add

解决方案

  • 如果API兼容性很重要,重新编译程序链接新版库
  • 或者保留旧版库在另一个路径,用RPATH指向它

场景3:使用RPATH固化路径

不想每次设置LD_LIBRARY_PATH,把库路径写入程序

# gcc main.c -L -lmymath -Wl, -rpath,[绝对路径] -o my_program_rpath

验证

$ unset LD_LIBRARY_PATH
$ ldd ./my_program_rpath

输出

        linux-vdso.so.1 (0x00007f8e61fee000)
        libmymath.so.1 => libmymath.so.1的绝对路径 (0x00007f8e61fdc000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8e61c00000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8e61ff0000)

无需任何变量,直接运行即可

场景4:用-u找出多余的链接

编译时故意多加一个不需要的库

gcc main.c -L. -lmymath -Wl,--no-as-needed -lm -o my_program_extra
  • --no-as-needed强制保留未使用的库,以免被链接器优化掉

ldd -u 检查

$ LD_LIBRARY_PATH=. ldd -u ./my_program_extra
Unused direct dependencies:
        /lib/x86_64-linux-gnu/libm.so.6