显式链接、隐式链接和显式加载、隐式加载以及动态库路径查找

2023-11-11

我们知道库一般有静态库和动态库2种:

  • 静态库是编译时就链接到可执行文件中的;
  • 动态库是在程序运行时再进行加载的。

故本文讨论的链接与加载方式是指对动态库而言的。

一、动态库的加载方式

1、隐式加载

    就是我们需要准备好.h、.lib或者.so,对头文件进行包含,并添加对lib的链接命令,来完成对库函数的调用。这种链接方式,称之为隐式链接

    在程序从开始运行时,就会按照系统中一定的搜索路径,寻找动态库,找到就自动加载它,才能成功运行程序,这些步骤,是系统自动完成的。

    一般来说,隐式链接的动态库,在程序加载时,无需用户干涉,便称之为隐式加载

    一般通常使用隐式加载的方式,比显示加载,代码编写更简单。

2、显式加载

    我们对动态库的调用,是在代码中直接使用LoadLibrary,或其他加载函数,直接对dll或so进行加载,然后解析文件中的函数符号,并调用该函数。这种链接方式,称之为显式链接

    这种加载方式依赖于用户的加载代码,所以是程序运行起来后,属于延迟加载。需要用户明确知道库文件名称,故称之为显式加载

    这种方式,解析函数符号的代码较多,编写不方便。

二、Windows下程序如何查找dll

1、搜索顺序

在win系统下,应用程序搜索其依赖的dll时,按如下顺序进行查找:

  • 应用程序所在的路径;
  • Windows的SYSTEM目录,如C:\Windows\System,通过调用GetSystemDirectory函数可以获取这个目录的路径;
  • Windows目录,如C:\Windows,通过调用GetWindowsDirectory函数可以获取这个目录的路径;
  • PATH环境变量指定的路径。

上述查找顺序,对隐式加载、显式加载方式的dll均有效

隐式加载dll时,没什么好说的,必定通过上述顺序查找dll。

显式加载dll时,由于使用了LoadLibrary函数,如下:

HMODULE WINAPI LoadLibrary(
  _In_ LPCTSTR lpFileName
);
  • 如果lpFileName字符串指定完整路径,则该函数仅搜索该dll的路径。
  • 如果lpFileName字符串指定一个没有路径的dll名称或者相对路径(如lpFileName=“Hello.dll”),则该函数使用标准搜索策略来查找模块,也就是使用上述顺序进行查找"Hello.dll"。

2、指定隐式加载dll的路径

对于需要隐式加载的dll,由于其加载过程由系统接管。即隐式加载的dll是在exe主体模块之前加载进来的,执行顺序上是先通过PE文件的导入表加载隐式的dll,然后再加载EXE本体。

所以在程序exe中无法通过代码,来指定隐式加载dll的路径。

只能通过修改PATH环境变量来指定加载dll的路径,或者直接将dll移入可以被搜索到的路径中来实现。

3、指定显式加载dll的路径

对于需要显式加载的dll,由于其LoadLibrary函数参数,即可指定dll的路径;若只提供了文件名,则也可以将dll的路径设置到PATH环境变量中,或者直接将dll移入可以被搜索到的路径中。

4、小结

dll的搜索顺序,对于无论是显式、隐式加载dll,都是有效的,因为都需要查找dll。只不过显式加载时,它通过传递参数直接定位到具体dll时,不需要额外搜索。

牢记:使用隐式加载时,程序中无法通过代码,来指定该dll搜索路径。

无论显式、隐式加载,通常最简单的办法,就是将dll与exe放于同一目录下。

三、Linux下程序如何查找so

1、搜索顺序

在linux系统下,应用程序搜索其依赖的so时,按如下顺序进行查找:

  • gcc编译时指定的运行时库路径 -Wl,-rpath
  • 环境变量LD_LIBRARY_PATH指定的路径
  • 从cache文件/etc/ld.so.cache中查找,该文件包含了先前在库路径中找到的编译好的候选库列表。
  • 系统默认库位置/lib和/usr/lib路径

上述查找顺序,对隐式加载、显式加载方式的so均有效

隐式加载so时,没什么好说的,必定通过上述顺序查找so。

显式加载so时,由于使用了dlopen函数,如下:

void* dlopen(const char* filename, int flags);
  • 如果filename字符串指定完整路径,则该函数仅搜索该dll的路径。
  • 如果filename字符串指定一个没有路径的dll名称或者相对路径(如lpFileName=“Hello.so”),则该函数使用标准搜索策略来查找模块,也就是使用上述顺序进行查找"Hello.so"。

关于linux下显式加载so的例子,可参考《Linux 动态加载并调用动态库(.so)方法介绍》

2、指定隐式加载so的路径

对于需要隐式加载的so,由于其加载过程由系统接管。即隐式加载的so是在程序主体模块之前加载进来的,执行顺序上是先加载隐式的so,然后再加载程序本体。

所以在程序中无法通过代码,来指定隐式加载so的路径。

但是可以通过修改上述顺序中任一环节,来指定隐式加载的so路径。

linux与win有区别,win下直接将dll和exe放于同一目录就可以找到。但是在linux下不行,这显得有些智障。。。主要是linux的理念不一样,系统设计者希望大家把so库都放到系统指定的目录下,但是个人并不喜欢这样做,很多的应用so糅合在一起很乱。

推荐使用第一种,即指定gcc编译选项来实现。

假设,我们使用Qt开发的程序,希望在程序所在目录下lib/中去寻找so,那么可以在程序工程的.pro文件中,添加如下编译选项:

QMAKE_LFLAGS += -Wl,-rpath=./lib

则该程序启动时,会在./lib/去找so。当然也可以改成./当前目录下,这就和win下时很像了。

另外,如果程序显示加载1.so,1.so隐式加载调用2.so,出现找不到2.so的问题。那么,可以在1.so代码所属工程.pro中,添加上述的编译选项,就可以解决这个问题。

3、指定显式加载so的路径

对于需要显式加载的so,由于其dlopen函数参数,即可指定so的路径;若只提供了文件名,则可以通过修改上述顺序中任一环节,来指定so路径。

4、小结

so的搜索顺序,对于无论是显式、隐式加载,都是有效的,因为都需要查找so。只不过显式加载时,它通过传递参数直接定位到具体so时,不需要额外搜索。

牢记:使用隐式加载时,程序中无法通过代码,来指定该so搜索路径。但是可以通过编译选项来指定。

四、总结

win、linux下动态库的加载和搜索机制很相似,但是有一些不同,主要不同之处体现在,隐式加载动态库时:

  • win下,只能通过修改PATH指定库路径,但是默认支持搜索程序当前目录。
  • linux下,支持gcc编译选项指定库路径,但是默认不支持搜索程序当前目录。

另外,在win下,使用隐式加载时,程序中无法通过代码,来指定该dll搜索路径。

在linux下,虽然依然不能通过程序中代码来,指定该so搜索路径;但是可以使用gcc编译选项的方式,提前指定该程序搜索so的路径,也算是一种很好的补充方式。



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

显式链接、隐式链接和显式加载、隐式加载以及动态库路径查找 的相关文章

  • C++_生成随机字符串

    include
  • C#中unsafe的使用

    1 unsafe在C 程序中的使用场合 实时应用 采用指针来提高性能 引用非 net DLL提供的如C 编写的外部函数 需要指针来传递该函数 调试 用以检测程序在运行过程中的内存使用状况 2 使用unsafe的利弊 好处是 性能和灵活性提高
  • fastcgi的环境变量

    FCGI ROLE RESPONDER SCRIPT FILENAME scripts 5 cgi QUERY STRING aaa 11111111111111 bbb 2222222222222222 ccc 3333333333333
  • [原]Pro*C介绍-内嵌SQL

    Translate by Z Jingwei Document address http www db stanford edu ullman fcdb oracle or proc html Pro C介绍内嵌SQL 概要 Pro C语法
  • SQL 查询指定行数的数据。

    今天遇到一个关于 查询指定行数的数据 的sql查询语句问题 突然发现以前没怎么接触过 刚才想起来了 赶紧看了下文档 又上网搜了下 有了下面的东西 不知道有没有什么地方不对 oracle 先看一下文档中关于any和all的例子 很不错噢 An
  • C++ 中的虚函数及虚函数表

    C 中的虚函数及虚函数表 一 虚函数及虚函数表的定义 二 虚函数表指针和虚函数表的创建时机 三 虚函数实现多态的原理 一 虚函数及虚函数表的定义 虚函数 虚函数就是在基类中定义一个未实现的函数名 使用虚函数的核心目的就是通过基类访问派生类定
  • 五. python面向对象(多态 和metaclass=abc.ABCMeta)

    一 多态 多肽 一种事物的多种形态 叫多肽 例如 动物 animal 猫 狗 animal py 文件 动物类 都有名字这个属性 和吃这个方法 class Animal object def init self name self name
  • Qt5学习之路(vs2012下创建一个QT应用程序)2013-10-14

    刚开始学习QT在网上找的资料基本都是使用QT Create进行开发的 VS下开发的学习资料感觉很少很难找的到 视频教程也基本没看到过貌似 因为我们研发中心是使用MFC进行开发开发工具是VS2010 使用QT开发的话基本我们不会再使用QT C
  • 编写递归算法,计算二叉树叶子结点的数目。

    编写递归算法 计算二叉树叶子结点的数目 编写递归算法 计算二叉树叶子结点的数目 include stdio h 包含 getchar scanf printf include malloc h malloc 动态申请空间 函数 二叉树 结点
  • Vc/MFC中自定义消息及其PostMessage触发使用

    http blog csdn net ztz0223 article details 2058402 http blog csdn net a8082649 article details 7733527 http bbs csdn net
  • JNA模拟复杂的C类型——Java映射char*、int*、float*、double*

    文章目录 引言 Java Native Type Conversions Java和C基本类型指针对应关系 Pointer的具体用法 引言 最近项目在用Java调用C写的一些三方库 没办法直接调 用Java封装一下C的接口 这就少不了要用到
  • c++得到窗口句柄

    include
  • Lua和C++交互总结(很详细)

    出处 http blog csdn net shun fzll article details 39120965 一 lua堆栈 要理解lua和c 交互 首先要理解lua堆栈 简单来说 Lua和C c 语言通信的主要方法是一个无处不在的虚拟
  • ATL字符串转换宏

    有比MultiByteToWideChar和WideCharToMultiByte更简单的字符串转换宏 你相信吗 头文件 d program files microsoft visual studio 8 vc atlmfc include
  • 值得学习与推荐的c/c++框架和函数库

    这几天不上班 翻翻Evernote中记录的一些笔记 刚好有时间把记录的一些好玩链接转载一下 这篇文章里提到的很多库都用过 尤其是图像处理相关库 尤其是opencv及cximage 当时做图像算法时 很多算法就是从上面找来 然后自己修改的 比
  • 模板的完全特例化和部分特例化

    介绍 完全特例化就是类型完全明确的版本 而部分特例化指的是 只知道是几个参数的函数而不知道参数的类型 或者是只知道是引用或者是指针类型 而不知道具体是char 还是 int 模板特例化实例1 template
  • mfc窗口创建的create与oncreate

    在view类中 create 是虚函数由框架调用 是用来 生成一个窗口的子窗口 oncreate 消息响应函数 是用来 表示一个窗口正在生成 某个CWnd的Create函数由当前CWnd的Owner调用 而在CWnd Create中 又会调
  • C++ 字符串比较------strcmp函数和strncmp函数

    strcmp 函数原型 int strcmp const char str1 const char str2 功能 strcmp函数会按照字典顺序逐个比较两个字符串的字符 直到遇到不同的字符或者遇到字符串结束符 0 返回值 该函数返回值如下
  • C 语言运算符详解

    C 语言中的运算符 运算符用于对变量和值进行操作 在下面的示例中 我们使用 运算符将两个值相加 int myNum 100 50 虽然 运算符通常用于将两个值相加 就像上面的示例一样 它还可以用于将变量和值相加 或者将变量和另一个变量相加
  • C++ 中 const 和 constexpr 关键字解析:常量、函数和指针

    很多 C 的初学者看到 const 这个关键字的第一反应都是一头雾水 主要是因为 const 可 以出现在很多的位置 以及后面加入的 constexpr 更是常常感到困惑 今天就为大家一一解释出现它们的含义和以及作用 const 关键字 c

随机推荐