Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1681101
  • 博文数量: 584
  • 博客积分: 13857
  • 博客等级: 上将
  • 技术积分: 11883
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-16 09:34

分类:

2011-01-21 19:49:20

原创  linux/c下对动态库的编译和装载的学习

测试的方法是将一个日志函数(mr_errlog), 单独记录在一个C文件(errlog.c)中, 对该C文件编译为一个动态库(libtest.so), 再用测试程序(main.c)调用该动态库.

1. 对动态库的编译

    对动态库的编译命令可以为:

    gcc -fPIC -shared -o libtest.so errlog.c

    其中gcc的选项说明如下:

-fpic
           Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target
           machine.  Such code accesses all constant addresses through a global offset table (GOT).  The dynamic
           loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part
           of the operating system).  If the GOT size for the linked executable exceeds a machine-specific maximum
           size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile
           with -fPIC instead.  (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000.  The 386 has no
           such limit.)

           Position-independent code requires special support, and therefore works only on certain machines.  For the
           386, GCC supports PIC for System V but not for the Sun 386i.  Code generated for the IBM RS/6000 is always
           position-independent.

       -fPIC
           If supported for the target machine, emit position-independent code, suitable for dynamic linking and
           avoiding any limit on the size of the global offset table.  This option makes a difference on the m68k,
           PowerPC and SPARC.

           Position-independent code requires special support, and therefore works only on certain machines.

    由上可以了解:

          1. -fpic / -fPIC 依赖于-shared选项;

          2. 通过-fpic / -fPIC编译的动态库通过"全局偏移表"(GOT: global offset table), 进入内存, 并且在内存中地址保持不变;

          3. 对"全局偏移表"的调用是"动态装载程序"(the dynamic loader),  它是操作系统的一部分, 而非GCC的一部分( 动态库本身就是在执行过程中被装入, 而非链接过程中);

          4. -fpic 与-fPIC的主要区别在于, -fpic对装入库大小的限制性比-fPIC大些, 对库具体的大小的要求根据处理器的不同而不同;

          5. Position-independent code requires special support, and therefore works only on certain machines. 不具备可移植性?

-shared
           Produce a shared object which can then be linked with other objects to form an executable.  Not all systems
           support this option.  For predictable results, you must also specify the same set of options that were used
           to generate code (-fpic, -fPIC, or model suboptions) when you specify this option.[1]

    与-shared对应的是-static

 -static
           On systems that support dynamic linking, this prevents linking with the shared libraries.  On other sys-
           tems, this option has no effect.

2. 对动态库的装载

    对动态库的装载主要通过调用下面四个接口完成:

     dlopen, dlerror, dlsysm, dlclose

     需包含的头文件为

     接口的具体使用可以通过man dlopen查看

     这里只记录两个要点:

     1. dlopen打开的动态库文件名中, 若不包含目录, 仅在系统和makefile指定的库文件目录中查找. 不会查找当前目录;

     2. 在调用dlsysm查找函数前, 应先调用dlerror清空出错信息;

     手册中的示例程序:

 

  1. #include   
  2. #include   
  3. #include   
  4.   
  5. int main(int argc, char **argv) {  
  6.     void *handle;  
  7.     double (*cosine)(double);  
  8.     char *error;  
  9.   
  10.     handle = dlopen ("libm.so", RTLD_LAZY);  
  11.     if (!handle) {  
  12.         fprintf (stderr, "%s\n", dlerror());  
  13.         exit(1);  
  14.     }  
  15.   
  16.     dlerror();    /* Clear any existing error */  
  17.     *(void **) (&cosine) = dlsym(handle, "cos");  
  18.     if ((error = dlerror()) != NULL)  {  
  19.         fprintf (stderr, "%s\n", error);  
  20.         exit(1);  
  21.     }  
  22.   
  23.     printf ("%f\n", (*cosine)(2.0));  
  24.     dlclose(handle);  
  25.     return 0;  
  26. }  

存在的问题:

      在手册中, 对dlopen中flag的可能取值中的一对必选参数: RTLD_LAZY与RTLD_NOW的理解存在问题.

      手册中对它们的解释如下:

RTLD_LAZY
              Perform lazy binding.  Only resolve symbols as the code that references them is executed.  If the symbol
              is  never  referenced,  then  it is never resolved.  (Lazy binding is only performed for function refer-
              ences; references to variables are always immediately bound when the library is loaded.)

       RTLD_NOW
              If this value is specified, or the environment variable LD_BIND_NOW is set to a  non-empty  string,  all
              undefined  symbols in the library are resolved before dlopen() returns. If this cannot be done, an error
              is returned.

       然而, 在实际测试过程中: 将动态库中的一个子函数屏蔽, 分别用LAZY和NOW模式完成编译链接后,

             LAZY: 似乎是在程序初始化过程中就发现错误并直接报错( 非程序中的显式报错 );

             NOW: 是在调用dlopen后, 程序检查出错时, 才发现的, 与手册中的描述不一致: all undefined  symbols in the library are resolved before dlopen() returns.

      报错信息如下:

            LAZY:

                 ./test_dll: symbol lookup error: ./libtest.so: undefined symbol: mr_get_time

            NOW:

                 1./libtest.so: undefined symbol: mr_get_time

     主程序代码如下:

 

  1. #include   
  2. #include   
  3. #include   
  4.   
  5.   
  6. int  
  7. main( )  
  8. {  
  9.     int     (* mr_err)( char *, intchar *, ... );  
  10.     void    *handle;  
  11.     char    *error;  
  12.   
  13.     handle = dlopen("./libtest.so", RTLD_NOW );  
  14.     if( !handle ){  
  15.         fprintf( stderr, "1%s\n", dlerror() );  
  16.         return -1;  
  17.     }  
  18.   
  19.     dlerror( );  
  20.     *(void **)(&mr_err) = dlsym( handle, "mr_errlog" );  
  21.     if( (error = dlerror( ) ) != NULL ){  
  22.         fprintf( stderr, "2%s\n", error );  
  23.         return -1;  
  24.     }  
  25.   
  26.     mr_err( __FILE__, __LINE__, "it's a frist show!" );  
  27.   
  28.     dlclose( handle );  
  29.   
  30.     return 0;  

阅读(598) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~