使用gcc理解共享库

时间:2010-10-10 20:37:29

标签: c linux gcc shared-libraries

我试图了解C

中共享库的以下行为
  

Machine One

$ cat one.c 
#include<stdio.h>

int main() {
    printf ("%d", 45);
}
$ gcc one.c -o one -O3
$ ldd one
    linux-gate.so.1 =>  (0x00331000)
    libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00bc2000)
    /lib/ld-linux.so.2 (0x006dc000)
$ cat two.c 
int main() {
    int i = 0;
}
$ gcc two.c -o two -O3
$ ldd two
    linux-gate.so.1 =>  (0x006f7000)
    libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
    /lib/ld-linux.so.2 (0x00eb0000)
$
  

机器二

$ cat three.c
#include<stdio.h>

int main() {
    printf ("%d", 45);
}
$ gcc three.c -o three -O3
$ ldd three
    /usr/lib/libcwait.so (0xb7ffd000)
    libc.so.6 => /lib/tls/i686/nosegneg/libc.so.6 (0x002de000)
    /lib/ld-linux.so.2 (0x002bf000)
$

目前我还不完全了解的一些事情:

  • 括号中给出的地址(例如(0x002de000))是什么意思?

    即使同一台机器上的同一个库,这些地址也不同,这表明这些地址是内存中加载这些库的位置的地址。但是,如果这是真的,为什么这些库根本加载到内存中(我还没有执行程序,不应该只在运行时加载它们吗?)。

  • 为什么two需要任何库?我使用了-O3,汇编程序输出是

    $ gcc two.c -S -O3 
    $ cat two.s
        .file   "two.c"
        .text
        .p2align 4,,15
    .globl main
        .type   main, @function
    main:
        pushl   %ebp
        movl    %esp, %ebp
        popl    %ebp
        ret
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
        .section    .note.GNU-stack,"",@progbits
    $
    

    任何图书馆的需求是什么?

  • 在第二台机器上,为什么使用/usr/lib/libcwait.so代替linux-gate.so.1

    我认为这是因为Machine Two上的内核很旧(2.6.9)而库linux-gate.so.1不可用。这是什么原因?

3 个答案:

答案 0 :(得分:5)

  

括号中给出的地址(例如,(0x002de000))是什么意思?

它是加载库的(虚拟)内存地址。 最近的系统可以提供随机加载库的位置,因此 该地址可能因调用而异。

  

不应该只在运行时加载它们吗?

是的。 ldd经历了大部分相同的程序 但是在运行时完成,以便能够找出各种各样的东西。

  

为什么两个人都需要任何图书馆?

libc.so.6是标准的C库(以及其他内容,如内核的接口),并且始终以ny默认链接。 gcc可以选择控制它,例如-nostdlib标志

ld-linux.so是一个动态加载器,可以加载/重定位其他共享库并运行您的应用程序。 ld-linux.so的联机帮助页为您提供了详细信息。

linux-gate.so.1是一个虚拟库,它只存在于内核的内存中。它用于执行对内核的系统调用,并根据您的CPU找出最有效的方法。这可能比你的其他2.6.9内核机器晚了。

我不知道/usr/lib/libcwait.so是什么,但你可以通过rpm -qif /usr/lib/libcwait.so获得一些关于它的信息

答案 1 :(得分:2)

地址基本上是随机数。在设计安全实现之前,ldd将始终指示加载程序段的内存地址。大约五年前,许多版本的Linux现在故意随意加载地址以挫败潜在的病毒编写者等。我编译one.c(作为t.c)并重复执行ldd:

[wally@zenetfedora .bin]$ cat t.c
#include <stdio.h>
int main()
{
    printf ("%d", 45);
}
[wally@zenetfedora .bin]$ gcc -o t t.c -O3
[wally@zenetfedora .bin]$ ldd t
    linux-gate.so.1 =>  (0x009e5000)
    libc.so.6 => /lib/libc.so.6 (0x002e4000)
    /lib/ld-linux.so.2 (0x002c2000)
[wally@zenetfedora .bin]$ ldd t
    linux-gate.so.1 =>  (0x00b8d000)
    libc.so.6 => /lib/libc.so.6 (0x002e4000)
    /lib/ld-linux.so.2 (0x002c2000)
[wally@zenetfedora .bin]$ ldd t
    linux-gate.so.1 =>  (0x00238000)
    libc.so.6 => /lib/libc.so.6 (0x002e4000)
    /lib/ld-linux.so.2 (0x002c2000)
[wally@zenetfedora .bin]$ ldd t
    linux-gate.so.1 =>  (0x002a0000)
    libc.so.6 => /lib/libc.so.6 (0x002e4000)
    /lib/ld-linux.so.2 (0x002c2000)
[wally@zenetfedora .bin]$ ldd t
    linux-gate.so.1 =>  (0x00f93000)
    libc.so.6 => /lib/libc.so.6 (0x002e4000)
    /lib/ld-linux.so.2 (0x002c2000)
[wally@zenetfedora .bin]$ ldd t
    linux-gate.so.1 =>  (0x00c7a000)
    libc.so.6 => /lib/libc.so.6 (0x002e4000)
    /lib/ld-linux.so.2 (0x002c2000)
[wally@zenetfedora .bin]$ ldd t
    linux-gate.so.1 =>  (0x00d1a000)
    libc.so.6 => /lib/libc.so.6 (0x002e4000)
    /lib/ld-linux.so.2 (0x002c2000)
[wally@zenetfedora .bin]$ ldd t
    linux-gate.so.1 =>  (0x00d12000)
    libc.so.6 => /lib/libc.so.6 (0x002e4000)
    /lib/ld-linux.so.2 (0x002c2000)

crtl和ld-linux加载地址是一致的,但linux-gate是随机的。

需要库,因为需要运行C运行时初始化和终止。当然,由于stdinstdoutstderr等等不需要初始化,因此可以在很大程度上优化这些。仍然,crtl是main()被调用的方式。

Linux的不同风格和版本有所不同。滑稽的演变有很多曲折。有些东西已被转移到其他图书馆。这与你当地的杂货店搬家的原因大致相同。它没有多大意义。

答案 2 :(得分:1)

该数字是运行可执行文件时加载库的内存地址。它是在链接时确定的,并且通常是随机的,以使库函数地址不可预测,因此更难以在漏洞利用中使用。标准C库默认由GCC链接。 libcwait可能是另一个默认库,可能由较旧的GCC版本使用。