2016-04-06 13:40:41 xlxxcc 阅读数 4583
  • 宏定义与预处理、函数和函数-C语言专题第6部分

    本课程综合讲解了C语言的预处理和宏定义,详细讲述了宏定义的细节规则和头文件包含等常用预处理;然后讲述了函数的使用、函数库的使用,静态链接库和动态链接库等的制作和使用。本章的目标是提升大家对函数及函数库的认知,提升在实战中使用函数库解决问题的能力。

    8158 人正在学习 去看看 朱有鹏

1、链接库的基本知识

    库是一种软件组件技术,库里面封装了数据和函数。它的使用,可以是程序模块化。在程序中使用,我们可以称之为程序函数库。
    程序函数库可分为3种类型:静态函数库(static libraries)、共享函数库(shared libraries)、动态函数库(dynamically loaded libraries):
    1、静态函数库,是在程序执行前就加入到目标程序中去了;
    2、共享函数库,则是在程序启动的时候加载到程序中,它可以被不同的程序共享
    3、动态函数库,并非另外一种库函数格式,它只是使用动态加载方式加载共享函数库。

    Windows系统包括静态链接库(.lib文件)和动态链接库(.dll文件)。
    Linux通常把库文件存放在/usr/lib或/lib目录下
    Linux库文件名由:前缀lib、库名和后缀3部分组成,其中共享链接库以.so.X最为后缀, .X是版本号,静态链接库通常以.a作为后缀。
    Linux下标准库链接的三种方式(全静态 , 半静态 (libgcc,libstdc++), 全动态。 三种标准库链接方式的选项及区别见下表。

三种标准库链接方式的选项及区别:

标准库链接方式 示例连接选项 优点 缺点
全静态 -static -pthread -lrt -ldl 不会发生不同Linux 版本下的标准库不兼容问题 生成的文件比较大,应用程序功能受限(不能调用动态库等)
全动态 -pthread -lrt -ldl 生成的文件最小 不同Linux版本下标准库依赖不兼容问题
半静态(libgcc,libstdc++) -static-libgcc -L. -pthread -lrt -ldl 灵活度大,针对不同的标准库采取不同的链接策略,从而避免不兼容问题发生 难识别哪些库容易发生不兼容问题,会因选择的标准库版本而丧失某些功能

上述三种标准库链接方式中,比较特殊的是 半静态链接方式,主要在于其还需要在链接前增加额外的一个步骤:
ln -s ‘g++ -print-file-name=libstdc++.a’,作用是将 libstdc++.a(libstdc++ 的静态库)符号链接到本地工程链接目录
-print-file-name在gcc中的解释如下: Display the full path to library

ldd 简介:该命令用于打印出某个应用程序或者动态库所依赖的动态库 ,使用该命令我们可以观察到Linux标准库三种链接方式的区别。
从实际应用当中发现,最理想的标准库链接方式就是半静态链接,通常会选择将 libgcc 与 libstdc++ 这两个标准库静态链接,从而避免应用程序在不同 Linux 版本间标准库依赖不兼容的问题发生。

size 简介:该命令用于显示出可执行文件的大小

示例链接选项中所涉及命令(引用 GCC 原文):

  • -llibrary
  • -l library:指定所需要的额外库
  • -Ldir:指定库搜索路径
  • -static:静态链接所有库
  • -static-libgcc:静态链接 gcc 库
  • -static-libstdc++:静态链接 c++ 库

2、静态链接库的创建和使用

   涉及命令:ar, ar是创建、修改、提取静态库的操作。
   ar -t 显示静态库的内容
   ar -d 从库中删除成员文件
   ar -r 在库中加入成员文件,若存在,则替换
   ar -c 创建一个库
   ar -s 无论ar命令是否修改了库内容,都强制重新生成库符号表
   步骤如下:
   1、在一个头文件种声明静态库所导出的函数。
   2、在一个源文件种实现静态库所导出的函数。
   3、编译源文件,生成可执行代码。
   4、将可执行代码所在的目标文件加入到某个静态库中,并将静态库拷贝到系统默认的存放库文件的目录下。

下面通过一个例子来说明:mylib.h种存放的是静态库提供给用户使用的函数的声明,mylib.c实现了mylib.h种声明的函数。
mylib.h

#ifndef _MYLIB_H_
#define _MYLIB_H_
void weclome(void);
void outString(const char *str);
#endif

mylib.cpp

#include "mylib.h"
void welcome(void){
    printf("welcome to libmylib\n");
}
void outString(const char *str){
    if(str != NULL)
        printf("%s\n", str);
}

test.cpp

#include "mylib.h"
#include 
int main(void){
    printf("create and use library:\n");
    welcome();
    outString("it's successful\n");
    return 0;
}
  1. 编译mylib.c生成目标文件:gcc -o mylib.o -c mylib.cpp
  2. 将目标文件加入到静态库中:ar rcs libmylib.a mylib.o
  3. 将静态库copy到Linux的库目录(/usr/lib或者/lib)下:cp libmylib.a /usr/lib/libmylib.a
  4. 使用静态库编译,编译时无需带上前缀和后缀:gcc -o test test.cpp -lmylib
  5. 运行可执行程序test: ./test

合并静态链接库的脚本代码清单:

 echo CREATE demo.a > ar.mac 
 echo SAVE >> ar.mac 
 echo END >> ar.mac 
 ar -M < ar.mac 
 ar -q demo.a CdtLog.o 
 echo OPEN demo.a > ar.mac 
 echo ADDLIB xml.a >> ar.mac 
 echo SAVE >> ar.mac 
 echo END >> ar.mac 
 ar -M < ar.mac 
 rm ar.mac 

Linux makefile 中使用 ar 脚本方式进行静态库的创建,可以编写如下代码:

 define BUILD_LIBRARY 
 $(if $(wildcard $@),@$(RM) $@) 
 $(if $(wildcard ar.mac),@$(RM) ar.mac) 
 $(if $(filter %.a, $^), 
 @echo CREATE $@ > ar.mac 
 @echo SAVE >> ar.mac 
 @echo END >> ar.mac 
 @$(AR) -M < ar.mac 
 ) 
 $(if $(filter %.o,$^),@$(AR) -q $@ $(filter %.o, $^)) 
 $(if $(filter %.a, $^), 
 @echo OPEN $@ > ar.mac 
 $(foreach LIB, $(filter %.a, $^), 
 @echo ADDLIB $(LIB) >> ar.mac 
 ) 
 @echo SAVE >> ar.mac 
 @echo END >> ar.mac 
 @$(AR) -M < ar.mac 
 @$(RM) ar.mac 
 ) 
 endef 

 $(TargetDir)/$(TargetFileName):$(OBJS) 
    $(BUILD_LIBRARY) 

Linux 静态库链接顺序问题及解决方法:
为了解决这种库链接顺序问题,我们需要增加一些链接选项 :
(CXX)(LINKFLAGS) (OBJS)Xlinker"("(LIBS) -Xlinker “-)” -o $@
通过将所有需要被链接的静态库放入 -Xlinker “-(” 与 -Xlinker “-)” 之间,可以是 g++ 链接过程中, 自动循环链接所有静态库,从而解决了原本的链接顺序问题。

3、共享函数库的创建和使用

   GNU标准建议所有的函数库文件都放在/usr/local/lib目录下,而且建议命令可执行程序都放在/usr/local/bin目录下。
   文件系统层次化标准FHS(Filesystem Hierarchy Standard)规定了在一个发行包中大部分的函数库文件应该安装到/usr/lib目录下,但是如果某些
库是在系统启动的时候要加载的,则放到/lib目录下,而那些不是系统本身一部分的库则放到/usr/local/lib下面。
   上面两个路径的不同并没有本质的冲突。GNU提出的标准主要对于开发者开发源码的,而FHS的建议则是针对发行版本的路径的。具体的置信息可以看
/etc/ld.so.conf里面的配置信息,通过对它的修改,可以增加自己的目录。
   如果你想覆盖某个库中的一些函数,用自己的函数替换它们,同时保留该库中其他的函数的话,你可以在 /etc/ld.so.preload中加入你想要替换的库
(.o结尾的文件),这些preloading的库函数将有优先加载的权ldconfig可以更新/etc/ld.so.cache。/etc/ld.so.cache可以大大提高访问函数库的速度。
   HP-UX系统下,就是用SHLIB_PATH这个变量,而在AIX下则使用LIBPATH这个变量。

共享函数库创建的一个标准命令格式:
gcc -shared -Wl,-soname,your_soname -o library_name file_list library_list

例子:

  • 1、创建Object文件:
    gcc -fPIC -g -c -Wall a.c
    gcc -fPIC -g -c -Wall b.c
  • 2、创建共享函数库
    gcc -shared -Wl,-soname,liblusterstuff.so.1 -o liblusterstuff.so.1.0.1 a.o b.o -lc

如果是C++项目,最简单是使用Cmake来完成共享库的创建,步骤如下:

  • 如果创建的是JNI链接库,则需要将 jdk/include/jni.h 和 jdk/include/linux/jni_md.h 复制到 /usr/include 目录下。反正执行make命令的时候将会报错

  • 1、确保gcc-c++编译环境, 安装命令::
    yum install gcc-c++

  • 2、安装Cmake
    wget https://cmake.org/files/v3.5/cmake-3.5.1.tar.gz
    tar -xvf cmake-3.5.1.tar.gz
    cd cmake-3.5.1
    ./bootstrap
    make
    make install

  • 3、如果您使用的windows系统,则将您的项目上传到Linux,进入Linux下该项目的文件夹,创建CMakeLists.txt,内容格式如下:

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)  
# cpp 文件  
SET(test_SRCS  
    source/test1.cpp  
    source/test2.cpp  
    ......
)  

# 头文件
SET(test_HDRS  
    include/test1.h  
    include/test2.h  
    ..... 
)  

INCLUDE_DIRECTORIES(include)  

# test: 是生产的库的名字, 这里可以加上SHARED或者STATIC或者MODULE,分别表示动态库、静态库、模块。不加则默认是静态库
ADD_LIBRARY(test SHARED/STATIC/MODULE ${test_SRCS} ${test_HDRS}) 

# 生成可执行文件
# ADD_EXECUTABLE(test ${test_SRCS} ${test_HDRS})
  • 4、创建动态链接库:
    ccmake directory #用于配置编译选项,如VTK_DIR目录,一般这一步不需要配置
    cmake directory #用于根据CMakeLists.txt生成Makefile文件
    make #用于执行Makefile文件,编译程序,生成可执行文件

共享函数库的使用

   一旦你定义了一个共享函数库,你还需要安装它。其实简单的方法就是拷贝你的库文件到指定的标准的目录(例如/usr/lib),然后运行ldconfig。
如果你没有权限去做这件事情, 那么最简单的方法就是运行ldconfig:
    ldconfig -n directory_with_shared_libraries 
    然后设置LD_LIBRARY_PATH这个环境变量,它是一个以逗号分隔的路径的集合:
    LD_LIBRARY_PATH=$LD_LIBRARY_PATH,my_program
    如果一个新版的函数库要和老版本的二进制的库不兼容,则soname需要改变。对于C语言,有四种情况会出现不兼容问题:
   · 一个函数的行文改变了,这样它就可能与最开始的定义不相符合。
   · 输出的数据项改变了。
   · 某些输出的函数删除了。
   · 某些输出函数的接口改变了。**

4、共享函数库的动态加载

   共享函数库可以在程序运行过程中的任何时间加载,它们特别适合在函数中加载一些模块和plugin扩展模块的场合,因为它可以在当程序需要某个
plugin模块时才动态的加载。
   Linux系统下,DL函数库与其他函数库在格式上没有特殊的区别。通常C语言环境下,需要包含这个头文件。 Linux中使用的函数和Solaris中一样,都是
dlpoen()API。当然不是所有的平台都使用同样的接口,例如HP-UX使用shl_load()机制,而Windows平台用另外的其他的调用接口。

dlopen()

   dlopen函数打开一个函数库然后为后面的使用做准备。C语言原形是: 
     void * dlopen(const char *filename, int flag);
   如果文件名filename是以“/”开头,也就是使用绝对路径,那么dlopne就直接使用它,而不去查找某些环境变量或者系统设置的函数库所在的目录了。
否则dlopen()就会按照下面的次序查找函数库文件:
   1. 环境变量LD_LIBRARY指明的路径。
   2. /etc/ld.so.cache中的函数库列表。
   3. /lib目录,然后/usr/lib。不过一些很老的a.out的loader则是采用相反的次序,也就是先查 /usr/lib,然后是/lib。
   dlopen()函数中,参数flag的值必须是RTLD_LAZY或者RTLD_NOW,RTLD_LAZY的意思是resolve undefined symbols as code from the dynamic library is executed,而RTLD_NOW的含义是resolve all undefined symbols before dlopen() returns and fail if this cannot be done'
   注意函数库的加载顺序。

dlerror()

    通过调用dlerror()函数,我们可以获得最后一次调用dlopen(),dlsym(),或者dlclose()的错误信息。 

dlsym()

   void * dlsym(void *handle, char *symbol);
   函数中的参数handle就是由dlopen打开后返回的句柄,symbol是一个以NIL结尾的字符串。
   如果dlsym()函数没有找到需要查找的symbol,则返回NULL。典型的调用过程如下:
dlerror();      /*clear error code */  
s = (actual_type)dlsym(handle,    symbol_being_searched_for);  
if((error = dlerror()) != NULL){  
    /* handle error, the symbol wasn't found */  
} else {  
    /* symbol found, its value is in s */  
}    

dlclose()
dlopen()函数的反过程就是dlclose()数,dlclose()函数用力关闭一个DL函数库。真正释放的时候,如果函数库里面有_fini()这个函数,则自动调用_fini()这个函数,做一些必要的处理。Dlclose()返回0表示成功,其他非0值表示错误。

动态函数库的创建:
动态函数库并非另外一种库函数格式,可只是在程序运行的任何时候动态的加载的共享函数库或。它的创建可以参考共享函数库的创建。

动态函数库的使用:

int main(int argc, char *argv){  
        void *handle;  
        char *error;  

        double (*cosine )(double);  
        handle = dlopen("/lib/libm.so.6", RTLD_LAZY);  
        if(!handle){  
            fputs(dlerror(), stderr);  
             exit(1);  
        }  

        cosine = dlsym(handle, "cos");  
        if((error = dlerror()) != NULL){  
            fputs(error, stderr);  
            exit(1);  
        }  

        printf("%f", (*cosine)(2, 0));  

        dlclose(handle);  

        return 0;  
}  

如果这个程序名字叫test.c,那么用下面的命令来编译:
gcc -o test test.c –ldl

2019-10-23 16:29:53 weixin_37127273 阅读数 62
  • 宏定义与预处理、函数和函数-C语言专题第6部分

    本课程综合讲解了C语言的预处理和宏定义,详细讲述了宏定义的细节规则和头文件包含等常用预处理;然后讲述了函数的使用、函数库的使用,静态链接库和动态链接库等的制作和使用。本章的目标是提升大家对函数及函数库的认知,提升在实战中使用函数库解决问题的能力。

    8158 人正在学习 去看看 朱有鹏

Linux 下动态链接库的生成与使用

动态链接库介绍

动态链接库是一种不可以直接执行的二进制程序文件,它允许程序共享执行一段公用的代码和资源。 在 Linux 平台上动态链接库是以 .so 作为后缀名的。

相对于静态链接库来说,动态链接库在编译的时候并没有被编译进目标代码中,直到程序在执行到动态链接库中的相关函数的时候,才会调用动态链接库中的函数运行,因此使用动态链接库所生成的可执行文件比较小。正是由于动态链接库没有被直接编译到可执行文件中,因此动态链接库中的函数具体实现的变化并不影响可执行文件,因此使用动态链接库进行软件升级比较方便。

Linux下动态链接库的命令以 libxxx.so 方式进行命名,库名的前面加上 lib ,库名后缀为 .so

测试工程说明

这里以使用 md5 模块生成 md5 码的工程为例,介绍动态链接库的生成与使用。

  • testmain.c

  • md5.c

  • md5.h

testmain.c 源码

#include <stdio.h>
#include <string.h>
#include "md5.h"


int main()
{
	char str[150];
	int i=0;
	MD5_CTX md5;
	unsigned char decrypt[16]; 
	printf("please input a string ,the string length must <100\n");
	scanf("%s",str);

	printf("str :%s\n",str);


	MD5Init(&md5);         		
	MD5Update(&md5,str,strlen((char *)str));
	MD5Final(&md5,decrypt);  
	printf("加密前:%s\n加密后:\n",str);
	for(i=0;i<16;i++)
	{
		printf("%02x",decrypt[i]);
	}
	printf("\n");

	return 0;
}

md5.c 源码

#include <memory.h>
#include "md5.h"
 
unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
                         
void MD5Init(MD5_CTX *context)
{
     context->count[0] = 0;
     context->count[1] = 0;
     context->state[0] = 0x67452301;
     context->state[1] = 0xEFCDAB89;
     context->state[2] = 0x98BADCFE;
     context->state[3] = 0x10325476;
}
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
{
    unsigned int i = 0,index = 0,partlen = 0;
    index = (context->count[0] >> 3) & 0x3F;
    partlen = 64 - index;
    context->count[0] += inputlen << 3;
    if(context->count[0] < (inputlen << 3))
       context->count[1]++;
    context->count[1] += inputlen >> 29;
    
    if(inputlen >= partlen)
    {
       memcpy(&context->buffer[index],input,partlen);
       MD5Transform(context->state,context->buffer);
       for(i = partlen;i+64 <= inputlen;i+=64)
           MD5Transform(context->state,&input[i]);
       index = 0;        
    }  
    else
    {
        i = 0;
    }
    memcpy(&context->buffer[index],&input[i],inputlen-i);
}
void MD5Final(MD5_CTX *context,unsigned char digest[16])
{
    unsigned int index = 0,padlen = 0;
    unsigned char bits[8];
    index = (context->count[0] >> 3) & 0x3F;
    padlen = (index < 56)?(56-index):(120-index);
    MD5Encode(bits,context->count,8);
    MD5Update(context,PADDING,padlen);
    MD5Update(context,bits,8);
    MD5Encode(digest,context->state,16);
}
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
{
    unsigned int i = 0,j = 0;
    while(j < len)
    {
         output[j] = input[i] & 0xFF;  
         output[j+1] = (input[i] >> 8) & 0xFF;
         output[j+2] = (input[i] >> 16) & 0xFF;
         output[j+3] = (input[i] >> 24) & 0xFF;
         i++;
         j+=4;
    }
}
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
{
     unsigned int i = 0,j = 0;
     while(j < len)
     {
           output[i] = (input[j]) |
                       (input[j+1] << 8) |
                       (input[j+2] << 16) |
                       (input[j+3] << 24);
           i++;
           j+=4; 
     }
}
void MD5Transform(unsigned int state[4],unsigned char block[64])
{
     unsigned int a = state[0];
     unsigned int b = state[1];
     unsigned int c = state[2];
     unsigned int d = state[3];
     unsigned int x[64];
     MD5Decode(x,block,64);
     FF(a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */
 FF(d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */
 FF(c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */
 FF(b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */
 FF(a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */
 FF(d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */
 FF(c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */
 FF(b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */
 FF(a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */
 FF(d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */
 FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
 FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
 FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
 FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
 FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
 FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
 
 /* Round 2 */
 GG(a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */
 GG(d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */
 GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
 GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */
 GG(a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */
 GG(d, a, b, c, x[10], 9,  0x2441453); /* 22 */
 GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
 GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */
 GG(a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */
 GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
 GG(c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */
 GG(b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */
 GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
 GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */
 GG(c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */
 GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
 
 /* Round 3 */
 HH(a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */
 HH(d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */
 HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
 HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
 HH(a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */
 HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */
 HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */
 HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
 HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
 HH(d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */
 HH(c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */
 HH(b, c, d, a, x[ 6], 23,  0x4881d05); /* 44 */
 HH(a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */
 HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
 HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
 HH(b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */
 
 /* Round 4 */
 II(a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */
 II(d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */
 II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
 II(b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */
 II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
 II(d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */
 II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
 II(b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */
 II(a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */
 II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
 II(c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */
 II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
 II(a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */
 II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
 II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */
 II(b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */
     state[0] += a;
     state[1] += b;
     state[2] += c;
     state[3] += d;
}

md5.h 源码

#ifndef MD5_H
#define MD5_H
 
typedef struct
{
    unsigned int count[2];
    unsigned int state[4];
    unsigned char buffer[64];   
}MD5_CTX;
 
                         
#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
#define FF(a,b,c,d,x,s,ac) \
          { \
          a += F(b,c,d) + x + ac; \
          a = ROTATE_LEFT(a,s); \
          a += b; \
          }
#define GG(a,b,c,d,x,s,ac) \
          { \
          a += G(b,c,d) + x + ac; \
          a = ROTATE_LEFT(a,s); \
          a += b; \
          }
#define HH(a,b,c,d,x,s,ac) \
          { \
          a += H(b,c,d) + x + ac; \
          a = ROTATE_LEFT(a,s); \
          a += b; \
          }
#define II(a,b,c,d,x,s,ac) \
          { \
          a += I(b,c,d) + x + ac; \
          a = ROTATE_LEFT(a,s); \
          a += b; \
          }                                            
void MD5Init(MD5_CTX *context);
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
void MD5Final(MD5_CTX *context,unsigned char digest[16]);
void MD5Transform(unsigned int state[4],unsigned char block[64]);
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);
 
#endif

工程测试

正常生成可执行文件

gcc -o sotest testmain.c md5.c

编译 md5 模块为动态库

/home/libsotest# gcc -fPIC -shared -o libmd5.so md5.c

/home/libsotest# ls

libmd5.so  md5.c  md5.h  sotest  testmain.c

链接动态库生成可执行文件

ldd 介绍

ldd是list, dynamic, dependencies的缩写。

使用 ldd 命令可以查看一个可执行文件所链接的动态链接库。

/home/libsotest#gcc -o testsomain testmain.c -L. libmd5.so
/home/libsotest# ldd testsomain 

        linux-vdso.so.1 (0x00007fffdafd6000)
        libmd5.so => not found
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9ca5164000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f9ca5757000)
/home/libsotest# cp libmd5.so /lib/

/home/libsotest# ldd testsomain 
        linux-vdso.so.1 (0x00007ffec20fe000)
        libmd5.so => /lib/libmd5.so (0x00007f86a08b2000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f86a04c1000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f86a0cb8000)
/home/libsotest# ./testsomain 

please input a string ,the string length must <100
testmd5
str :testmd5
加密前:testmd5
加密后:
32269ae63a25306bb46a03d6f38bd2b7

动态链接库生成

下面介绍一下如何在 Makefile 中生成动态链接库文件。

CC = gcc
RM = rm -f
TARGET = testsomain
OBJS = md5.o testmain.o
MD5OBJ = md5.o
LIBS = libmd5.so

all:$(OBJS)
        $(CC) -o $(TARGET) $(OBJS)

dynamic:$(LIBS)

clean:
        $(RM) $(TARGET) $(OBJS) $(LIBS)


$(LIBS):$(MD5OBJ)
        $(CC) -shared -fPIC -o $@ $^

%.o:%.c
        $(CC) -c -fPIC $^ -o $@

动态链接库使用

CC = gcc
RM = rm -f
TARGET = testsomain
OBJS = md5.o testmain.o
MD5OBJ = md5.o
LIBS = libmd5.so

all:$(OBJS)
        $(CC) -o $(TARGET) $(OBJS)


dymtarget:$(LIBS)
        $(CC) -o $(TARGET) testmain.c  -L. $^

dynamic:$(LIBS)

clean:
        $(RM) $(TARGET) $(OBJS) $(LIBS)

$(LIBS):$(MD5OBJ)
        $(CC) -shared -fPIC -o $@ $^

%.o:%.c
        $(CC) -c -fPIC $^ -o $@
2016-07-27 20:03:37 Oscer2016 阅读数 800
  • 宏定义与预处理、函数和函数-C语言专题第6部分

    本课程综合讲解了C语言的预处理和宏定义,详细讲述了宏定义的细节规则和头文件包含等常用预处理;然后讲述了函数的使用、函数库的使用,静态链接库和动态链接库等的制作和使用。本章的目标是提升大家对函数及函数库的认知,提升在实战中使用函数库解决问题的能力。

    8158 人正在学习 去看看 朱有鹏

1.什么是动态链接库

动态链接库是程序运行时加载的库,当动态链接库正确安装后,所有的程序都可以使用动态库来运行程序。动态链接库是目标文件的集合,目标文件在动态链接库中的组织方式是按照特殊方式形成的。库中函数和变量的地址是相对地址,不是绝对地址,其真实地址在调用动态库的程序加载时形成。
动态链接库的名称有别名(soname),真名(realname)和链接名(linker name)。别名由一个前缀lib,然后是库的名字,再加上一个后缀“.so”构成。真名是动态链接库的真实名称,一般总是在别名的基础上加上一个小版本号,发布版本等构成。除此之外,还有一个链接名,即程序链接时使用的库的名字在动态链接库安装的时候,总是复制库文件到某个目录下,然后用一个软链接生成别名,在库文件进行更新的时候,仅仅更新软连接即可。

2.为什么要使用动态链接库:

   (1)减小可执行文件大小 .
   (2)实现资源共享 .
   (3)便于维护和升级 . 
   (4)比较安全 .
由于动态链接库函数的共享特性,它们不会被拷贝到可执行文件中。在编译的时候,编译器只会做一些函数名之类的检查。在程序运行的时候,被调用的动态链接库函数被安置在内存的某个地方,所有调用它的程序将指向这个代码段。因此,这些代码必须使用相对地址,而不是绝对地址。在编译的时候,我们需要告诉编译器,这些对象文件是用来做动态链接库的,所以要用地址无关代码( Position Independent Code (PIC) ) .
//filename:fun.c
#include <stdio.h>
void fun(void) {
    printf("XiyouLinuxGroup\n");
}
//filename:main.c
#include <stdio.h>

//声明函数,可以把所有的声明放到一个"*.h"文件中,然后主函数包含它就可以
void fun(void);

int main(void) {
    printf("hello universe\n");
    fun();
    return 0;
}

3.动态链接库的制作

gcc -fpic -shared fun.c -o libfun.so
//生成动态库文件libfun.so
-fpic:编译生成地址无关代码
-share:生成动态链接库

生成动态链接库之后一个很重要的问题就是安装,一般情况下将生成的动态链接库复制到系统默认的动态链接库的搜索路径下,通常有 /lib, /usr/lib, /usr/local/lib, 放到之上任何一个目录下都可以 。

4.动态链接库的配置

动态链接库并不是可以随意地使用,要在运行的程序中使用动态链接库,需要指定系统的动态链接搜索的路径,让系统找到运行所需的动态链接库才可以。系统中的配置文件/etc/ld.so.conf是动态链接库的搜索路径配置文件。在这个文件内,存放着可被Linux共享的动态链接库所在目录的名字(系统目录/lib,/usr/lib除外),多个目录名间以空白字符(空格,换行等)或冒号或逗号分隔。

//查看系统中的动态链接库的配置文件的内容
hp@lenovo 10:28 :~$ cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
//Debian的配置文件将目录/etc/ld.so.conf.d中的配置文件包含进来,对这个目录下的文件进行查看:

hp@lenovo 10:28 :~$ ls /etc/ld.so.conf.d/
fakeroot-x86_64-linux-gnu.conf  x86_64-linux-gnu.conf
i386-linux-gnu.conf             x86_64-linux-gnu_EGL.conf
libc.conf                       x86_64-linux-gnu_GL.conf

hp@lenovo 10:33 :~$ cat /etc/ld.so.conf.d/i386-linux-gnu.conf 
# Multiarch support
/lib/i386-linux-gnu
/usr/lib/i386-linux-gnu
/lib/i686-linux-gnu
/usr/lib/i686-linux-gnu

从上面的配置文件可以看出,在系统的动态链接库配置中,包含了该动态库/lib/i386-linux-gnu
/usr/lib/i386-linux-gnu
/lib/i686-linux-gnu
/usr/lib/i686-linux-gnu四个目录。

5.动态链接库管理命令

为了让新增加的动态链接库能够被系统共享,需要运行动态链接库的管理命令ldconfig, ldconfig 命令的作用是在系统的默认搜索路径,和动态库配置文件中所列出的目录里搜索动态链接库,创建动态链接装入程序需要的链接和缓存文件。搜索完毕后,将结果写入缓存文件/etc/ld.so.cache中,文件中保存的是已经排好的动态链接库名字列表。ldconfig 命令行的选项及含义如下:

  -c, --format=FORMAT        采用的格式:新、旧或兼容(默认)
  -C CACHE                   使用用户指定的缓存文件代替系统默认的             缓存文件/etc/ld.so.cache                                               

  -f CONF                    使用用户指定的配置文件代替默认文 件/etc/ld.so.conf

  -i, --ignore-aux-cache     忽略辅助缓存文件
  -l                         手动链接单个动态链接库。
  -n                         只在命令行中给出了进程目录,未创建缓冲区,进处理命令行指定的目录,不对系统的默认目录/lib,/usr/lib进行扫描,也不对配置文件/etc/ld.so.conf中所指定的目录进行扫描

  -N                         不要创建缓冲区
  -p, --print-cache          打印缓存文件中共享库的名字
  -r ROOT                   改变当前应用程序的根目录
  -v, --verbose              生成详细消息,打印ldconfig的当前版本号,显示所扫描的每一个目录和动态链接库

  -X                         不更新链接
  -?, --help                 给出该系统求助列表
      --usage                给出简要的用法信息
  -V, --version              打印程序版本号

如果想知道系统中有哪些动态链接库,可以使用ldconfig的-p选项来列出缓存文件中的动态链接库列表。下面的命令表明在系统缓存中共有1596个动态链接库。

hp@lenovo 10:34 :~$ ldconfig -p
在缓冲区“/etc/ld.so.cache”中找到 1596 个库
libzvbi.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi.so.0
libzvbi-chains.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi-chains.so.0
libzip.so.4 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzip.so.4
libzephyr.so.4 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzephyr.so.4
libzeitgeist-2.0.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzeitgeist-2.0.so.0
libzeitgeist-1.0.so.1 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzeitgeist-1.0.so.1
libz.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libz.so.1
libz.so.1 (libc6) => /lib/i386-linux-gnu/libz.so.1
libz.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libz.so
libyelp.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libyelp.so.0
libyaml-0.so.2 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libyaml-0.so.2

当用户的目录并不在系统动态链接库配置文件/etc/ld.so.conf中指定的时候,可以使用ldconfig命令显示指定要扫描的目录,将用户指定目录中的动态链接库放入系统中进行共享。命令格式的形式为:

ldconfig 目录名

这个命令将ldconfig指定的目录名中的动态链接库放入系统的缓存/etc/ld.so.cache中,从而可以被系统共享使用。下面的代码将扫描当前用户的lib目录,将其中的动态链接库加入系统:

ldconfig ~/lib


Note: 如果在运行上述命令后,再次运行ldconfig而没有加参数,系统会将/lib,/usr/lib,及/etc/ld.so.conf中指定目录中的动态库加入缓存,这时候上述代码中的动态链接库可能不会被系统共享了。

6.链接调用动态库

gcc -o main main.c ./libfun.so
//生成可执行文件main
这里写图片描述

gcc -o main main.c -L./ -lfun

-L 指定链接动态链接库的路径
-lfun链接库函数fun,但运行一般会出现如下问题:

这里写图片描述

这是由于程序运行时没有找到动态链接库造成的。程序编译时链接动态链接库和运行时使用动态链接库的概念是不同的,在运行时,程序链接的动态链接库需要在系统目录下才行。有几种办法可以解决此种问题:

将动态链接库的目录放到程序搜索路径中,可以将库的路径加到环境变量LD_LIBRARY_PATH中实现,例如:

export LD_LIBRARY_PATH=/home/hp/LinuxC:$LD_LIBRARY_PATH


将存放库文件libfun.so的路径~/LinuxC加入到搜索路径中,再运行就没有之前的警告了。

这里写图片描述

另一种方法是使用ld-Linux.so.2来加载程序,命令格式为:
/lib/ld-Linux.so.2 –library-path 路径 程序名

加载main程序的命令为:

/lib/ld-Linux.so.2 –library-path ~/LinuxC main

Note:如果系统的搜索路径下同时存在静态链接库和动态链接库,默认情况下会链接动态链接库。如果需要强制链接静态库,需要加上“-static”选项,即上述的编译方式改为如下的方式:
gcc -o main main.c -static -ldl
2015-12-31 22:36:29 u010073895 阅读数 2023
  • 宏定义与预处理、函数和函数-C语言专题第6部分

    本课程综合讲解了C语言的预处理和宏定义,详细讲述了宏定义的细节规则和头文件包含等常用预处理;然后讲述了函数的使用、函数库的使用,静态链接库和动态链接库等的制作和使用。本章的目标是提升大家对函数及函数库的认知,提升在实战中使用函数库解决问题的能力。

    8158 人正在学习 去看看 朱有鹏

windows

  1. dll放入C:\Windows\System32下
  2. Win+R,输入cmd,回车,黑窗口出现
  3. 输入regsvr32链接库名称      回车

如果dll没有放入C:\Windows\System32下,

需要输入绝对路径

 

linux下加载动态链接库的3种方法

1. 用ln将需要的so文件链接到/usr/lib或者/lib这两个默认的目录下边

 

ln -s /where/you/install/lib/*.so /usr/lib

sudo ldconfig

 

 

2.修改LD_LIBRARY_PATH

 

exportLD_LIBRARY_PATH=/where/you/install/lib:$LD_LIBRARY_PATH

sudo ldconfig

 

 

3.修改/etc/ld.so.conf,然后刷新

vim /etc/ld.so.conf

add /where/you/install/lib

sudo ldconfig

 

验证动态链接库是否安装成功的方式是

如果出现 libNLPIR:                                说明安装失败


2016-08-12 10:14:42 lihuajie1003 阅读数 819
  • 宏定义与预处理、函数和函数-C语言专题第6部分

    本课程综合讲解了C语言的预处理和宏定义,详细讲述了宏定义的细节规则和头文件包含等常用预处理;然后讲述了函数的使用、函数库的使用,静态链接库和动态链接库等的制作和使用。本章的目标是提升大家对函数及函数库的认知,提升在实战中使用函数库解决问题的能力。

    8158 人正在学习 去看看 朱有鹏

linux下动态链接库的制作:

so可以供多个进程使用,不同进程调用同一个so文件,

so文件源程序不需要main函数,有也不会被执行。

下面通过一个简单例子,来学习.so文件的制作跟使用。

/* 
 * max.h 
 * 
 */  
  
/*条件编译*/  
#ifndef MAX_H_  
#define MAX_H_  
  
#ifdef __cpluscplus  
extern "C"{  
#endif  
   int max(int a,int b);  
#endif /* MAX_H_ */  

/* 
 * max.cpp
 * 
*/
#include "max.h"
int max(int a,int b)  
{  
    return a>b?a:b;  
}  

链接库的制作:

编译c程序:
gcc -shared -fpic XXX.c -o XXX.so

编译c++程序:
g++ -shared -fpic XXX.cpp -o XXX.so

/* 
 * main.cpp 
 * 
 */  
#include <stdio.h>  
#include "max.h"  
int main(void)  
{  
    printf("call max function results is: %d\n",max(20,12));  
    return 0;  
}  
/* 使用libmax.so库 
 * gcc -o  main main.c -L. -lmax 
 * 
 *-L.: 在当前路径下寻找.so文件 
 *-lmax: 要链接这个libmax.so文件 
 * 
 * */  

编译main.cpp

       g++ -o main main.cpp -lmax

此时需要注意的是应该把libmax.so 放到/usr/lib目录中否则会出现错误

error while loading shared libraries: libmax.so: cannot open shared object file: No such file or directory

      ./main 结果出现20说明动态链接库制作正确。

如果要是在QT中调用,需要加上头文件和.so文件的绝对路径

INCLUDEPATH + =" "
LIBS += ""


$ g++ hello.cpp  -fPIC -shared -o libhello.so

$ g++ main.cpp -L. -lhello -o main

交叉编译步骤一致:

(1)  $arm-linux-g++ hello.cpp -fPIC -shared -o libarmhello.so

(2)  $arm-linux-g++ main.cpp -L. -larmhello -o armhello

交叉编译的时候出现错误:fatal error:opencv2/imgproc/imgproc.hpp:no such file or directory

解决方法:把交叉编译好的opencv下的include中的opencv2拷贝到交叉编译器中的include下

我的交叉编译好的opencv和opencv2路径是在:/opt/arm/include  

/opt/arm/include  文件下的opencv2 复制到/opt/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/include即可。


linux动态连接库

阅读数 1059

没有更多推荐了,返回首页