精华内容
下载资源
问答
  • Linux 动态库链接详解

    2018-11-13 21:54:26
    转自 ... 静态库的缺点: 库函数被包含在每一个运行的进程...动态库: 是一个目标文件,包含代码和数据,它可以在程序运行时动态的加载并链接。修改动态库不需要重新编译目标文件,只需要更新动态库即可。动态库还可...

    转自

    https://www.cnblogs.com/MaAce/p/7999795.html

    静态库的缺点:

    1. 库函数被包含在每一个运行的进程中,会造成主存的浪费。
    2. 目标文件的size过大
    3. 每次更新一个模块都需要重新编译,更新困难,使用不方便。

    动态库: 是一个目标文件,包含代码和数据,它可以在程序运行时动态的加载并链接。修改动态库不需要重新编译目标文件,只需要更新动态库即可。动态库还可以同时被多个进程使用。在linux下生成动态库 gcc -c a.c  -fPIC -o a.o     gcc -shared -fPIC a.o -o a.so.     这里的PIC含义就是生成位置无关代码,动态库允许动态装入修改,这就必须要保证动态库的代码被装入时,可执行程序不依赖与动态库被装入的位置,即使动态库的长度发生变化也不会影响调用它的程序。

    动态链接器:

    在加载可执行文件时,加载器发现在可执行文件的程序头表中有.interp段,其中包含了动态连接器路径ld-linux.so . 加载器加载动态链接器,动态链接器完成相应的重定位工作后,再将控制权交给可执行文件。

    程序头:
      Type           Offset             VirtAddr           PhysAddr
                     FileSiz            MemSiz              Flags  Align
      PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                     0x00000000000001f8 0x00000000000001f8  R E    8
      INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                     0x000000000000001c 0x000000000000001c  R      1
          [正在请求程序解释器:/lib64/ld-linux-x86-64.so.2]

    位置无关代码PIC:

    其中动态库用到的一个核心概念就是与代码无关PIC。共享库代码位置可以是不确定的,即使代码长度发生变化也不影响调用它的程序,动态链接器是不会把可执行文件的代码段数据段与动态链接库合并的。那么这里牵涉到模块内与模块间的引用和跳转问题。

    模块内的跳转和引用:

    //b.c
    static int temp = 12344;
    extern int a;
    void add(int c)
    {
        a += c;
        temp += c;
    }
    
    //a.c
    int a = 1000;
    void add(int c);
    int main()
    {
        int c = 123;
        add(c);
        return 0;
    
    }

    我们将b.c编译为动态库,这里的temp就是模块内的引用,我们将动态库反编译可以看到

    00000000000006a8 <add>:
     6a8:   55                      push   %rbp
     6a9:   48 89 e5                mov    %rsp,%rbp
     6ac:   89 7d fc                mov    %edi,-0x4(%rbp)
     6af:   48 8b 05 2a 09 20 00    mov    0x20092a(%rip),%rax        # 200fe0 <    _DYNAMIC+0x1d0>
     6b6:   8b 10                   mov    (%rax),%edx
     6b8:   8b 45 fc                mov    -0x4(%rbp),%eax
     6bb:   01 c2                   add    %eax,%edx
     6bd:   48 8b 05 1c 09 20 00    mov    0x20091c(%rip),%rax        # 200fe0 <    _DYNAMIC+0x1d0>
     6c4:   89 10                   mov    %edx,(%rax)
     6c6:   8b 15 5c 09 20 00       mov    0x20095c(%rip),%edx        # 201028 <    temp>
     6cc:   8b 45 fc                mov    -0x4(%rbp),%eax
     6cf:   01 d0                   add    %edx,%eax
     6d1:   89 05 51 09 20 00       mov    %eax,0x200951(%rip)        # 201028 <    temp>
    0000000000201028 <temp>:             #数据段temp
      201028:   38 30                   cmp    %dh,(%rax)

    这里的mov 0x20095c(%rip),%edx  取rip中下一条指令地址+0x20095c 的数据放到edx寄存器中,地址为0x201028 正是temp的地址。模块内的函数跳转与此类似。

    模块间的跳转和引用:

    首先看一下全局变量的引用,看上例中b.so的反编译 这里引用了一个全局变量a 它的定义在另一个模块a.o 中

    00000000000006a8 <add>:
     6a8:   55                      push   %rbp
     6a9:   48 89 e5                mov    %rsp,%rbp
     6ac:   89 7d fc                mov    %edi,-0x4(%rbp)
     6af:   48 8b 05 2a 09 20 00    mov    0x20092a(%rip),%rax        # 200fe0 <    _DYNAMIC+0x1d0>
     6b6:   8b 10                   mov    (%rax),%edx
     6b8:   8b 45 fc                mov    -0x4(%rbp),%eax
     6bb:   01 c2                   add    %eax,%edx
     6bd:   48 8b 05 1c 09 20 00    mov    0x20091c(%rip),%rax        # 200fe0 <    _DYNAMIC+0x1d0>
    Disassembly of section .got:
    
    0000000000200fd0 <.got>:
        ...
    
    Disassembly of section .got.plt:
    
    0000000000201000 <_GLOBAL_OFFSET_TABLE_>:
      201000:   10 0e                   adc    %cl,(%rsi)
      201002:   20 00                   and    %al,(%rax)

    这里的mov 取地址是一个got数组的某个地址。GOT 全局偏移表,在data段的开始处的一个指针数组,每一个指针可以指向一个全局变量, GOT与引用数据的指令之间的相对距离固定,编译器为GOT每一项生成一个重定位项,加载时 动态链接器对GOT中的各项进行重定位,填入引用的地址。(32位 占4个字节  64位 8个字节)

    每一个引用全局数据的目标模块都有一张自己的GOT,那么就需要一个额外的寄存器来保持GOT表目的地址。至于模块间的函数调用和跳转也可以使用此模块,但是这种情况下过程调用都要求三条额外的指令,Linux这里就使用了叫做延迟绑定的技术,将过程调用的绑定推迟到第一次调用该过程。这种技术是通过俩个数据结构之间的交互来实现,即GOT 和PLT 全局偏移表和过程链接表, 如果一个目标模块调用定义在共享库中的任何函数,那么它就有自己的GOT和PLT,GOT是data节的一部分,PLT是text节的一部分 —— 《深入理解计算机系统》

    上图为 32 位linux。 从GOT[3]开始才是函数调用地址。我们将a.c b.so编译链接为可执行文件a, 反汇编a 观察函数add的调用(无关代码已省略)。(64位机器)

    Disassembly of section .plt:
    <add@plt-0x10>:
      400580:    ff 35 82 0a 20 00        pushq  0x200a82(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
      400586:    ff 25 84 0a 20 00        jmpq   *0x200a84(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
      40058c:    0f 1f 40 00              nopl   0x0(%rax)
    <add@plt>:
      400590:    ff 25 82 0a 20 00        jmpq   *0x200a82(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> // ++++++++++++++++++++++
      400596:    68 00 00 00 00           pushq  $0x0
      40059b:    e9 e0 ff ff ff           jmpq   400580 <_init+0x20>
    
    00000000004005a0 <__libc_start_main@plt>:
      4005a0:    ff 25 7a 0a 20 00        jmpq   *0x200a7a(%rip)        # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
      4005a6:    68 01 00 00 00           pushq  $0x1
      4005ab:    e9 d0 ff ff ff           jmpq   400580 <_init+0x20>
    
    
    00000000004006b0 <main>:
      4006b0:    55                       push   %rbp
      4006b1:    48 89 e5                 mov    %rsp,%rbp
      4006b4:    48 83 ec 10              sub    $0x10,%rsp
      4006b8:    c7 45 fc 7b 00 00 00     movl   $0x7b,-0x4(%rbp)
      4006bf:    8b 45 fc                 mov    -0x4(%rbp),%eax                
      4006c2:    89 c7                    mov    %eax,%edi                                      
      4006c4:    e8 c7 fe ff ff           callq  400590 <add@plt>           //call add--------------------------------
      4006c9:    b8 00 00 00 00           mov    $0x0,%eax
      4006ce:    c9                       leaveq 
      4006cf:    c3                       retq   
    
    
    Disassembly of section .got:
    
    0000000000600ff8 <.got>:
        ...
    
    Disassembly of section .got.plt:
    #data段  无效反编译代码
    <_GLOBAL_OFFSET_TABLE_>:
      601000:   GOT[0]   GOT    64位下每个条目8个字节   32位是4个字节
      601008:   GOT[1] 链接器标示信息
     GOT[2]  动态连接器入口地址
      601017:    00 96 05 40 00 00        add    %dl,0x4005(%rsi)
      60101d:    00 00                    add    %al,(%rax)
      60101f:    00 a6 05 40 00 00        add    %ah,0x4005(%rsi)
      601025:    00 00                    add    %al,(%rax)
      601027:    00 b6 05 40 00 00        add    %dh,0x4005(%rsi)
      60102d:    00 00                    add    %al,(%rax)
        ...

    我们可以看到main函数中跳转call add 跳转到了地址0x400590 处,执行 jmpq *0x200a82(%rip) 指令 跳转到 0x601018的地址 ,还是跳到GOT数组中的add位置。这个位置其实就是 jmpq *0x200a82(%rip)的下一条指令地址400596: 68 0000 00 00 pushq $0x0。我们可以使用gdb看一下。

    (gdb) disassemble main
    Dump of assembler code for function main:
       0x00000000004006b0 <+0>:   push   %rbp
       0x00000000004006b1 <+1>:   mov    %rsp,%rbp
       0x00000000004006b4 <+4>:   sub    $0x10,%rsp
    => 0x00000000004006b8 <+8>:    movl   $0x7b,-0x4(%rbp)
       0x00000000004006bf <+15>:  mov    -0x4(%rbp),%eax
       0x00000000004006c2 <+18>:  mov    %eax,%edi
       0x00000000004006c4 <+20>:  callq  0x400590 <add@plt>
       0x00000000004006c9 <+25>:  mov    $0x0,%eax
       0x00000000004006ce <+30>:  leaveq
       0x00000000004006cf <+31>:  retq  
    End of assembler dump.
    (gdb) disassemble 0x400590
    Dump of assembler code for function add@plt:
       0x0000000000400590 <+0>:   jmpq   *0x200a82(%rip)        # 0x601018 <add@got.plt> 这个地址就是GOT数组中add对应的那条
       0x0000000000400596 <+6>:   pushq  $0x0
       0x000000000040059b <+11>:  jmpq   0x400580
    End of assembler dump.
    (gdb) x 0x601018
    0x601018 <add@got.plt>:   0x00400596

    pushq $0x0 这条指令将add符号的ID压入栈,然后 jmpq  0x400580 它将会跳转到PLT[0]接下来的指令就是

    0000000000400580 <add@plt-0x10>:
      400580:    ff 35 82 0a 20 00        pushq  0x200a82(%rip)        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>   链接器标示信息入栈  前面还有个ID
      400586:    ff 25 84 0a 20 00        jmpq   *0x200a84(%rip)        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>  跳转到链接器入口地址
      40058c:    0f 1f 40 00              nopl   0x0(%rax)

    跳转到动态连接器之后,链接器根据这两个栈中的信息(其实就是重定位描述符地址和索引值)得到 add的实际地址, 在将地址放入GOT[3]中 通过gdb详细看一下。

    (gdb) n
           add(c);  运行  add之前
    (gdb) x /32x 0x601010
    0x601010:    0xf7df0210    0x00007fff    0x00400596    0x00000000  
    这时GOT[3]add地址还不是add的实际地址 而是0000000000400590<add@plt>: jmp的目的地址0x00400596
    0x601020 <__libc_start_main@got.plt>:    0xf7839a20    0x00007fff    0x004005b6    0x00000000
    0x601030:    0x00000000    0x000003e8    0x00000000    0x00000000
    0x601040:    0x00000000    0x00000000    0x00000000    0x00000000
    0x601050:    0x00000000    0x00000000    0x00000000    0x00000000
    0x601060:    0x00000000    0x00000000    0x00000000    0x00000000
    0x601070:    0x00000000    0x00000000    0x00000000    0x00000000
    0x601080:    0x00000000    0x00000000    0x00000000    0x00000000
    (gdb) print add
    $2 = {<text variable, no debug info>} 0x7ffff7bd96a8 <add>   add函数的地址
    (gdb) n
           return 0;
    (gdb) x /32x 0x601010
    0x601010:    0xf7df0210    0x00007fff    0xf7bd96a8    0x00007fff   
    进入add GOT[3]中地址变为了 add的实际地址 再之后的
    <add@plt>: jmpq *0x200a82(%rip) 就会直接跳到add的地址 开始执行指令。
    
    
    0x601020 <__libc_start_main@got.plt>:    0xf7839a20    0x00007fff    0x004005b6    0x00000000
    0x601030:    0x00000000    0x00000463    0x00000000    0x00000000
    0x601040:    0x00000000    0x00000000    0x00000000    0x00000000
    0x601050:    0x00000000    0x00000000    0x00000000    0x00000000
    0x601060:    0x00000000    0x00000000    0x00000000    0x00000000
    0x601070:    0x00000000    0x00000000    0x00000000    0x00000000
    0x601080:    0x00000000    0x00000000    0x00000000    0x00000000

    这里可以看到被引用的函数调用之前 GOT中的地址并没有被值为函数的实际地址。之后实际地址就被装入,跳转到相应地址开始执行,之后就可以直接跳转运行了,只需要一个跳转指令即可。

    展开全文
  • 之前写了一个API的开发程序 windows跑过了 移到linux上死活找不到.so动态库 反复检查了好多次makefile 查了半天发现是API提供的动态库没有lib前缀 及其烦躁 - -

    之前写了一个API的开发程序

    windows跑过了 移到linux上死活找不到.so动态库

    反复检查了好多次makefile

    查了半天发现是API提供的动态库没有lib前缀

    及其烦躁 - -

    展开全文
  • 1. 编写Shared Object(共享对象,或动态库)文件 #include myLibFunction(){  printf("myLibFunction called.\n") } 保存为so.so 2. 编译动态库文件 gcc so.c -fPIC -shared -o libso.so 3. ...

    1. 编写Shared Object(共享对象,或动态库)文件

    #include <stdio.h>

    myLibFunction(){ 

    printf("myLibFunction called.\n")

    }

    保存为so.so


    2. 编译动态库文件

    gcc so.c -fPIC -shared -o libso.so


    3. 编写测试文件

    int main(){

    myLibFunction();

    return 0;

    }

    保存为test.c


    4. 编译测试文件

    gcc test.c -L. -lso -o test


    5. 将动态库地址添加到/etc/ld.so.conf文件中



    参考:http://www.linuxidc.com/Linux/2011-10/45744.htm

    展开全文
  • 在./执行程序时会提示找不到库, 此时需要在里面手动敲打一个export命令例如:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/mqtt/MQTT-c/lib 红色字体是你需要替换的动态库绝对目录 ...

    在./执行程序时会提示找不到库,

    此时需要在里面手动敲打一个export命令例如:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/mqtt/MQTT-c/lib

    红色字体是你需要替换的动态库绝对目录

    展开全文
  • Linux动态库剖析 https://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/,非常好的一篇文章 还有 Peter Seebach 所著的文章 “剖析共享库”(developerWorks,2005 年 1 月),了解共享库,...
  • linux qt 动态链接库

    2011-08-15 15:23:18
    linux qt 动态链接库linux qt 动态链接库linux qt 动态链接库linux qt 动态链接库linux qt 动态链接库linux qt 动态链接库
  • linux动态链接库

    2018-08-22 18:01:46
    动态链接库与普通的程序相比而言,没有main函数,是一系列函数的实现。通过shared和fPIC编译参数生产so动态链接库文件。程序在调用库函数时,只需要连接上这个即可。例如下面实现一个简单的整数四则运输的动态链接...
  • linux动态链接库示例

    2011-09-18 13:58:06
    linux动态链接库的制作,linux下制作动态链接库 C语言描述
  • Linux动态链接库

    2015-04-28 16:44:12
    原文地址:Linux动态链接库作者:liliang Linux动态链接库编程入门 动态链接库是一种通用的软件组件技术,是多种操作系统中提供基本服务的方式。比如Win32内核就是3个DLL文件构成。这种技术在Linux...
  • 主要介绍了linux动态链接库使用方法,大家参考使用吧
  • Linux 动态链接库

    2015-12-11 20:43:59
    Linux动态链接库的使用 1、前言  在实际开发过程中,各个模块之间会涉及到一些通用的功能,比如读写文件,查找、排序。为了减少代码的冗余,提高代码的质量,可以将这些通用的部分提取出来,做出公共的...
  • 3、两种系统动态库比较分析Windows和Linux采用动态链接库技术目的是基本一致的,但由于操作系统的不同,他们在许多方面还是不尽相同,下面从以下几个方面进行阐述。(1)动态库程序编写,在Windows系统下的执行文件...
  • 最近又重新复习了一下Linux动态链接库和静态链接库,稍作笔记以备后查。 1. 静态链接库动态链接库  静态链接库就是一个多个汇编文件(obj文件)的集合,在Linux中通常命名为libxxx.a。对于静态链接库函数的连接...
  • http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/ ...下面通过一个简单的例子开始介绍Linux标准对象。 我们的标准对象文件含有一个函数,不需要声明export导出符号,只需要编译器设置即可。如
  • Linux动态链接库

    2017-03-12 16:50:11
    linux动态库的命名规则 动态链接库的名字形式为 libxxx.so,前缀是lib,后缀名为“.so”。 l 针对于实际库文件,每个共享库都有个特殊的名字“soname”。在程序启动后,程序通过这个名字来告诉动态加载器该...
  • LINUX动态链接库的使用-dlopen dlsym dlclose dlerror
  • Linux动态链接库的使用
  • linux动态链接库的加载顺序

    千次阅读 2019-04-19 18:05:37
    一、Linux 动态库选择顺序指: 1. 编译程序时用到动态库,该从那些地方查找,按照怎么样的顺序查找? 2. 运行程序时需要动态库,该从那些地方查找,按照怎么样的顺序查找? 二、gcc 编译程序时查找SO顺序如下: ...
  • linux静态链接库与动态链接库的区别及动态库的创建(转) linux静态链接库与动态链接库的区别及动态库的创建(转) - 周飞 - 博客园linux静态链接库与动态链接库的区别及动态库的创建(转)一、引言 ...
  • 介绍了LINUX动态链接库的基本知识.其要点是:用户根据实际情况需要,利用dlopen,dlsym,dlclose等动态链接库操作函 数,装入指定的动态链接库中指定的函数,然后加以执行.
  • linux 动态链接库问题

    2018-05-31 23:10:38
    生成动态链接库时可以用多个.c文件生成一个动态链接库,主程序调用时,要分别include进头文件。
  • linux动态链接库路径配置总结

    千次阅读 2018-04-02 16:42:43
    在linux上配置caffe上经常会遇到各种错误,其中就包括ld error,即链接库无法找到之类的错误,因此在这里总结一下linux动态库搜索路径的配置。 动态链接库 动态链接库是程序运行时需要加载的库,类似windows里的...
  • linux 动态库 .so 注意命名必须为libXXX.so,不能随便起名字 linux静态库 .a 注意命名必须为libXXX.a,不能随便起名字 其编译方法为: 静态库:  g++ -c 2.cpp  ar -cr libmy2.a 2.o  g++ -o main 1....
  • -动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助。...
  • Linux制作动态链接库

    2016-07-27 20:03:37
    1.什么是动态链接库动态链接库是程序运行时加载的库,当动态链接库正确安装后,所有的程序都可以使用动态库来运行程序。动态链接库是目标文件的集合,目标文件在动态链接库中的组织方式是按照特殊方式形成的。库中...
  • linux动态链接库入门

    2014-03-05 13:58:10
    2在linux动态链接库一般使用.so作为扩展名,生成动态链接库的方法 gcc -shared -Wall -fPIC add.o -o libadd.so 3生成main.o gcc -c main.c 4 指定动态链接库路径,生成可执行文件gcc -o main main.o -ladd -L ...
  • Linux 动态库剖析 进程与 API 动态链接的共享库是 GNU/Linux® 的一个重要方面。该种库允许可执行文件在运行时动态访问外部函数,从而(通过在需要时才会引入函数的方式)减少它们对内存的总体占用。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,649
精华内容 3,059
关键字:

linux动态库链接

linux 订阅