Posts Tagged ‘动态链接’

December 25, 2011

  场景是这样的。我在写一个Nginx模块,该模块使用了MySQL的C客户端接口库libmysqlclient,当然mysqlclient还引用了其他的库,比如libm, libz, libcrypto等等。对于使用mysqlclient的代码来说,需要关心的只是mysqlclient引用到的动态库。大部分情况下,不是每台机器都安装有libmysqlclient,所以我想把这个库静态链接到Nginx模块中,但又不想把mysqlclient引用的其他库也静态的链接进来。
  我们知道gcc的-static选项可以使链接器执行静态链接。但简单地使用-static显得有些’暴力’,因为他会把命令行中-static后面的所有-l指明的库都静态链接,更主要的是,有些库可能并没有提供静态库(.a),而只提供了动态库(.so)。这样的话,使用-static就会造成链接错误。
  在StackOverflow上面提了How to do partial linking这个问题,但两天都没人搭理我,大概那些哥们儿也像中国人一样,圣诞狂欢呢吧。但最后一个叫Employed Russian的哥们给了一个链接,和我的问题相似(你很少能遇到独一无二的问题)。这个帖子唯一的一个解答解决了问题。
  我之前的链接选项大致是这样的,

1
CORE_LIBS="$CORE_LIBS -L/usr/lib64/mysql -lmysqlclient -lz -lcrypt -lnsl -lm -L/usr/lib64 -lssl -lcrypto"
Tags: . 99 views
July 4, 2010

动态链接库

  动态库和静态库相似,也是各个目标文件的集合。但相比静态链接的程序,动态链接可执行程序要小得多:这类程序运行时需要外部共享 函数库的支持,因此好像并不完整。除了程序体小之外,动态链接允许程序包指定必须的库,而不必将库装入程序包内。动态链接技术还允许多个运行中的程序共享一个库,这样就不会出现同一代码的多份拷贝共占内存的情况了。由于这些原因,当前多数程序采用动态链接技术。
  在Linux中的扩展名通常为.so。但在链接时,并不会被链接到可执行文件中,而是在执行时(需要时)由操作系统的动态加载模块动态地加载到内存,并链接到可执行文件地址空间的相应位置。
  动态链接库的创建也分为编译和”归档”两个阶段,但不同的是在这两个阶段需要使用一些不同的命令选项。首先,需要将源文件编译成一种成为位置无关码(PIC: Position Independent Code)的目标文件,这种代码可以被加入到内存的任何位置却不需要加载器对其进行重定位,关于这种格式可以参考《链接器与加载器》和《程序员的自我修养–链接装载与库》中较为详尽的描述。接下来需要将这些位置无关码“归档”为.so文件。整个过程只需一个工具即可,即gcc。还是上面的源文件,执行以下命令:

1
2
$ cc -c -fpic plus.c sub.c
$ cc -shared -o libmath.so *.o
Tags: ,. 569 views
May 4, 2010

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ ssh FenglinHou@Dalian
~% cd Wdir/
~/Wdir% cat dump.cpp
#inlcude <everything>
int
main()
{
    std::set<std::Girl> ().clear();
    std::ifstream lifein("~/*");
    std::ofstream lifeout("~/*");
    std::copy(std::istream_iterator<std::stuff>(lifein), 
              std::istream_iterator<std::stuff>(), 
              std::ostream_iterator<std::stuff>(lifeout, "\newday"));
    return (0);
}
~/Wdir% cat Makefile
dump:dump.cpp
    g++ -odump dump.cpp
~/Wdir% make
~/Wdir% ./dump
Tags: ,,,. 43 views
September 2, 2009

  这里面没有用到C库,也没有main函数,为了把这个程序编译成可执行文件,需要指定程序的入口。编译指令:

1
2
3
4
5
6
7
8
9
10
$ gcc -c nomain.c 
$ ld -e nomain nomain.o -o nomain
$ ./nomain
$ echo $?
42
$ ls -l nomain
-rwxr-xr-x 1 ivan ivan 618 2009-09-02 22:11 nomain
$ strip nomain
$ ls -l nomain
-rwxr-xr-x 1 ivan ivan 356 2009-09-02 22:15 nomain

  解释一下,ld是linux下的一个链接器,-e选项用来指定程序的入口。编译后可执行文件的大小为618字节(一个动态链接的HelloWorld需要9KB,静态链接将近600K),strip命令可以”剥去”可执行文件中的调试信息,可进一步减小文件的大小,另外在链接时通过其他选项还可以将可执行文件中保存的编译器和系统版本信息也一并去掉……

Tags: ,,. 56 views
August 26, 2009

实现了动态加载标准库中的数学函数库,输入函数名及参数,返回计算结果。

1
2
3
4
5
6
7
8
9
10
11
/*
 * #include <dlfcn.h>
 * void *dlopen( const char *file, int mode );
 * void *dlsym( void *restrict handle, const char *restrict name );
 * char *dlerror();
 * char *dlclose( void *handle );
 * dlopen	使对象文件可被程序访问
 * dlsym	获取执行了 dlopen 函数的对象文件中的符号的地址
 * dlerror	返回上一次出现错误的字符串错误
 * dlclose	关闭目标文件
 */
Tags: ,. 20 views
August 25, 2009
0x4000a963 <_dl_runtime_resolve+3>:     movl   0x10(%esp,1),%edx
0x4000a967 <_dl_runtime_resolve+7>:     movl   0xc(%esp,1),%eax
0x4000a96b <_dl_runtime_resolve+11>:    call   0x4000a740 

  指令xchgl %eax,(%esp,1)将printf的地址放入栈顶。最精彩的一条指令当属ret $0×8,它将栈顶元素即printf的地址弹出至程序计数器PC,作为下一条将执行的指令地址,同时,清除堆栈中的0×10和0×8049560。此时堆栈中的情形,就如同直接调用了printf函数,似乎什么都没发生过。

  此外还做了一件重要的工作,就是把前面提到的addr2替换为printf的地址。从而当再次调用printf时jmp *addr1就直接将程序定位到printf,不需要再次加载库libc.so了。

  不知道我说清楚了没有,感觉说的很乱,文字也很乱。:-)

Tags: ,,. 311 views
Page 1 of 11