精华内容
下载资源
问答
  • Error in inDL(x, as.logical(local), as.logical(now), …) : 无法载入共享目标对象‘D:/Program Files (x86)/R/R-4.0.3/library/RGtk2/libs/x64/RGtk2.dll’:: 求大神帮助 这是在加载RGtk2 包时遇到的问题:具体...

    Error in inDL(x, as.logical(local), as.logical(now), …) : 无法载入共享目标对象‘D:/Program Files (x86)/R/R-4.0.3/library/RGtk2/libs/x64/RGtk2.dll’:: 求大神帮助

    这是在加载RGtk2 包时遇到的问题:具体报错如下
    Error in inDL(x, as.logical(local), as.logical(now), …) :
    无法载入共享目标对象‘D:/Program Files (x86)/R/R-4.0.3/library/RGtk2/libs/x64/RGtk2.dll’::
    LoadLibrary failure: 找不到指定的模块。

    Learn more about GTK+ at http://www.gtk.org
    If the package still does not load, please ensure that GTK+ is installed and that it is on your PATH environment variable
    IN ANY CASE, RESTART R BEFORE TRYING TO LOAD THE PACKAGE AGAIN
    Error in inDL(x, as.logical(local), as.logical(now), …) :
    无法载入共享目标对象‘D:/Program Files (x86)/R/R-4.0.3/library/RGtk2/libs/x64/RGtk2.dll’::
    LoadLibrary failure: 找不到指定的模块。

    此外: Warning message:
    Failed to load RGtk2 dynamic library, attempting to install it.
    试开URL’http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/gtk±bundle_2.22.1-20101229_win64.zip’
    错误: package or namespace load failed for ‘RGtk2’:
    loadNamespace()里算’RGtk2’时.onLoad失败了,详细内容:
    调用: download.file(dep_url, path, mode = “wb”)
    错误: 无法打开URL’http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/gtk±bundle_2.22.1-20101229_win64.zip’
    此外: Warning messages:
    1: Failed to load RGtk2 dynamic library, attempting to install it.
    2: In download.file(dep_url, path, mode = “wb”) :
    InternetOpenUrl失败:’不能连接到吊销服务器,或者未能获得最终响应。’
    毫无疑问,rattle包也随之出现问题

    载入需要的程辑包:tibble
    载入需要的程辑包:bitops
    Rattle: A free graphical interface for data science with R.
    XXXX 5.4.0 Copyright © 2006-2020 Togaware Pty Ltd.
    键入’rattle()'去轻摇、晃动、翻滚你的数据。

    rattle()
    Loading required package: RGtk2
    Error in inDL(x, as.logical(local), as.logical(now), …) :
    无法载入共享目标对象‘D:/Program Files (x86)/R/R-4.0.3/library/RGtk2/libs/x64/RGtk2.dll’::
    LoadLibrary failure: 找不到指定的模块。

    试开URL’http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/gtk±bundle_2.22.1-20101229_win64.zip’
    Error: package or namespace load failed for ‘RGtk2’:
    .onLoad failed in loadNamespace() for ‘RGtk2’, details:
    call: download.file(dep_url, path, mode = “wb”)
    error: 无法打开URL’http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/gtk±bundle_2.22.1-20101229_win64.zip’
    Error in inDL(x, as.logical(local), as.logical(now), …) :
    无法载入共享目标对象‘D:/Program Files (x86)/R/R-4.0.3/library/RGtk2/libs/x64/RGtk2.dll’::
    LoadLibrary failure: 找不到指定的模块。

    此外: Warning messages:
    1: Failed to load RGtk2 dynamic library, attempting to install it.
    2: In download.file(dep_url, path, mode = “wb”) :
    InternetOpenUrl失败:’操作超时’
    试开URL’http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/gtk±bundle_2.22.1-20101229_win64.zip’
    错误: .onLoad failed in loadNamespace() for ‘RGtk2’, details:
    call: download.file(dep_url, path, mode = “wb”)
    error: 无法打开URL’http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/gtk±bundle_2.22.1-20101229_win64.zip’
    此外: Warning messages:
    1: Failed to load RGtk2 dynamic library, attempting to install it.
    2: In download.file(dep_url, path, mode = “wb”) :
    InternetOpenUrl失败:’不能连接到吊销服务器,或者未能获得最终响应。

    上网查资料有类似内容,说是36位的R版本问题。
    但是,我在安装R时没有安装36位的R!!!
    求哪位大神帮忙解决问题。

    展开全文
  • 无法载入共享目标对象‘C:/Users/yunli/Documents/R/win-library/4.0/Rcpp/libs/x64/Rcpp.dll’:: LoadLibrary failure: 找不到指定的程序。 在运行library(oligo)时出现以上问题。 请大神指教。
  •  error: 无法载入共享目标对象‘D:/Program Files/R/R-3.2.2/library/rJava/libs/x64/rJava.dll’::  LoadLibrary failure: %1 不是有效的 Win32 应用程序。 Error : package 'rJava' could not be ...

    1、问题

    在安装RWordReg包时出现如下错误:

    > utils:::menuInstallPkgs()
    installing the source package ‘Rwordseg’

    试开URL’http://R-Forge.R-project.org/src/contrib/Rwordseg_0.2-1.tar.gz'
    Content type 'application/x-gzip' length 5445754 bytes (5.2 MB)
    downloaded 5.2 MB

    * installing *source* package 'Rwordseg' ...
    ** R
    ** demo
    ** inst
    ** preparing package for lazy loading
    Error : .onLoad failed in loadNamespace() for 'rJava', details:
      call: inDL(x, as.logical(local), as.logical(now), ...)
      error: 无法载入共享目标对象‘D:/Program Files/R/R-3.2.2/library/rJava/libs/x64/rJava.dll’::
      LoadLibrary failure:  %1 不是有效的 Win32 应用程序。

    Error : package 'rJava' could not be loaded
    ERROR: lazy loading failed for package 'Rwordseg'
    * removing 'D:/Program Files/R/R-3.2.2/library/Rwordseg'

    下载的程序包在
            ‘C:\Users\NR\AppData\Local\Temp\RtmpCGvWIP\downloaded_packages’里
    Warning messages:
    1: 运行命令'"D:/PROGRA~1/R/R-32~1.2/bin/x64/R" CMD INSTALL -l "D:\Program Files\R\R-3.2.2\library" C:\Users\NR\AppData\Local\Temp\RtmpCGvWIP/downloaded_packages/Rwordseg_0.2-1.tar.gz'的状态是1
    2: In install.packages(NULL, .libPaths()[1L], dependencies = NA, type = type) :
      安装程序包‘Rwordseg’时退出狀態的值不是0


    2、解决

    在R语言环境中设置JRE路径:

    > Sys.setenv(JAVA_HOME='D:/jdk1.6.0_45/jre')

    注意是JRE路径,而不是JDK的路径。


    展开全文
  • 1、首先排查对应的.so文件是否... 2、验证.so文件是否可以执行(ldd可以列出一个程序所需要得动态链接库),正常的话会列出链接库,否则会异常 ldd lib.test.so 笔者原因是.so文件无法执行,重新生成下对应系统版本的.so

    1、首先排查对应的.so文件是否存在;
    2、验证.so文件是否可以执行(ldd可以列出一个程序所需要得动态链接库),正常的话会列出链接库,否则会异常
    ldd lib.test.so

    笔者原因是.so文件无法执行,重新生成下对应系统版本的.so

    展开全文
  • 共享载入时重定位

    千次阅读 2016-04-01 11:38:43
    共享载入时重定位 原作者:Eli Bendersky http://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries 本文的目的是解释现代操作系统如何使得共享载入时重定位成为可能。它关注运行...

    共享库载入时重定位

    原作者:Eli Bendersky

    http://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries

    本文的目的是解释现代操作系统如何使得共享库载入时重定位成为可能。它关注运行在32位x86的LinuxOS,但通用的原则也适用于其他OS与CPU。

    共享库有许多名字——共享库,共享对象,动态共享对象(DSO),动态链接库(DLL——如果你有Windows背景)。为了统一起见,我将尽量在本文里使用“共享库”这个名字。

    载入可执行文件

    Linux,类似于其他支持虚拟内存的OS,将可执行文件载入固定地址。如果我们随机检查某些可执行文件的ELF头,我们将看到一个入口点地址:

    $ readelf -h /usr/bin/uptime

    ELF Header:

      Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 0000

      Class:                             ELF32

      [...] some header fields

      Entry pointaddress:               0x8048470

      [...] some header fields

    这是由链接器放置来告诉OS在哪里开始执行该可执行文件的代码[1]。而如果我们使用GDB载入该可执行文件并检查地址0x804870,我们确实将看到该可执行文件.text节的第一条指令。

    这意味着在链接可执行文件时,链接器可以将所有内部符号引用(函数及数据)完全解析到固定及最终的位置。链接器自己执行一些重定位[2],最终产生的输出不包含任何重定位。

    真的吗?注意到在前一段我强调内部。只要该可执行文件不需要共享库[3],它不需要重定位。但如果它确实使用了共享库(就像绝大多数Linux应用程序),归结于共享库被载入的方式,需要重定位从这些共享库获取的符号。

    载入共享库

    不像可执行文件,在构建共享库时,链接器不能对它们的代码假设一个已知的载入地址。这样的原因很简单。每个程序可以使用任意多的共享库,没有一个简单的方法预先知道给定的共享库将被载入虚拟内存的什么位置。多年来,对这个问题发明了很多方法,但在本文里我只关注当前Linux使用的方法。

    不过首先让我们简要地检查这个问题。这里是一个C例子代码[4],我将它编译为一个共享库:

    int myglob = 42;

     

    intml_func(int a, int b)

    {

        myglob += a;

        return b + myglob;

    }

    注意ml_func如何几次访问myglob。

    在编译为x86汇编时,这将涉及一条mov指令将myglob的值从内存位置载入寄存器。Mov要求绝对地址——这样链接器如何知道它放在哪个地址?答案是——它不知道。正如我之前提到的,共享库没有预定义的载入地址——这将在运行时确定。

    在Linux里,动态载入器[5]是一段为准备运行的程序做准备的代码。它的其中一个任务是在运行程序要求时,将共享库从硬盘载入内存。在一个共享库被载入内存后,根据新确定的载入地址调整它。解决前一段提到的问题是动态载入器的工作。

    在Linux ELF共享库里,解决这个问题有两个主要的途径:

    1.      载入时重定位

    2.      位置无关代码(PIC)

    尽管PIC更通用且是现在推荐方案,在本文我将关注载入时重定位。最后我计划涵盖这两个方法,写一篇单独关于PIC的文章,我觉得以载入时重定位开始会更容易解释PIC。

    载入时重定位链接的共享库

    要创建载入时重定位的共享库,我将不使用-fPIC标记进行编译(否则将触发生成PIC):

    gcc -g -c ml_main.c -o ml_mainreloc.o

    gcc -shared -o libmlreloc.so ml_mainreloc.o

    看到第一个有趣的事是libmlreloc.so的入口:

    $ readelf -h libmlreloc.so

    ELF Header:

      Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 0000

      Class:                             ELF32

      [...] some header fields

      Entry pointaddress:               0x3b0

      [...] some header fields

    为了简单起见,链接器知道载入器会到处移动这个共享对象,因此只是从地址0x0链接它(.text节在0x3b0处开始)。记住这个事实——在本文后面这是有用的。

    现在让我们看一下这个共享库的汇编,关注ml_func:

    $ objdump -d -Mintel libmlreloc.so

     

    libmlreloc.so:     fileformat elf32-i386

     

    [...] skipping stuff

     

    0000046c <ml_func>:

     46c: 55                      push   ebp

     46d: 89 e5                   mov    ebp,esp

     46f: a1 00 00 00 00          mov   eax,ds:0x0

     474: 03 45 08                add    eax,DWORD PTR [ebp+0x8]

     477: a3 00 00 00 00          mov   ds:0x0,eax

     47c: a1 00 00 00 00          mov   eax,ds:0x0

     481: 03 45 0c                add    eax,DWORD PTR [ebp+0xc]

     484: 5d                      pop    ebp

     485: c3                      ret

     

    [...] skipping stuff

    在作为prologue部分的头两条指令后[6],我们看到myglob+= a的编译后结果[7]。从内存将myglob的值提取到eax,加上a(它在ebp+0x8),然后放回内存。

    但等一下,mov获取了myglob?为什么?看起来mov实际的操作数只是0x0[8]。出了什么事?链接器将一些临时的预定义值(这里是0x0)放入指令流,然后创建一个特殊的重定位项指向这个位置。让我们检查一下这个共享库的重定位项:

    $ readelf -r libmlreloc.so

     

    Relocation section '.rel.dyn' at offset 0x2fc contains 7entries:

     Offset     Info   Type            Sym.Value  Sym. Name

    00002008  00000008R_386_RELATIVE

    00000470  00000401R_386_32          0000200C   myglob

    00000478  00000401R_386_32          0000200C   myglob

    0000047d  00000401R_386_32          0000200C   myglob

    [...] skipping stuff

    ELF的rel.dyn节保留给动态(载入时)重定位,由动态载入器使用。在上面显示的节里myglob有3个重定位项,因为在反汇编代码里对myglob有3个引用。让我们解释第一个。

    它说:去到这个目标文件(共享库)偏移0x470处,对符号myglob应用R_386_32类型的重定位。如果我们查询ELF规范,看到R_386_32类型重定位表示:在重定位项中获取指定偏移的值,加上符号的地址,并把它置入偏移。

    在该目标文件偏移0x470处我们有什么?回忆ml_func反汇编代码的指令:

    46f:  a1 00 00 00 00          mov   eax,ds:0x0

     

    a1编码了指令mov,因此它的操作数在下一个地址,即0x470。这是在反汇编代码里我们看到的0x0。因此回到重定位项,现在我们明白它说:将myglob的地址加上mov指令的操作数。换句话说它告诉动态载入器——一旦你执行实际的地址分配,将myglob的真实地址放入0x470,然后将正确的符号值替换mov的操作数。简洁,对吧?

    注意重定位节的“Sym.value”列,对myglob它包含0x200C。这是myglob在这个共享库的虚拟内存映像里的偏移(还记得吗,链接器假定这个共享库在0x0处载入)。也可以通过查看这个库的符号表来检查这个值,比如使用nm:

    $ nm libmlreloc.so

    [...] skipping stuff

    0000200c D myglob

    这个输出也提供了myglob在这个库里的偏移。D表示该符号在初始化数据节(.data)。

    运行中的载入时重定位

    要看运行中的载入时重定位,我将使用来自一个简单启动可执行文件的共享库。在运行这个可执行文件时,OS将载入该共享库并正确地重定位它。

    有趣的是,因为Linux中启用的地址空间布局的随机化,跟随重定位相对困难,因为每次运行这个可执行文件,共享库libmreloc.so被放在不同的虚拟内存地址[9]

    不过这是一个相当弱的限制。这一切有一个方法让它变得合理。但首先,让我们讨论我们共享库包含的段:

    $ readelf --segments libmlreloc.so

     

    Elf file type is DYN (Shared object file)

    Entry point 0x3b0

    There are 6 program headers, starting at offset 52

     

    Program Headers:

      Type           Offset   VirtAddr  PhysAddr   FileSiz MemSiz  Flg Align

      LOAD           0x000000 0x00000000 0x000000000x004e8 0x004e8 R E 0x1000

      LOAD           0x000f04 0x00001f04 0x00001f040x0010c 0x00114 RW  0x1000

      DYNAMIC        0x000f18 0x00001f18 0x00001f18 0x000d00x000d0 RW  0x4

      NOTE           0x0000f4 0x000000f4 0x000000f40x00024 0x00024 R   0x4

      GNU_STACK      0x000000 0x00000000 0x00000000 0x000000x00000 RW  0x4

      GNU_RELRO      0x000f04 0x00001f04 0x00001f04 0x000fc0x000fc R   0x1

     

     Section to Segmentmapping:

      Segment Sections...

       00     .note.gnu.build-id .hash .gnu.hash .dynsym.dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini.eh_frame

       01     .ctors .dtors .jcr .dynamic .got .got.plt.data .bss

       02     .dynamic

       03     .note.gnu.build-id

       04

       05     .ctors .dtors .jcr .dynamic .got

    要追踪符号myglob,我们感兴趣的是这里列出的第二个段。注意这些东西:

    ·        在底部的节到段的映射里,段01声称包含.data节,它是myglob的大本营

    ·        VirAddr列指明第二个段在0x1f04开始且大小为0x10c,表示它一直扩展到0x2010,因此包含0x200C处的myglob。

    现在使用Linux提供给我们的一个好用的工具来检查载入时链接过程——dl_iterate_phdr方法,它允许应用程序在运行时查询载入了那些共享库,而且更重要的是——窥探它们的程序头。

    因此我准备将下面的代码写入driver.c:

    #define _GNU_SOURCE

    #include <link.h>

    #include <stdlib.h>

    #include <stdio.h>

     

     

    staticintheader_handler(struct dl_phdr_info* info, size_t size, void* data)

    {

        printf("name=%s (%d segments) address=%p\n",

               info->dlpi_name, info->dlpi_phnum, (void*)info->dlpi_addr);

        for (int j = 0; j <info->dlpi_phnum; j++) {

             printf("\t\t header %2d: address=%10p\n", j,

                 (void*) (info->dlpi_addr+ info->dlpi_phdr[j].p_vaddr));

             printf("\t\t\t type=%u, flags=0x%X\n",

                    info->dlpi_phdr[j].p_type, info->dlpi_phdr[j].p_flags);

        }

        printf("\n");

        return0;

    }

     

     

    externint ml_func(int, int);

     

     

    intmain(int argc, constchar* argv[])

    {

       dl_iterate_phdr(header_handler, NULL);

     

        int t = ml_func(argc,argc);

        return t;

    }

    header_handler实现了dl_iterate_phdr的回调。对所有的库它都将得到调用,并报告它们的名字及载入地址,连同它们所有的段。它还会调用ml_func,这个方法来自libmlreloc.so共享库。

    要以我们的共享库编译并链接driver,运行:

    gcc -g -c driver.c -o driver.o

    gcc -o driver driver.o -L. -lmlreloc

    单独运行driver我们会得到信息,但每次运行的地址都是不同的。因此我要做的就是在gdb下运行它[10],看它说了什么,然后使用gdb进一步查询进程的地址空间:

    $ gdb -q driver

     Reading symbols fromdriver...done.

     (gdb) b driver.c:31

     Breakpoint 1 at0x804869e: file driver.c, line 31.

     (gdb) r

     Starting program: driver

     [...] skipping output

     name=./libmlreloc.so (6segments) address=0x12e000

                   header  0: address=  0x12e000

                           type=1, flags=0x5

                    header  1: address= 0x12ff04

                           type=1, flags=0x6

                   header  2: address=  0x12ff18

                           type=2, flags=0x6

                   header  3: address=  0x12e0f4

                           type=4, flags=0x4

                   header  4: address=  0x12e000

                           type=1685382481, flags=0x6

                   header  5: address=  0x12ff04

                           type=1685382482, flags=0x4

     

    [...] skipping output

     Breakpoint 1, main(argc=1, argv=0xbffff3d4) at driver.c:31

     31    }

     (gdb)

    因为driver报告它载入的所有库(甚至隐含的库,像libc或动态载入器自身),输出很长,我将只关注libmlreloc.so的报告。注意这6个段与readelf报告的相同,但这次重定位到了它们最终的内存位置。

    让我们做一点算术。输出说libmlreloc.so放在了虚拟地址0x12e000。我们对第二个段感兴趣,正如我们在readelf中看到的,它在偏移0x1f04。确实,在输出中我们看到它被载入到地址0x12ff04。因为myglob在文件的偏移为0x200C,我们期望它现在在地址0x13000C。

    好,让我们问问GDB:

     (gdb) p &myglob

    $1 = (int *) 0x13000c

    棒极了!不过访问myglob的dmml_func又怎么样了呢?再问问GDB:

     (gdb) setdisassembly-flavor intel

    (gdb) disas ml_func

    Dump of assembler code for function ml_func:

       0x0012e46c<+0>:   push   ebp

       0x0012e46d<+1>:   mov    ebp,esp

       0x0012e46f<+3>:   mov    eax,ds:0x13000c

       0x0012e474<+8>:   add    eax,DWORD PTR [ebp+0x8]

       0x0012e477<+11>:  mov    ds:0x13000c,eax

       0x0012e47c<+16>:  mov    eax,ds:0x13000c

       0x0012e481<+21>:  add    eax,DWORD PTR [ebp+0xc]

       0x0012e484<+24>:  pop    ebp

       0x0012e485<+25>:  ret

    End of assembler dump.

    正如期望的,myglob的真实地址放入了所有访问它的mov指令里,就像重定位项指出的那样。

    重定位函数调用

    到目前为止本文展示了数据访问的重定位——以全局变量myglob的使用为例。另一个需要重定位的是代码访问——也就是函数调用。本节简要介绍这怎么做到。节奏要比本文的其他部分要快得多,因为我现在假定读者已经明白了重定位是什么。

    言归正传,让我们开始吧。我已经将共享库的代码修改如下:

    int myglob = 42;

     

    intml_util_func(int a)

    {

        return a + 1;

    }

     

    intml_func(int a, int b)

    {

        int c = b +ml_util_func(a);

        myglob += c;

        return b + myglob;

    }

    添加了由ml_func使用的ml_util_func。下面是完成链接的共享库里ml_func的反汇编代码:

    000004a7 <ml_func>:

     4a7:   55                      push   ebp

     4a8:   89 e5                   mov    ebp,esp

     4aa:   83 ec 14                sub    esp,0x14

     4ad:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]

     4b0:   89 04 24                mov    DWORD PTR [esp],eax

     4b3:   e8 fc ff ff ff          call  4b4 <ml_func+0xd>

     4b8:   03 45 0c                add    eax,DWORD PTR [ebp+0xc]

     4bb:   89 45 fc                mov    DWORD PTR [ebp-0x4],eax

     4be:   a1 00 00 00 00          mov   eax,ds:0x0

     4c3:   03 45 fc                add    eax,DWORD PTR [ebp-0x4]

     4c6:   a3 00 00 00 00          mov   ds:0x0,eax

     4cb:   a1 00 00 00 00          mov   eax,ds:0x0

     4d0:   03 45 0c                add    eax,DWORD PTR [ebp+0xc]

     4d3:   c9                      leave

     4d4:   c3                      ret

    这里有趣的是地址0x4b3处的指令——它是对ml_util_func的调用。让我们分解它:

    e8是call的操作码。这个call的参数是相对于下一条指令的偏移。在上面的反汇编代码里,这个参数是0xfffffffc,或-4。因此call当前指向自己。这显然是不对的——但不要忘记重定位。下面是共享库重定位节现在的样子:

    $ readelf -r libmlreloc.so

     

    Relocation section '.rel.dyn' at offset 0x324 contains 8entries:

     Offset     Info   Type            Sym.Value  Sym. Name

    00002008  00000008R_386_RELATIVE

    000004b4  00000502 R_386_PC32        0000049c   ml_util_func

    000004bf  00000401R_386_32          0000200c   myglob

    000004c7  00000401R_386_32          0000200c   myglob

    000004cc  00000401R_386_32          0000200c   myglob

    [...] skipping stuff

    如果将它与前面readelf –r调用比较,我们会注意到为ml_util_func添加了一个新的项。这个项指向call指令参数的地址0x4b4,并且它的类型是R_386_PC32。与R_386_32相比,这个重定位类型更复杂,但不是复杂得太多。

    它表示:获取项中指定偏移处的值,加上符号的地址,减去偏移地址本身,把它放回偏移处的内存字。记住这个重定位是在载入时完成的,那时符号及被重定位偏移本身的最后载入地址都是已知的。这些最终地址参与这个计算。

    这由什么作用?基本上,它是相对重定位,考虑了它的位置,因此适用于相对寻址的指令参数(e8call就是)。我保证一旦我们得到真实的数字,这会变得更清楚。

    我现在准备再次编译driver代码并在GDB下运行它,看这个重定位如何工作。下面是GDB节,跟着解释:

    $ gdb -q driver

     Reading symbols fromdriver...done.

     (gdb) b driver.c:31

     Breakpoint 1 at0x804869e: file driver.c, line 31.

     (gdb) r

     Starting program: driver

     [...] skipping output

     name=./libmlreloc.so (6segments) address=0x12e000

                   header  0: address= 0x12e000

                          type=1, flags=0x5

                   header  1: address= 0x12ff04

                          type=1, flags=0x6

                   header  2: address= 0x12ff18

                          type=2, flags=0x6

                   header  3: address= 0x12e0f4

                          type=4, flags=0x4

                   header  4: address= 0x12e000

                          type=1685382481, flags=0x6

                   header  5: address= 0x12ff04

                          type=1685382482, flags=0x4

     

    [...] skipping output

    Breakpoint 1, main (argc=1, argv=0xbffff3d4) at driver.c:31

    31    }

    (gdb)  setdisassembly-flavor intel

    (gdb) disas ml_util_func

    Dump of assembler code for function ml_util_func:

       0x0012e49c<+0>:   push   ebp

       0x0012e49d<+1>:   mov    ebp,esp

       0x0012e49f<+3>:   mov    eax,DWORD PTR [ebp+0x8]

       0x0012e4a2<+6>:   add    eax,0x1

       0x0012e4a5<+9>:   pop    ebp

       0x0012e4a6<+10>:  ret

    End of assembler dump.

    (gdb) disas /r ml_func

    Dump of assembler code for function ml_func:

       0x0012e4a7<+0>:    55     push  ebp

       0x0012e4a8<+1>:    89 e5  mov   ebp,esp

       0x0012e4aa<+3>:    83 ec 14       sub   esp,0x14

       0x0012e4ad <+6>:    8b 45 08       mov   eax,DWORD PTR [ebp+0x8]

       0x0012e4b0<+9>:    89 04 24       mov   DWORD PTR [esp],eax

       0x0012e4b3<+12>:   e8 e4 ff ff ff call   0x12e49c <ml_util_func>

       0x0012e4b8<+17>:   03 45 0c       add   eax,DWORD PTR [ebp+0xc]

       0x0012e4bb <+20>:   89 45 fc       mov   DWORD PTR [ebp-0x4],eax

       0x0012e4be<+23>:   a1 0c 00 13 00 mov    eax,ds:0x13000c

       0x0012e4c3<+28>:   03 45 fc       add   eax,DWORD PTR [ebp-0x4]

       0x0012e4c6<+31>:   a3 0c 00 13 00 mov    ds:0x13000c,eax

       0x0012e4cb<+36>:   a1 0c 00 13 00 mov    eax,ds:0x13000c

       0x0012e4d0<+41>:   03 45 0c       add   eax,DWORD PTR [ebp+0xc]

       0x0012e4d3<+44>:   c9     leave

       0x0012e4d4<+45>:   c3     ret

    End of assembler dump.

    (gdb)

    这里重要的部分是:

    1.      在driver的输出里我们看到libmlreloc.so第一个段(代码段)被映射到0x12e000[11]

    2.      ml_util_func被载入地址0x0012e49c

    3.      被重定位偏移的地址是0x0012e4b4

    4.      0xfffffffe4被填充到ml_func里对ml_util_func调用的参数里(我以/r选项反汇编ml_func,在反汇编代码之外,显示原始的16进制数),它被解释为到ml_util_func的正确偏移。

    显然我们最感兴趣4是如何完成的。又到了做数学的时间。如上述解释R_386_PC32重定位,我们有:

    获取在项指定偏移处的值(0xfffffffc),加上符号的地址(0x0023e49c),减去偏移本身的地址(0x0012e4b4),把它放回偏移处的内存字。当然,所有这一切都假设使用32位2进制补码完成。结果是0xffffffe4,正如预期。

    额外的学分:为什么需要调用重定位?

    这是一个讨论Linux中共享库载入实现的某些独特性的“奖励”章节。如果你只希望理解重定位如何完成,你完全可以跳过它。

    在尝试理解ml_util_func的重定位时,我必须承认我为此挠头了一阵。回忆call的参数是相对偏移。当然call与ml_util_func之间的偏移在载入库时是不会改变的——它们都在代码段里作为一个整体移动。这样为什么需要这个重定位呢?

    这是一个小的实验尝试:回到共享库的代码,向ml_util_func声明添加static。重新编译看一下readelf–r的输出。

    做完了?我会揭晓结果——重定位不见了!检查ml_func的反汇编代码——现在一个正确的偏移被设置为call的参数——不需要重定位。发生了什么?

    在将全局符号引用绑定到它们实际的定义时,关于查找哪些共享库,动态载入器有某些规则。用户也可以通过设置LD_PRELOAD环境变量来影响这个次序。

    这里要涉及太多细节,因此如果你真正感兴趣你可以看一下ELF标准,动态载入器的man页以及google一下。不过简而言之,当ml_util_func是全局时,它可能在该可执行文件或其他共享库里被覆盖,因此当链接我们的共享库时,链接器不能假设偏移是已知的并且写死它[12]。链接器使得对全局符号的所有访问都是可重定位的,以允许动态载入器决定如何解析它们。这是为什么将函数声明为static会不同——因为它不再是全局或导出的,链接器可以在代码里写死它的偏移。

    额外的学分#2:从可执行文件访问共享库数据

    同样,这是讨论一个进阶议题的“奖励”章节。如果你已经厌倦了,你可以跳过它。

    在上面的例子里,myglob仅在共享库内部使用。如果我们从程序(driver.c)访问它会发生什么?毕竟,myglob是一个全局变量,因此外部可见。

    让我们将driver.c修改如下(注意我删除了段的遍历代码):

    #include <stdio.h>

     

    externint ml_func(int, int);

    externint myglob;

     

    intmain(int argc, constchar* argv[])

    {

        printf("addr myglob = %p\n", (void*)&myglob);

        int t = ml_func(argc,argc);

        return t;

    }

    现在它打印出myglob的地址。输出是:

    addr myglob = 0x804a018

    等一下,有一些东西这里没有计算。难道myglob不是在共享库地址空间里吗?0x804xxxxx看起来像程序地址空间。发生了什么?

    回忆程序/可执行文件是不可重定位的,因此它的数据地址必须在链接时绑定。因此,链接器必须创建在程序地址空间里变量的拷贝,动态载入器将使用它作为重定位地址。这类似于之前章节里的讨论——在某种意义上,在主程序里的myglob覆盖了共享库里的对象,根据全局符号查找规则,它替代了共享库的对象。如果我们在GDB里检查ml_func,我们将看到对myglob的正确访问。

    0x0012e48e <+23>:     a1 18 a0 04 08 mov   eax,ds:0x804a018

    这很合理,因为myglob的R_386_32重定位仍然存在于libmlreloc.so里,动态载入器使它指向myglob现存的正确位置。

    这很不错,但遗漏了一些东西。Myglob是在共享库里初始化(为42)的——这个初始化值如何跑到程序地址空间里的?原来链接器为程序生成了一个特殊的重定位项(目前为止我们仅在共享库里检查重定位项):

    $ readelf -r driver

     

    Relocation section '.rel.dyn' at offset 0x3c0 contains 2entries:

     Offset     Info   Type            Sym.Value  Sym. Name

    08049ff0  00000206R_386_GLOB_DAT    00000000   __gmon_start__

    0804a018  00000605R_386_COPY        0804a018   myglob

    [...] skipping stuff

    注意myglob的R_386_COPY重定位。它只是表示:从符号地址处将值拷贝到这个偏移。这在载入共享库时由动态载入器执行。它怎么知道要拷贝多少呢?符号表节包含了每个符号的大小;例如在libmlreloc.so的.symtab节里myglob的大小是4。

    我觉得这是一个相当酷的例子。显示了可执行文件的链接与载入的过程如何被精心安排。链接器在输出里放入特殊的指令让载入器使用、执行。

    总结

    载入时重定位是Linux(及其他OS)用来解决,在将共享库载入内存时,在共享库里访问内部数据与代码的问题。时至今日,位置无关代码(PIC)是一个更流行的方法,一些现代系统(比如x86-64)已不再支持载入时重定位。

    仍然,出于两个原因我决定写一篇关于载入时重定位的文章。首先,在某些系统上载入时重定位对PIC有几个优势,特别在性能方面。其次,恕我直言,在没有预备知识时载入时重定位更容易理解,这使得将来解释PIC更容易。

    无论动机如何,我希望本文能有助于揭开一点现代OS中链接与载入共享库幕后的神秘面纱。

     



    [1] 关于这个入口点的更多信息,参考这篇文章的 “离题 – 进程地址与入口点”一节。

    [2] 链接时重定位发生在将多个目标文件合并到一个可执行文件(或共享库时)。它涉及解析目标文件间大量的重定位。相比载入时重定位,链接时重定位是一个更复杂的议题,我不会在本文里讨论它。

    [3] 可以通过将你所有的库编译为静态库做到这一点(使用ar合并目标文件,而不是gcc –shared),并在链接可执行文件时向gcc提供-static——避免链接libc的共享库版本。

    [4] ml只是代表“我的库”。类似的,代码本身没有什么意义,仅用作展示的目的。

    [5] 也称为“动态链接器”。它本身是一个共享对象(尽管它也可以作为可执行文件运行),存身于/lib/ld-linux.so.2(最后一个数字是SO版本,可能会不同)。

    [6] 如果你不熟悉x86架构如何组织它的栈帧,是时候看这篇文章了。.

    [7] 你可以向objdump提供-l选项在汇编里添加C源代码行,可以更清楚地知道什么编译成什么。这里我忽略它是为了缩短篇幅。

    [8] 我观察objdump输出的左侧,那里是原始的内存字节。a1 00 00 00 00表示mov将操作数0x0移动到eax,该操作数被反汇编器解释为ds:0x0。

    [9] 因此在这个可执行文件上调用的ldd在每次运行时将报告不同的载入地址。

    [10] 有经验的读者可能注意到我可以i shared询问GDB来得到共享库的载入地址。不过,i shared仅是指整个库的载入地址(或更准确些,它的入口点),而我的兴趣在段。

    [11] 什么,又是0x12e000?我不是刚说过载入地址随机化吗? 原来出于调试目的,可以被操控动态载入器关闭这个特性。这正是GDB的行为。

    [12] 除非传入-Bsymbolic选项。参考ld的man页。

     

    展开全文
  • 错误: 无法载入共享目标对象‘/Library/Frameworks/R.framework/Versions/3.4/Resources/library/rJava/libs/rJava.so’:: dlopen(/Library/Frameworks/R.framework/Versions/3.4/Resources/library/rJava/libs/...
  • Error : /tmp/Rtmp7cfZnR/R.INSTALL291a47a178675/xml2/man/read_xml.Rd:47: 无法载入共享目标对象‘/opt/microsoft/ropen/3.5.3/lib64/R/library/xml2/libs/xml2.so’:: libicui18n.so.58: 无法打开共享对象文件:...
  • centos tidyverse 报错

    2019-07-06 07:38:33
    Error : /tmp/Rtmp7cfZnR/R.INSTALL291a47a178675/xml2/man/read_xml.Rd:47: 无法载入共享目标对象‘/opt/microsoft/ropen/3.5.3/lib64/R/library/xml2/libs/xml2.so’:: libicui18n.so.58: 无法打开共享对象文件:...
  • R零基础学习-无法载入程辑包‘rJava’ 问题描述: library(xlsx) 载入需要的程辑包:rJava Error: package or namespace load failed for ‘rJava’: loadNamespace()里算’rJava’时.onLoad失败了,详细...
  • 问题描述: 安装包xlsx包后,运行library...无法启动此程序,因为计算机中丢失 jvm.dll。尝试重新安装该程序以解决此问题。 在R语言环境中的错误是: 载入需要的程辑包:rJava Error : loadNamespace()里算'rJava'...
  • R 语言爬虫 之 cnblog博文爬取

    千次阅读 2017-07-20 16:24:10
    来源:R 语言爬虫 之 cnblog博文爬取 a). 加载用到的R包 ##library packages needed in this case ...## Warning in doTryCatch(return(expr), name, parentenv, handler): 无法载入共享目标对象‘/Library/Fram
  • 错误: package or namespace load failed for ‘xlsx’: loadNamespace()里算'rJava'时.onLoad失败了,详细内容: 调用: dyn.load(file, DLLpath = DLLpath, ...) 错误: 无法载入共享目标对象‘/Library/Frameworks/...
  • Error : .onLoad failed in loadNamespace() for 'rJava', details: call: inDL(x, as.logical(local), as.logical(now), ...) error: 无法载入共享目标对象‘D:/Program Files/R/R-3.2.2/library/rJava/libs/x64/...
  • R语言导入xlsx包错误解决

    万次阅读 多人点赞 2014-12-08 16:38:00
    > library(xlsx) 载入需要的程辑包:rJava Error : loadNamespace()里算'rJava'时.onLoad失败了,详细内容:  调用: inDL(x, as.... 错误: 无法载入共享目标对象‘C:/Program Files/R/R-3.1.2/library/rJava/li
  • R:无法载入共享目标对象‘/usr/lib64/R/library/rJava/libs/rJava.so 问题描述: 今天在R上面安装mailR的包的时候,发生了这个错误 然后经过寻找发现,尝试安装一波rJava,结果失败了,出现了下述的错误: 通过这个...
  • Linux写时拷贝技术(copy-on-write) ...然后在这个基础上,由于代码段是不会被修改的,所以操作系统可以采用copy on write的优化技术,让两个进程共享同一份物理内存。这是属于在不改变系统行为的基础上,为了节省内存,
  • Java实现面向对象编程

    万次阅读 2018-07-17 16:18:06
    1.1用面向对象设计电子宠物系统... 14 1.1.1为什么使用面向对象... 14 1.1.2使用面向对象进行设计... 15 1.2通过创建对象实现领养宠物功能... 17 1.2.1创建类的对象... 17 1.2.2构造方法及其重载... 23 1.2.3...
  • 类与对象

    千次阅读 2012-10-06 19:43:02
    、类与对象 1、面向对象的特征有哪些方面,OOP的好处  类是具备某些共同特征的实体...抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象包括两个方面,一是过程抽象,二
  • 共享库中的位置无关代码(PIC)

    千次阅读 2016-04-08 11:11:22
    原作者:Eli Bendersky ...在之前的文章里我已经描述过在将共享载入程序地址空间时需要特殊的处理。简而言之,在链接器创建共享库时,它不能预先知道这个库将在哪里载入。这给在库里访问数据与代码带来了麻烦,应该使
  • 第1章 对象入门——Thinking-in-Java

    千次阅读 2016-03-24 11:31:01
    第1章 对象入门“为什么面向对象的编程会在软件开发领域造成如此震憾的影响?”面向对象编程(OOP)具有多方面的吸引力。对管理人员,它实现了更快和更廉价的开发与维护过程。对分析与设计人员,建模处理变得更加...
  • linux下有两种库:动态库和静态库(共享库)二者的不同点在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积比较大。动态库(共享库)的代码在可执行程序运行时才载入内存,在编译过程中...
  • const对象为什么可以在头文件中定义

    千次阅读 2014-06-15 10:13:53
    首先明确两点: const对象默认为文件的局部变量。《C++ Primer 4》p86头文件用于声明而不是用于定义。《C++ Primer 4》p100,
  • Android .so 共享库(动态链接库)

    千次阅读 2015-11-26 15:25:16
    l -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。 l -L.:表示要连接的库在当前目录...
  • VxWorks6.6开发共享库指南要点

    千次阅读 2015-02-10 14:32:14
    开放封闭原则(OCP,Open Closed Principle)是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一目标的最直接体现。 在软件架构中,模块化编程思想将系统分成很...
  • php面向对象的基础题

    千次阅读 2018-06-20 20:01:34
    (理解着回答)答:面向对象OO = 面向对象的分析OOA + 面向对象的设计OOD + 面向对象的编程OOP;通俗的解释就是“万物皆对象”,把所有的事物都看作一个个可以独立的对象(单元),它们可以自己完成自己的功能,而不是...
  • 用opencv的dnn模块做yolov5目标检测

    万次阅读 多人点赞 2021-01-17 13:23:29
    最近在微信公众号里看到多篇讲解yolov5在openvino部署做目标检测文章,但是没看到过用opencv的dnn模块做yolov5目标检测的。于是,我就想着编写一套用opencv的dnn模块做yolov5目标检测的程序。在编写这套程序时,遇到...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,106
精华内容 10,042
关键字:

无法载入共享目标对象