吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2635|回复: 2
收起左侧

[会员申请] Linux下库的编译和运行

[复制链接]
吾爱游客  发表于 2016-3-11 12:58

1、申 请 I D:bugnofree
2、个人邮箱:up2geek@163.com
今天才知道吾爱破解可以通过发文章来申请会员,不过这几日没有新文章,原创文章如下,是我今年三月份写的一篇文章(最开始发表在我的博客上,不知道若没有首发在吾爱可不可以申请),文章是关于Linux下库的编译和运行,是在学习Android NDK的时候,捎带着把Linux下的看了看,不知道此文能否通过,不过很希望加入吾爱破解大家庭.

Linux下库的编译和运行|bugnofree|2016-03-02 Wed 05:07 PM|
目录概览:

0x01.静态库的编译和使用
0x02.动态库的编译和使用
0x03.动态共享库显式调用
..........1.C语言动态库的显式调用
...........2.C++语言动态库的显式调用

Linux下的库大致有两种,分别是静态库和动态共享库.本文章将会介绍这两种库的编译以及运行.
0x01.静态库的编译和使用
环境为Ubuntu 14.04 x64,Linux下的静态库通常以.a为后缀,用于创建.a的工具为ar(archive的缩写).静态库链接后会将所有数据会添加到调用程序, 因此使用静态库的程序体积可能较大,但是使用静态库的程序不需要外部依赖项.
下面我们需要用到四个文件,如下所示:
├── libsrc.cpp
├── libhdr.h
├── caller.cpp
└── makefile

代码分别如下:
//libsrc.cpp
#include<iostream>
using namespacestd;
void brother()
{
    cout<<"Brother is a handsomeboy!"<<endl;
}
void sister()
{
    cout<<"Sister is abeauty!"<<endl;
}

//libhdr.h
#ifndef LIBHDR
#define LIBHDR
void brother();
void sister();
#endif

//caller.cpp
#include"libhdr.h"
int main()
{
    brother();
    sister();
    return 0;
}

#makefile
app:caller.cpplibstatic.a   
        @g++ -o app caller.cpp -I. -L. -lstatic
libstatic.a:libsrc.o
        @ar rcs libstatic.a libsrc.o
libsrc.o:libsrc.cpp
        @g++ -c libsrc.cpp
clean:## Clean themiddle files like *.o *.a etc
        @-rm *.o *.a app
run:  ## Run the program
        @./app
.PHONY:help
help:
        @grep -E '^[a-zA-Z_-]+:.*?## .*$$'$(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf"\033[36m%-30s\033[0m %s\n", $$1, $$2}'

说明如下:
1. 编写库libstatic.a的源码文件libsrc.cpp:
1. 将libsrc.cpp编译为libsrc.o文件:g++ -c libsrc.cpp
2. 生成库文件libstatic.a:ar rcslibstatic.a libsrc.o
2. 编写头文件libhdr.h,包含所有库中的函数原型.
3. 编写调用程序caller.cpp,包括库的头文件,并调用库函数
4. 写makefile自动化编译运行
其中的makefile可以使用make help来查看帮助,make来生成可执行程序,make run来运行程序,如下图所示:
1.png

其中makehelp很有用处,具体可以见参考链接2.
0x02.动态库的编译和使用
只需更改makefile如下即可:
#makefile
app:caller.cpplibshared.so   
        @g++ caller.cpp -o app -L. -lshared
        @echo You can type \"makerun\" to run the program.
libshared.so:libsrc.cpp
        @g++ -fPIC -shared -o libshared.solibsrc.cpp
clean:## Clean themiddle files like *.o *.a etc
        @-rm *.o *.a *.so app
        @echo Clean Done!
run:  ## Run the program
        @echo Run the program..
        @LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH./app
.PHONY:help
help:
        @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST)| sort | awk 'BEGIN {FS = ":.*?## "}; {printf"\033[36m%-30s\033[0m %s\n", $$1, $$2}'

主要有两个地方需要说明:
1.编译生成.so库的命令为:g++ -fPIC-shared -o libshared.so libsrc.cpp,其中的-fPIC表示编译位置无关代码(Position Independent Code), shared指明是生成动态链接库.
2.程序运行所需要的库,其默认的搜索路径一般为LD_LIBRARY_PATH所指定的路径,然后是/etc/ld.so.cache,再然后是/lib/和/usr/lib/.
这里我使用了设置环境变量LD_LIBRARY_PATH的方法.更多的方法可以参考链接3.
0x03.动态共享库显式调用
上述讲述的静态库和动态库都属于隐式调用.还有一种方式是显式调用.静态库因为直接"打包"到了程序中,因此显式调用是没有意义的. 动态共享库由于是在需要时才加载,所以往往是在需要的时候动态调用.
1.C语言动态库的显式调用
C语言动态库的显示调用很简单.
头文件dlfcn.h提供了下面几个接口:
函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程
void * dlopen(const char * pathname, int mode )
dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址.使用这个函数不但可以获取函数地址,也可以获取变量地址.
void* dlsym(void*handle,const char* symbol)
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载.
int dlclose (void*handle)
当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功.
const char*dlerror(void)

下面是一套针对C语言编写的源码调用.
//libsrc.c
#include<stdio.h>
void brother()
{
    printf("Brother is a handsomeboy!\n");
}

//libhdr.h
#ifndef LIBHDR
#define LIBHDR
void brother();
#endif

//caller.c
#include<dlfcn.h>
#include"libhdr.h"
int main()
{
    //函数指针声明
    void (*bro)();
    //句柄
    void *handle =dlopen("./libshared.so", RTLD_LAZY);
    //获取函数指针
    bro = dlsym(handle, "brother");
    //调用函数
    (*bro)();
    //释放
    dlclose(handle);
    return 0;
}

#makefile
app:caller.clibshared.so   
        @gcc -rdynamic -o app caller.c -ldl
        @echo You can type \"makerun\" to run the program.
libshared.so:libsrc.c
        @gcc -fPIC -shared -o libshared.solibsrc.c
clean:## Clean themiddle files like *.o *.a etc
        @-rm *.o *.a *.so app
        @echo Clean Done!
run:  ## Run the program
        @echo Run the program..
        @./app
.PHONY:help
help:
        @grep -E '^[a-zA-Z_-]+:.*?## .*$$'$(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf"\033[36m%-30s\033[0m %s\n", $$1, $$2}'

其中有几个地方需要注意:
1. 调用函数应该加入头文件dlfcn.h
2. dlopen函数应该传入动态共享库的相对或者绝对路径,不需要额外设置库环境变量
3. 主要的编译命令为:gcc -rdynamic -oapp caller.c -ldl
2.C++语言动态库的显式调用
由于没有专门针对C++语言动态库显示调用的API,因此还须借助为C语言提供的API,因此这就导致C++库代码的编写略为复杂.
解决方法是使用extern "C",这是C++中的关键字,用于声明C语言绑定的函数.这意味这只有非成员函数可以用此法来声明,因为它们不能重载. 使用这种方法后,C++中的函数名将会被作为符号名使用(C语言就是这样).通常地,我们就使用这种方法来用C++写动态库.
1)第一个例子是加载函数,如下:
//libsrc.cpp
#include<iostream>
using namespacestd;
extern"C" void brother()
{
    cout<<"Brother is a handsomeboy!"<<endl;
}
extern"C" void sister()
{
    cout<<"Sister is abeauty!"<<endl;
}

//libhdr.h
#ifndef LIBHDR
#define LIBHDR
#ifdef __cplusplus
extern"C" {
#endif
void brother();
void sister();
#ifdef __cplusplus
}
#endif
#endif

//caller.cpp
#include<dlfcn.h>
#include"libhdr.h"
int main()
{
    //函数指针声明
    void (*brother)();
    void (*sister)();
    //句柄
    void *handle =dlopen("./libshared.so", RTLD_LAZY);
    //获取函数指针
    brother = dlsym(handle,"brother");
    sister = dlsym(handle, "sister");
    //调用函数
    sister();
    brother();
    //释放
    dlclose(handle);
    return 0;
}

#makefile
app:caller.cpplibshared.so   
        @g++ -rdynamic caller.cpp -o app  -ldl
        @echo You can type \"makerun\" to run the program.
libshared.so:libsrc.cpp
        @gcc -fPIC -shared -o libshared.solibsrc.cpp
clean:## Clean themiddle files like *.o *.a etc
        @-rm *.o *.a *.so app
        @echo Clean Done!
run:  ## Run the program
        @echo Run the program..
        @./app
.PHONY:help
help:
        @grep -E '^[a-zA-Z_-]+:.*?## .*$$'$(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf"\033[36m%-30s\033[0m %s\n", $$1, $$2}'

其中的几点注意事项如下:
1. 编译动态库仍然是使用gcc,虽然我们是C++源码,编译调用动态库的程序建议使用g++
2. extern "C"关键字的使用,其中libsrc.h中的写法具有参照意义,因为这样的头文件既可以用于C++源码也可以用于C源码
3. extern "C"有两种形式:
第一种是extern"C" 变量或函数声明,第二种是另外就是extern"C"{函数或变量声明}.
第一种形式(inline form)表示同时声明为外部连接和C风格连接,而第二种形式仅声明为C风格连接(并不附带外部声明).例如:

    //第一种形式
    extern "C" int foo;       //外部变量声明并且C风格连接
    extern "C" void bar();    //外部函数声明并且C风格连接,此时就不能为static
    //第二种形式
    extern "C"
    {
       int foo;               //C风格连接,并且定义变量(不是外部连接声明!)
       void bar();
    }
   //当将第二种形式改写成如下形式时,第一二两种形式才具有等价性:
    extern "C"
    {
       extern int foo;        //这儿的extern声明其作用对象为外部连接
       extern void bar();
    }
   

4. 对于有extern "C"声明的函数,其作用只是改变了函数的连接形式(采用C风格,即在目标文件中的符号名与函数名相同),
并非改变函数的性质(也就是说被修饰的C++函数还可照常使用C++语言特性,与一般的C++函数除了多了个修饰之外,从外表上看并没有其它区别)
2)第二个是加载C++中的类:
由于类定义在可执行程序中,我们不能使用new来实例化一个类,但我们可以通过C++的多态来解决这个问题. 首先我们定义一个带有虚成员的接口类,然后定义一个实现了其虚成员的衍生类. 这里使用C++ dlopen mini HOWTO中用到的代码. 如下所示:
//triangle.cpp:
#include"polygon.hpp"
#include<cmath>
class triangle :public polygon
{
public:
    virtual double area() const
    {
        return side_length_ * side_length_ *sqrt(3) / 2;
    }
};
// the classfactories
extern"C" polygon* create()
{
    return new triangle;
}
extern"C" void destroy(polygon* p)
{
    delete p;
}

// polygon.hpp:
#ifndefPOLYGON_HPP
#definePOLYGON_HPP
class polygon
{
protected:
    double side_length_;
public:
    polygon() : side_length_(0) {}
    virtual ~polygon() {}
    void set_side_length(double side_length)
    {
        side_length_ = side_length;
    }
    virtual double area() const = 0;
};
// the types ofthe class factories
typedef polygon*create_t();
typedef voiddestroy_t(polygon*);
#endif

//main.cpp:
#include"polygon.hpp"
#include<iostream>
#include<dlfcn.h>
int main()
{
    using std::cout;
    using std::cerr;
    // load the triangle library
    void* triangle =dlopen("./triangle.so", RTLD_LAZY);
    if (!triangle)
    {
        cerr << "Cannot loadlibrary: " << dlerror() << '\n';
        return 1;
    }
    // reset errors
    dlerror();
   
    // load the symbols
    create_t* create_triangle = (create_t*)dlsym(triangle, "create");
    const char* dlsym_error = dlerror();
    if (dlsym_error)
    {
        cerr << "Cannot load symbolcreate: " << dlsym_error << '\n';
        return 1;
    }
   
    destroy_t* destroy_triangle = (destroy_t*)dlsym(triangle, "destroy");
    dlsym_error = dlerror();
    if (dlsym_error)
    {
        cerr << "Cannot load symboldestroy: " << dlsym_error << '\n';
        return 1;
    }
    // create an instance of the class
    polygon* poly = create_triangle();
    // use the class
    poly->set_side_length(7);
    cout<< "The area is: " << poly->area() << '\n';
    // destroy the class
    destroy_triangle(poly);
    // unload the triangle library
    dlclose(triangle);
}

#makefile
app:main.cpptriangle.so   
        @g++ -rdynamic main.cpp -o app  -ldl
        @echo You can type \"makerun\" to run the program.
triangle.so:triangle.cpp
        @gcc -fPIC -shared -o triangle.sotriangle.cpp
clean:## Clean themiddle files like *.o *.a etc
        @-rm *.o *.a *.so app
        @echo Clean Done!
run:  ## Run the program
        @echo Run the program..
        @./app
.PHONY:help
help:
        @grep -E '^[a-zA-Z_-]+:.*?## .*$$'$(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf"\033[36m%-30s\033[0m %s\n", $$1, $$2}'

需要注意的事项如下:
1. 你必须同时提供创建与销毁示例的函数,不能直接使用delete释放实例
2. 接口类的析构函数必须是虚函数
3. 如果你的基类不需要析构器,那么你需要定义一个空的虚的析构器

References:
1. http://www.dwheeler.com/program-library/Program-Library-HOWTO/x26.html
2. http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
3. http://www.dwheeler.com/program-library/Program-Library-HOWTO/x36.html
4. http://www.cnblogs.com/skynet/p/3372855.html
5. http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
6. http://tldp.org/HOWTO/C++-dlopen/thesolution.html
7. http://www.cnblogs.com/lichkingct/archive/2009/07/21/1527893.html



发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2016-3-11 15:32
13号开放注册,到时候自己来注册吧。
李莹莹 发表于 2016-3-11 16:06
好长一篇Linux下c++学习文
楼主的Ubuntu用的什么主题
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 22:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表