首页 > 其他分享 >运行期加载时共享库路径搜索优先级实验

运行期加载时共享库路径搜索优先级实验

时间:2024-08-01 20:57:00浏览次数:8  
标签:opt 优先级 hellolib lib libhello so.1 共享 runtime 加载

目录

前言

《共享库链接和加载时的路径搜索优先级》中提到,共享库在运行期加载时的路径搜索优先级为:
RPATH>LD_LIBRARY_PATH>RUNPATH>/etc/ld.so.conf/etc/ld.so.conf.d/*>默认库路径

本实验分两步验证上述结论
①单独测试每种方法指定的路径的可行性
②查看链接器输出的详细信息,验证上述优先级顺序

实验环境

操作系统:Ubuntu 20.04
编译器:g++-11.4.0
make:GNU Make 4.2.1

目录说明

项目的目录结构如下:

.
├── lib
├── obj
├── libhello.cpp
├── main.cpp
├── run_cmp_all.sh
├── run_default.sh
├── run_ld_library_path.sh
├── run_ld_so_cache.sh
├── run_none.sh
├── run_rpath.sh
├── run_runpath.sh
└── makefile

其中:

  • lib为存放共享库的文件夹
  • obj为存放可重定位目标文件的文件夹
  • libhello.cpp为共享库源码,它将被编译为libhello.so.1.1.0(soname为libhello.so.1
  • mian.cpp为主函数,其中调用了hello函数
  • run_cmp_all.sh为对比测试上述路径优先级的脚本
  • run_default.sh为单独测试默认路径可行性的脚本
  • run_ld_library_path.sh为单独测试LD_LIBRARY_PATH可行性的脚本
  • run_ld_so_cache.sh为单独测试ld.so.cache可行性的脚本
  • run_none.sh为不配置任何路径的脚本,用于演示搜索不到动态库文件,程序无法运行的情况
  • run_rpath.sh为单独测试RPATH可行性的脚本
  • run_runpath.sh为单独测试RUNPATH可行性的脚本
  • makefile为自动化构建脚本

本文主要讨论运行期的加载过程,因此不对编译期的过程做过多解释,内容详见makefile文件

在附录中,我将提供本次实验涉及到的代码。

单独测试

不配置路径

不做任何路径的配置并且不在默认路径下放置libhello.so.1文件,查看程序是否可以运行成功。

直接运行脚本即可完成上述操作:

sh ./run_none.sh

输出:

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

可以看到由于我们没有配置任何额外的搜索路径,并且没有在默认搜索路径下放置libhello.so.1文件,动态链接器就找不到相应的共享库文件,就会导致加载失败,程序无法运行。

默认路径

libhello.so.1.1.0拷贝至默认搜索路径/usr/lib,并在/usr/lib下创建一个软链接(libhello.so.1)指向它
运行可执行文件。

直接运行脚本即可完成上述操作:

sh ./run_default.sh

输出:

Hello from the 1.1.0 library!

没有报错。

ld.so.cache

创建路径/opt/hellolib_runtime
libhello.so.1.1.0拷贝至/opt/hellolib_runtime,并在/opt/hellolib_runtime下创建一个软链接(libhello.so.1)指向它
/etc/ld.so.conf.d中新增一个配置文件libhello.conf,并向其中写入动态库文件所在的路径/opt/hellolib_runtime
使用ldconfig命令更新ld.so.cache
运行可执行文件

直接运行脚本即可完成上述操作:

sh ./run_ld_so_cache.sh

输出:

Hello from the 1.1.0 library!

没有报错。

RUNPATH

在编译期添加-Wl,--enable-new-dtags,-rpath,/opt/hellolib_runtime指定RUNPATH
libhello.so.1.1.0拷贝至/opt/hellolib_runtime,并在/opt/hellolib_runtime下创建一个软链接(libhello.so.1)指向它
运行可执行文件

直接运行脚本即可完成上述操作:

sh ./run_runpath.sh

输出:

Hello from the 1.1.0 library!

没有报错。

LD_LIBRARY_PATH

创建路径/opt/hellolib_runtime
libhello.so.1.1.0拷贝至/opt/hellolib_runtime,并在/opt/hellolib_runtime下创建一个软链接(libhello.so.1)指向它
向环境变量LD_LIBRARY_PATH中添加/opt/hellolib_runtime
运行可执行文件

直接运行脚本即可完成上述操作:

sh ./run_ld_library_path.sh

输出:

Hello from the 1.1.0 library!

没有报错。

RPATH

在编译期添加-Wl,--disable-new-dtags,-rpath,/opt/hellolib_runtime指定RPATH
libhello.so.1.1.0拷贝至/opt/hellolib_runtime,并在/opt/hellolib_runtime下创建一个软链接(libhello.so.1)指向它
运行可执行文件

直接运行脚本即可完成上述操作:

sh ./run_rpath.sh

输出:

Hello from the 1.1.0 library!

没有报错。

优先级测试

在此模块我们要测试共享库在运行期加载时的路径搜索优先级(没有测RUNPATH,有兴趣的朋友可以自行测试一下),关键步骤如下:
创建路径/opt/hellolib_runtime,/opt/hellolib_runtime1,/opt/hellolib_runtime2
在编译期添加-Wl,--disable-new-dtags,-rpath,/opt/hellolib_runtime指定RPATH
向环境变量LD_LIBRARY_PATH中添加/opt/hellolib_runtime1
/etc/ld.so.conf.d中新增一个配置文件libhello.conf,向其中写入路径/opt/hellolib_runtime2,并使用ldconfig命令更新ld.so.cache
libhello.so.1.1.0拷贝至默认搜索路径/usr/lib,并在/usr/lib下创建一个软链接(libhello.so.1)指向它
运行可执行文件

直接运行脚本即可完成上述操作:

sh ./run_cmp_all.sh

在脚本中,我设置了环境变量LD_DEBUG=libs,启用了动态链接器的调试输出(针对库的加载过程),因此终端输出了以下信息(仅截取关键部分):

 	460445:     find library=libhello.so.1 [0]; searching
    460445:      search path=/opt/hellolib_runtime/tls/haswell/x86_64:/opt/hellolib_runtime/tls/haswell:/opt/hellolib_runtime/tls/x86_64:/opt/hellolib_runtime/tls:/opt/hellolib_runtime/haswell/x86_64:/opt/hellolib_runtime/haswell:/opt/hellolib_runtime/x86_64:/opt/hellolib_runtime            (RPATH from file ./hello_rpath)
...
    460445:       trying file=/opt/hellolib_runtime/libhello.so.1
    460445:      search path=/opt/hellolib_runtime1/tls/haswell/x86_64:/opt/hellolib_runtime1/tls/haswell:/opt/hellolib_runtime1/tls/x86_64:/opt/hellolib_runtime1/tls:/opt/hellolib_runtime1/haswell/x86_64:/opt/hellolib_runtime1/haswell:/opt/hellolib_runtime1/x86_64:/opt/hellolib_runtime1:tls/haswell/x86_64:tls/haswell:tls/x86_64:tls:haswell/x86_64:haswell:x86_64:               (LD_LIBRARY_PATH)
...
    460445:       trying file=/opt/hellolib_runtime1/libhello.so.1
...
    460445:      search cache=/etc/ld.so.cache
    460445:      search path=/lib/x86_64-linux-gnu/tls/haswell/x86_64:/lib/x86_64-linux-gnu/tls/haswell:/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls:/lib/x86_64-linux-gnu/haswell/x86_64:/lib/x86_64-linux-gnu/haswell:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu/tls/haswell/x86_64:/usr/lib/x86_64-linux-gnu/tls/haswell:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls:/usr/lib/x86_64-linux-gnu/haswell/x86_64:/usr/lib/x86_64-linux-gnu/haswell:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu:/lib/tls/haswell/x86_64:/lib/tls/haswell:/lib/tls/x86_64:/lib/tls:/lib/haswell/x86_64:/lib/haswell:/lib/x86_64:/lib:/usr/lib/tls/haswell/x86_64:/usr/lib/tls/haswell:/usr/lib/tls/x86_64:/usr/lib/tls:/usr/lib/haswell/x86_64:/usr/lib/haswell:/usr/lib/x86_64:/usr/lib            (system search path)
...
    460445:       trying file=/lib/libhello.so.1

可以看出动态链接器依次搜索了:RPATH指定的路径,LD_LIBRARY_PATH记录的路径,ld.so.cache中记录的路径,默认路径。

因此验证了上述的路径搜索优先级的顺序。

附录

库文件源码

//file: libhello.cpp
#include <iostream>
void hello()
{
    std::cout << "Hello from the 1.1.0 library!" << std::endl;
}

主程序源码

//file: main.cpp
extern void hello();
int main()
{
    hello();
    return 0;
}

makefile

# file: makefile
CXX = g++
CXXFLAGS = -fPIC
LDFLAGS = -shared
DEBUG_MODE ?= 0
 
ifeq ($(DEBUG_MODE),1)
    DEBUG_OPTS = -v -Wl,--verbose
endif
 
all: lib/libhello.so.1.1.0 obj/main.o
 
lib/libhello.so.1.1.0: libhello.cpp
	$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -Wl,-soname,libhello.so.1
 
obj/main.o: main.cpp
	$(CXX) -c -o $@ $^
 
# 仅使用-L
hello: obj/main.o
	mkdir -p /opt/hellolib_compile
	cp ./lib/libhello.so.1.1.0 /opt/hellolib_compile
	ln -sf /opt/hellolib_compile/libhello.so.1.1.0 /opt/hellolib_compile/libhello.so
	$(CXX) $(DEBUG_OPTS) -o $@ $^ -L/opt/hellolib_compile -lhello

hello_rpath: obj/main.o
	mkdir -p /opt/hellolib_compile
	cp ./lib/libhello.so.1.1.0 /opt/hellolib_compile
	ln -sf /opt/hellolib_compile/libhello.so.1.1.0 /opt/hellolib_compile/libhello.so
	mkdir -p /opt/hellolib_runtime
	cp ./lib/libhello.so.1.1.0 /opt/hellolib_runtime
	ln -sf /opt/hellolib_runtime/libhello.so.1.1.0 /opt/hellolib_runtime/libhello.so.1
	$(CXX) $(DEBUG_OPTS) -o $@ $^ -Wl,--disable-new-dtags,-rpath,/opt/hellolib_runtime -L/opt/hellolib_compile -lhello

hello_runpath: obj/main.o
	mkdir -p /opt/hellolib_compile
	cp ./lib/libhello.so.1.1.0 /opt/hellolib_compile
	ln -sf /opt/hellolib_compile/libhello.so.1.1.0 /opt/hellolib_compile/libhello.so
	mkdir -p /opt/hellolib_runtime
	cp ./lib/libhello.so.1.1.0 /opt/hellolib_runtime
	ln -sf /opt/hellolib_runtime/libhello.so.1.1.0 /opt/hellolib_runtime/libhello.so.1
	$(CXX) $(DEBUG_OPTS) -o $@ $^ -Wl,--enable-new-dtags,-rpath,/opt/hellolib_runtime -L/opt/hellolib_compile -lhello
 
clean:
	rm -f ./lib/* ./obj/* ./hello*
	rm -f /usr/lib/libhello*
	rm -rf /opt/hellolib_compile*
	rm -rf /opt/hellolib_runtime*

.PHONY: clean hello hello_rpath hello_runpath

脚本

run_none

#!/bin/bash

make
make hello

LD_DEBUG=libs ./hello

make clean

run_default

#!/bin/bash

make
make hello

cp ./lib/libhello.so.1.1.0 /usr/lib
ln -sf /usr/lib/libhello.so.1.1.0 /usr/lib/libhello.so.1

LD_DEBUG=libs ./hello

rm -f /usr/lib/libhello.so*
make clean

run_ld_so_cache

#!/bin/bash

make
make hello

mkdir -p /opt/hellolib_runtime
cp ./lib/libhello.so.1.1.0 /opt/hellolib_runtime
ln -sf /opt/hellolib_runtime/libhello.so.1.1.0 /opt/hellolib_runtime/libhello.so.1

echo "/opt/hellolib_runtime" > /etc/ld.so.conf.d/libhello.conf
ldconfig

LD_DEBUG=libs ./hello

rm -rf /opt/hellolib_runtime*
rm -f /etc/ld.so.conf.d/libhello.conf
ldconfig
make clean

run_runpath

#!/bin/bash

make
make hello_runpath

LD_DEBUG=libs ./hello_runpath

make clean

run_ld_library_path

#!/bin/bash

make
make hello

mkdir -p /opt/hellolib_runtime
cp ./lib/libhello.so.1.1.0 /opt/hellolib_runtime
ln -sf /opt/hellolib_runtime/libhello.so.1.1.0 /opt/hellolib_runtime/libhello.so.1

export LD_LIBRARY_PATH=/opt/hellolib_runtime:$LD_LIBRARY_PATH

LD_DEBUG=libs ./hello

rm -rf /opt/hellolib_runtime*
make clean

run_rpath

#!/bin/bash

make
make hello_rpath

LD_DEBUG=libs ./hello_rpath

make clean

run_cmp_all

#!/bin/bash

make
make hello_rpath
rm -rf /opt/hellolib_runtime/*

mkdir -p /opt/hellolib_runtime1
export LD_LIBRARY_PATH=/opt/hellolib_runtime1:$LD_LIBRARY_PATH

mkdir -p /opt/hellolib_runtime2
echo "/opt/hellolib_runtime2" > /etc/ld.so.conf.d/libhello.conf
ldconfig

cp ./lib/libhello.so.1.1.0 /usr/lib
ln -sf /usr/lib/libhello.so.1.1.0 /usr/lib/libhello.so.1

LD_DEBUG=libs ./hello_rpath

rm -f /usr/lib/libhello.so*
rm -f /etc/ld.so.conf.d/libhello.conf
ldconfig
rm -rf /opt/hellolib_runtime*
make clean

标签:opt,优先级,hellolib,lib,libhello,so.1,共享,runtime,加载
From: https://www.cnblogs.com/paw5zx/p/18337485

相关文章

  • Vue Hook 封装图片懒加载通用业务
     一、什么是图片懒加载图片懒加载(LazyLoading)是一种在用户需要的时候(通常是滚动到可视区域)才加载图片的技术。通过这种方式,可以减少页面的初始加载时间,减少带宽消耗,提高用户体验。二、Vue中使用IntersectionObserver实现图片懒加载IntersectionObserver是一个现代浏览器......
  • 为团队配置Linux环境,简单高效的项目共享方案
    前言最近好久没写博客了,事情太多了,我还搞了个新的好玩的项目,等后续做得差不多了来写篇文章介绍一下。在我们目前的AI项目中,团队需要共同使用一台GPU服务器来做模型训练和数据处理。为了让每个团队成员都能高效地使用这台服务器,我们决定设置一个多用户共享环境。这样,无论是代码开......
  • 处理卸载过滤和改写文件加载过滤的问题通常需要在编程或系统管理层面进行以下措施:
    处理卸载过滤和改写文件加载过滤的问题通常需要在编程或系统管理层面进行以下措施:1. 系统检测和监控确保系统中有足够的检测和监控措施,可以发现是否有未经授权的文件加载过滤器被安装或启用。使用安全工具和防病毒软件:常规扫描可以帮助发现异常的系统行为和不寻常的文件加载......
  • WebApi连接数据库报错:尝试加载Oracle客户端时引发BadImageFormatException
    出现的问题  今天在公司用C#搭建一个WebApi服务,接受请求并连接数据库进行查询,但连接数据库时报错:尝试加载Oracle客户端时引发BadImageFormatException。如果安装32位客户端组件的情况下以64位模式运行,将出现此问题。问题点  我之后了解点,确定了OracleClient客户端确实安装......
  • Unity中调试Scroll View,一个Scroll View可以加载不同的图片
    1.所有的图片宽度要相同(最好)2.锚点设置usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.UI;publicclassScrollImageScale:MonoBehaviour{publicImageimage;publicGameObjectcontent;privatef......
  • 如何将优先级任务添加到celery队列而不禁用获取
    我有一个celery工作线程,并发度设置为1,它从RabbitMQ获取任务。我想创建一个在单个并发设置中只有一个队列的系统,因此,所有任务都将添加到主队列中。关于任务-它只是一个循环,我们用更新状态。|||并行地我有两个服务。task.update_state()Celery-beat......
  • Tensorboard step和图片加载不完全处理办法
    importtorchvisionfromtorch.utils.dataimportDataLoaderfromtorch.utils.tensorboardimportSummaryWriter#加载CIFAR10测试数据集#参数说明:#"./dataset":数据集保存路径#train=False:加载测试集而非训练集#transform:将图像转换为PyTorch张量test_da......
  • Dynamics 365 online查询共享给某个Team的记录,然后取消共享
    intiSuccess=0;intiFaile=0;varadminService=CrmServiceClientCommon.GetService();//创建QueryExpression对象QueryExpressionquery=newQueryExpression("opportunity");......
  • 爱途拍共享相册小程序是一款便捷、易用的云共享相册工具
    爱途拍共享相册小程序是一款功能丰富、操作便捷的图片直播平台,它提供了极致易用的云共享相册服务,让用户能够轻松共享和管理照片与视频资料。以下是对爱途拍共享相册小程序的详细介绍:一、主要功能图片直播:爱途拍支持会议活动、婚礼庆典等场景的图片直播,组织者可以创建一个专门的......
  • 记一次解决SpringBoot项目由于依赖加载顺序问题导致启动报NoSuchMethodError的问题
    只发博客园,盗版必究先说背景平时我们的SpringBoot项目都是打成ExecutableJar启动应用,最近接了个技术需求,需要打成War包,将多个项目放在同一个Tomcat中运行。原本Jar包启动一切正常,但是打成WAR放Tomcat启动后报错了,异常栈如下:Causedby:org.springframework.beans.factory.......