Andy Niu �����ĵ�

Andy Niu

Andy Niu Help  1.0.0.0
编译

变量

 链接静态库只链接调用的方法
 
 编译的头文件和库文件路径
 
 编译告警
 
 编译报错的解决思路
 
 PHONY的作用
 
 编译示例
 
 Linux静态链接库
 
 Linux动态链接库
 
 Linux动态加载库
 
 makefile
 
 编译间接依赖的库
 
 静态库与动态库的链接
 
 编译链接的有关问题
 
 方法地址找错
 

详细描述

变量说明

Linux动态加载库
1、动态加载库,解决什么问题?
    静态链接库和动态链接库是在编程时,直接调用对应的方法。链接时已经完成了,静态链接直接放在后面,动态链接使用-l链接。
    而动态加载库在编程时,需要使用dlopen等函数来获取库里面方法的定义,然后进行调用。
    对于没有提供头文件的动态库,只能dlopen等函数来调用。
2、测试代码:
    test.cpp
    #include <string>
    std::string GetName()
    {
            return std::string("Andy");
    }
    
    main.cpp
    #include <string>
    #include <dlfcn.h>
    using namespace std;
    typedef string (*Func) ();
    
    int main(int argc, char* argv[])
    {
            void* pHandle = dlopen("./libtest.so", RTLD_NOW);
            void* error = dlerror();
            printf("Error[%s]\n", error);
    
            Func pFunc = (Func)dlsym(pHandle,"GetName");
            error = dlerror();
            printf("Error[%s]\n", error);
    
            string name = pFunc();
            printf("name[%s]\n",name.c_str());
            return 0;
    }
3、生成动态库so文件
    [root@localhost dll2]# g++ -fPIC -shared -o libtest.so test.cpp
    [root@localhost dll2]# ll
    total 16
    -rwxr-xr-x 1 root root 5158 Jan 31 22:38 libtest.so
    -rw-r--r-- 1 root root  401 Jan 31 22:37 main.cpp
    -rw-r--r-- 1 root root   73 Jan 31 22:33 test.cpp
4、生成可执行文件
    [root@localhost dll2]# g++ -o main main.cpp -ldl
    [root@localhost dll2]# ll
    total 24
    -rwxr-xr-x 1 root root 5158 Jan 31 22:38 libtest.so
    -rwxr-xr-x 1 root root 6222 Jan 31 22:38 main
    -rw-r--r-- 1 root root  401 Jan 31 22:37 main.cpp
    -rw-r--r-- 1 root root   73 Jan 31 22:33 test.cpp
    -ldl就是 -l dl,表示动态加载dll
5、运行
    [root@localhost dll2]# ./main
    Error[(null)]
    Error[./libtest.so: undefined symbol: GetName]
    Segmentation fault (core dumped)
    报错没有找到定义的符号,明明有这个方法,为什么没有找到?
    这是因为G++编译的时候,进行了名称重整,生成libtest.so的时候,GetName进行了名称重整。
    可能变成了__string_GetName,所以dlsym(pHandle,"GetName"); 找不到符号。
6、怎么解决?
    需要抑制名称重整,修改如下:
    #include <string>
    extern "C"
    {
    std::string GetName()
    {
            return std::string("Andy");
    }
    };
    重新执行一遍,运行如下:
    [root@localhost dll2]# g++ -fPIC -shared -o libtest.so test.cpp
    [root@localhost dll2]# g++ -o main main.cpp -ldl
    [root@localhost dll2]# ./main
    Error[(null)]
    Error[(null)]
    name[Andy]
7、上面的做法存在问题,因为C编译器并不认识extern "C",因此需要预编译,如下:
    #include <string>
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    std::string GetName()
    {
            return std::string("Andy");
    }
    #ifdef __cplusplus
    };
    #endif
8、静态链接、动态链接和动态加载的比较:
    a、对于静态库,实现发生改变,依赖它的exe(或者dll)文件必须重新静态链接,把新的代码实现链接到exe文件中,才会有效。
    b、对于动态库的链接,实现发生改变,依赖它的exe(或者dll)文件不需要重新链接。
        如果接口发生变化,必须重新构建,否则报错,如下:
        ./main: symbol lookup error: ./main: undefined symbol: _Z7GetNamev
    c、对于动态库的加载,和动态库的链接是同样道理。二者的区别是:动态链接是在程序运行之前,链接确定下来。
        但是有些场景下,需要在程序运行过程中,按照需要加载进来。
        相比较动态链接,动态加载的优点是:延迟和按照需要进行。
Linux动态链接库
1、测试代码:
    test.h
    #include <string>
    std::string GetName();
    
    test.cpp
    #include "test.h"
    std::string GetName()
    {
            return std::string("Andy");
    }
    
    main.cpp
    #include "test.h"
    using namespace std;
    int main(int argc, char* argv[])
    {
            string name = GetName();
            printf("name[%s]\n",name.c_str());
            return 0;
    }
2、生成动态库so文件
    [root@localhost dll]# g++ -fPIC -shared -o libtest.so test.cpp
    [root@localhost dll]# ll
    total 20
    -rwxr-xr-x 1 root root 5158 Jan 31 20:58 libtest.so
    -rw-r--r-- 1 root root  149 Jan 31 20:46 main.cpp
    -rw-r--r-- 1 root root   74 Jan 31 20:43 test.cpp
    -rw-r--r-- 1 root root   41 Jan 31 20:55 test.h
3、生成可执行文件
    [root@localhost dll]# g++ -o main -ltest -L./ main.cpp
    [root@localhost dll]# ll
    total 28
    -rwxr-xr-x 1 root root 5158 Jan 31 20:58 libtest.so
    -rwxr-xr-x 1 root root 5992 Jan 31 20:59 main
    -rw-r--r-- 1 root root  149 Jan 31 20:46 main.cpp
    -rw-r--r-- 1 root root   74 Jan 31 20:43 test.cpp
    -rw-r--r-- 1 root root   41 Jan 31 20:55 test.h
4、运行
    [root@localhost dll]# export LD_LIBRARY_PATH=$(pwd)
    [root@localhost dll]# ldd main
            linux-gate.so.1 =>  (0x00402000)
            libtest.so => /home/niu/dll/libtest.so (0x00e91000)
            libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x021da000)
            libm.so.6 => /lib/libm.so.6 (0x00817000)
            libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x07cee000)
            libc.so.6 => /lib/libc.so.6 (0x006c9000)
            /lib/ld-linux.so.2 (0x006ab000)
    [root@localhost dll]# ./main
    name[Andy]
Linux静态链接库
1、测试代码:
    test.h
    #include <string>
    std::string GetName();
    
    test.cpp
    #include "test.h"
    std::string GetName()
    {
            return std::string("Andy");
    }
    
    main.cpp
    #include "test.h"
    #include <stdio.h>
    using namespace std;
    int main(int argc, char* argv[])
    {
            string name = GetName();
            printf("name[%s]\n",name.c_str());
            return 0;
    }
2、生成静态库
    [root@localhost lib]# ll
    total 12
    -rw-r--r-- 1 root root 149 Jan 31 21:27 main.cpp
    -rw-r--r-- 1 root root  73 Jan 31 21:27 test.cpp
    -rw-r--r-- 1 root root  41 Jan 31 21:27 test.h
    [root@localhost lib]# g++ -c test.cpp 
    [root@localhost lib]# ll
    total 16
    -rw-r--r-- 1 root root  149 Jan 31 21:27 main.cpp
    -rw-r--r-- 1 root root   73 Jan 31 21:27 test.cpp
    -rw-r--r-- 1 root root   41 Jan 31 21:27 test.h
    -rw-r--r-- 1 root root 1384 Jan 31 21:30 test.o
    [root@localhost lib]# ar -rc libtest.a test.o
    [root@localhost lib]# ll
    total 20
    -rw-r--r-- 1 root root 1532 Jan 31 21:30 libtest.a
    -rw-r--r-- 1 root root  149 Jan 31 21:27 main.cpp
    -rw-r--r-- 1 root root   73 Jan 31 21:27 test.cpp
    -rw-r--r-- 1 root root   41 Jan 31 21:27 test.h
    -rw-r--r-- 1 root root 1384 Jan 31 21:30 test.o
3、生成可执行文件
    [root@localhost lib]# g++ -o main main.cpp libtest.a 
    [root@localhost lib]# ll
    total 28
    -rw-r--r-- 1 root root 1532 Jan 31 21:30 libtest.a
    -rwxr-xr-x 1 root root 6317 Jan 31 21:31 main
    -rw-r--r-- 1 root root  149 Jan 31 21:27 main.cpp
    -rw-r--r-- 1 root root   73 Jan 31 21:27 test.cpp
    -rw-r--r-- 1 root root   41 Jan 31 21:27 test.h
    -rw-r--r-- 1 root root 1384 Jan 31 21:30 test.o
4、特别注意:链接静态库,上面使用的方式是,后面直接添加静态库文件。
    也可以使用 -l的方式链接静态库,如下:
    g++ -o main main.cpp -L./ -ltest
    也就是说,【g++ -o main main.cpp -L./ -ltest】与【g++ -o main main.cpp libtest.a】是等价的。
    但是,有些特殊情况,需要明确标识是静态库还是动态库。
    使用-Wl,-Bstatic或者-Wl,-Bdynamic
    -Wl.option  此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然后传递给会连接程序.
5、运行
    [root@localhost lib]# ldd main
            linux-gate.so.1 =>  (0x00320000)
            libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x021da000)
            libm.so.6 => /lib/libm.so.6 (0x00817000)
            libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x07cee000)
            libc.so.6 => /lib/libc.so.6 (0x006c9000)
            /lib/ld-linux.so.2 (0x006ab000)
    [root@localhost lib]# ./main
    name[Andy]
makefile
1、有以下三个文件:main.cpp, student.h, student.cpp
2、makefile文件如下:
app:main.o student.o
    g++ -o app main.o student.o

main.o:main.cpp student.h
    g++ -c main.cpp

student.o:student.cpp student.h
    g++ -c student.cpp

clean:
    rm -fr app *.o
3、app是目标,依赖main.o student.o,要生成目标,执行的命令是 g++ -o app main.o student.o
    main.o是同样道理。
4、make xxx 执行对应的命令,
    make app 执行 g++ -o app main.o student.o
    make main.o 执行 g++ -c main.cpp
    make 默认执行 make app
5、目标main.o 和 student.o可以去掉,makefile在生成app的时候,发现依赖的对象不存在,会自动生成依赖的对象。
6、make会自动在当前目录下面,查找makefile文件,如果存在多个makefile,可以指定使用哪一个,如下:
    make -f makefile2
7、打印makefile中的变量
    makefile与shell脚本不一样,具体方法如下:
all: desc $(DLIBTARGET)
    $(CP) $(DLIBTARGET) $(TARGET_BASEPATH)/lib_linux/ibp_sdk/imds_sdk

desc:
    echo aaaaaaaaaaa
    @echo $(TARGET_BASEPATH)
    echo bbbbbbbbbbb
8、在makefile中调用shell脚本,如下:
all: desc
    echo '1111111'
    echo 22222222
    
desc:
    hh=kkkkkk;\
    echo $$hh
    ddddd;\
    ret=$$?;if [ $$ret -ne 0 ]; then echo "OK"; fi
    echo aaaaaaaaaaa
    @echo $(TARGET_BASEPATH)
    echo bbbbbbbbbbb
    在makefile中使用shell脚本,有两点特别注意:
    a、一行必须是一个完整的shell语句,可以使用反斜杠折行
    b、为了区分makefile中的变量,引用变量使用 $$var
9、如果你想要看到make构建的详细过程,可以使用make VERBOSE=1或者VERBOSE=1 make命令来进行构建。
PHONY的作用
1、避免语义冲突,告诉make不是创建目标文件,而是执行一些命令,也就是说,伪目标。
    考虑,下面的情况,makefile内容如下:
    clean:
        rm -fr *.o
    执行:
    niuzibin@ubuntu:~/work/test1/phony$ ll
    total 12
    drwxrwxr-x 2 niuzibin niuzibin 4096 Apr  8 23:09 ./
    drwxrwxr-x 3 niuzibin niuzibin 4096 Apr  8 23:07 ../
    -rw-rw-r-- 1 niuzibin niuzibin    0 Apr  8 23:08 1.o
    -rw-rw-r-- 1 niuzibin niuzibin    0 Apr  8 23:08 2.o
    -rw-rw-r-- 1 niuzibin niuzibin   19 Apr  8 23:07 makefile
    niuzibin@ubuntu:~/work/test1/phony$ more makefile 
    clean:
            rm -fr *.o
    niuzibin@ubuntu:~/work/test1/phony$ make clean
    rm -fr *.o
    niuzibin@ubuntu:~/work/test1/phony$ ll
    total 12
    drwxrwxr-x 2 niuzibin niuzibin 4096 Apr  8 23:10 ./
    drwxrwxr-x 3 niuzibin niuzibin 4096 Apr  8 23:07 ../
    -rw-rw-r-- 1 niuzibin niuzibin   19 Apr  8 23:07 makefile
    
    假设当前目录有个clean文件,如下:
    niuzibin@ubuntu:~/work/test1/phony$ touch clean  
    niuzibin@ubuntu:~/work/test1/phony$ ll
    total 12
    drwxrwxr-x 2 niuzibin niuzibin 4096 Apr  8 23:12 ./
    drwxrwxr-x 3 niuzibin niuzibin 4096 Apr  8 23:07 ../
    -rw-rw-r-- 1 niuzibin niuzibin    0 Apr  8 23:12 1.o
    -rw-rw-r-- 1 niuzibin niuzibin    0 Apr  8 23:12 2.o
    -rw-rw-r-- 1 niuzibin niuzibin    0 Apr  8 23:12 clean
    -rw-rw-r-- 1 niuzibin niuzibin   19 Apr  8 23:07 makefile
    niuzibin@ubuntu:~/work/test1/phony$ make clean
    make: `clean' is up to date.
    这个时候的make clean,make读取到makefile中的clean,检查当前目录是否有目标clean,已经有目标clean了,就不再执行。
    这不是我们所期望的,怎么解决这个问题?
    告诉make,这个clean是个伪目标,强制执行下面的命令。如下:
    niuzibin@ubuntu:~/work/test1/phony$ ll
    total 12
    drwxrwxr-x 2 niuzibin niuzibin 4096 Apr  8 23:23 ./
    drwxrwxr-x 3 niuzibin niuzibin 4096 Apr  8 23:07 ../
    -rw-rw-r-- 1 niuzibin niuzibin    0 Apr  8 23:23 1.o
    -rw-rw-r-- 1 niuzibin niuzibin    0 Apr  8 23:23 2.o
    -rw-rw-r-- 1 niuzibin niuzibin    0 Apr  8 23:12 clean
    -rw-rw-r-- 1 niuzibin niuzibin   32 Apr  8 23:23 makefile
    niuzibin@ubuntu:~/work/test1/phony$ more makefile 
    .PHONY:clean
    clean:
            rm -fr *.o
    niuzibin@ubuntu:~/work/test1/phony$ make clean
    rm -fr *.o
2、解决间接依赖的问题。
    考虑下面的情况,当前包含了四个目录test、add、sub、include 和一个顶层目录makefile文件。
    test、add、sub三个目录分别包含了三个源程序test.c、add.c、sub.c和三个子目录makefile。
    niuzibin@ubuntu:~/work/test1/phony$ ll
    total 28
    drwxrwxr-x 6 niuzibin niuzibin 4096 Apr  8 23:42 ./
    drwxrwxr-x 3 niuzibin niuzibin 4096 Apr  8 23:07 ../
    drwxrwxr-x 2 niuzibin niuzibin 4096 Apr  8 23:42 add/
    drwxrwxr-x 2 niuzibin niuzibin 4096 Apr  8 23:34 include/
    -rw-rw-r-- 1 niuzibin niuzibin  249 Apr  8 23:37 makefile
    drwxrwxr-x 2 niuzibin niuzibin 4096 Apr  8 23:42 sub/
    drwxrwxr-x 2 niuzibin niuzibin 4096 Apr  8 23:42 test/
    
    niuzibin@ubuntu:~/work/test1/phony$ more ./include/heads.h 
    #ifndef _HEAD_H_
    #define _HEAD_H_
    
    extern int add(int,int);
    extern int sub(int,int);
    
    #endif
    
    niuzibin@ubuntu:~/work/test1/phony$ more ./add/add.c       
    #include "../include/heads.h"
    int add(int a,int b)
    {
            return (a+b);
    }
    niuzibin@ubuntu:~/work/test1/phony$ more ./add/makefile 
    add.o :add.c ../include/heads.h
            gcc -c -o $@ $< 
    
    .PHONY: clean 
    clean: 
            rm -f *.o
    
    niuzibin@ubuntu:~/work/test1/phony$ more ./sub/sub.c 
    #include "../include/heads.h"
    int sub(int a,int b)
    {
            return a-b;
    }
    niuzibin@ubuntu:~/work/test1/phony$ more ./sub/makefile 
    sub.o:sub.c ../include/heads.h
            gcc -c -o $@ $< 
    
    .PHONY: clean 
    clean: 
            rm -f *.o
    
    niuzibin@ubuntu:~/work/test1/phony$ more ./makefile 
    OBJS = ./add/add.o ./sub/sub.o ./test/test.o
    program:  $(OBJS)
            gcc ./test/test.o ./add/add.o ./sub/sub.o -o program
    
    $(OBJS):
            make -C $(dir $@)
    
    .PHONY: clean
    clean:
            make -C ./add  clean
            make -C ./sub  clean
            make -C ./test clean
            rm -f program
    运行,如下:
    niuzibin@ubuntu:~/work/test1/phony$ make
    make -C add/
    make[1]: Entering directory `/home/niuzibin/work/test1/phony/add'
    gcc -c -o add.o add.c 
    make[1]: Leaving directory `/home/niuzibin/work/test1/phony/add'
    make -C sub/
    make[1]: Entering directory `/home/niuzibin/work/test1/phony/sub'
    gcc -c -o sub.o sub.c 
    make[1]: Leaving directory `/home/niuzibin/work/test1/phony/sub'
    make -C test/
    make[1]: Entering directory `/home/niuzibin/work/test1/phony/test'
    gcc -c -o test.o test.c
    make[1]: Leaving directory `/home/niuzibin/work/test1/phony/test'
    gcc ./test/test.o ./add/add.o ./sub/sub.o -o program
    
    注意:makefile的规则,是目标,依赖什么,怎么做,怎么做前面是tab键
    现在修改 ./add/add.c实现,如下:
    niuzibin@ubuntu:~/work/test1/phony$ vi ./add/add.c 
    #include "../include/heads.h"
    int add(int a,int b)
    {
            return (a+b+100);
    }
    再次执行make,如下:
    niuzibin@ubuntu:~/work/test1/phony$ make
    make: `program' is up to date.
    
    当代码实现修改了,我们期望重新构建,但是并没有重新构建。分析为什么?
    构建program的时候,检查被依赖的项 add sub test是否更新了,这里只有源代码更新了,依赖的目标文件没有更新,导致program没有重新构建。
    解决办法就是,告诉make,./add/add.o ./sub/sub.o ./test/test.o是伪目标,强制进入子目录执行make。如下:
    OBJS = ./add/add.o ./sub/sub.o ./test/test.o
    program:  $(OBJS)
            gcc ./test/test.o ./add/add.o ./sub/sub.o -o program
    
    .PHONY: $(OBJS)
    $(OBJS):
            make -C $(dir $@)
    
    .PHONY: clean
    clean:
            make -C ./add  clean
            make -C ./sub  clean
            make -C ./test clean
            rm -f program
方法地址找错
1、两个动态库,含有相同的C接口方法名,在linux下,找方法地址可能会找错误,windows不会找错
编译告警
1、测试代码
    niuzibin@ubuntu:~/work/flawfinder$ vi main.cpp 
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char* argv[])
    {
            printf("hello wolrd\n");
            int aa;
            int bb = aa + 10;
            char ch[50];
            gets(ch);
            scanf("%c",ch);
            return 1;
    }
2、输出所有的告警 -Wall,这里是-大写W 加上单词all
    niuzibin@ubuntu:~/work/flawfinder$ g++ -Wall -o main main.cpp
    main.cpp: In function ‘int main(int, char**)’:
    main.cpp:9:2: warning: ‘char* gets(char*)’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
    gets(ch);
    ^
    main.cpp:9:9: warning: ‘char* gets(char*)’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
    gets(ch);
            ^
    main.cpp:7:6: warning: unused variable ‘bb’ [-Wunused-variable]
    int bb = aa + 10;
        ^
    main.cpp:7:16: warning: ‘aa’ may be used uninitialized in this function [-Wmaybe-uninitialized]
    int bb = aa + 10;
                    ^
    /tmp/ccUdCIgt.o: In function `main':
    main.cpp:(.text+0x39): warning: the `gets' function is dangerous and should not be used.
3、禁止所有的告警 -w,在linux中,大小写往往表示相反的意思,-W是开启告警,-w是关闭告警,如下:
    niuzibin@ubuntu:~/work/flawfinder$ g++ -w -o main main.cpp               
    /tmp/ccVjBsPa.o: In function `main':
    main.cpp:(.text+0x39): warning: the `gets' function is dangerous and should not be used.
    注意:有些告警比较严重,即使带上-w选项,也不能禁止,如上。
4、每一种告警,默认值(是否输出是不一样的),如下:
    niuzibin@ubuntu:~/work/flawfinder$ g++ -Wall -o main main.cpp                
    main.cpp: In function ‘int main(int, char**)’:
    main.cpp:9:2: warning: ‘char* gets(char*)’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
    gets(ch);
    ^
    main.cpp:9:9: warning: ‘char* gets(char*)’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
    gets(ch);
            ^
    main.cpp:7:6: warning: unused variable ‘bb’ [-Wunused-variable]
    int bb = aa + 10;
        ^
    main.cpp:7:16: warning: ‘aa’ may be used uninitialized in this function [-Wmaybe-uninitialized]
    int bb = aa + 10;
                    ^
    /tmp/ccic7wY5.o: In function `main':
    main.cpp:(.text+0x39): warning: the `gets' function is dangerous and should not be used.
    niuzibin@ubuntu:~/work/flawfinder$ g++ -o main main.cpp      
    main.cpp: In function ‘int main(int, char**)’:
    main.cpp:9:2: warning: ‘char* gets(char*)’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
    gets(ch);
    ^
    main.cpp:9:9: warning: ‘char* gets(char*)’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
    gets(ch);
            ^
    /tmp/ccLU9M12.o: In function `main':
    main.cpp:(.text+0x39): warning: the `gets' function is dangerous and should not be used.
    可以看到,在默认情况下,-Wdeprecated-declarations是输出的,而-Wunused-variable和-Wmaybe-uninitialized是不输出的。
5、如何输出默认不输出的?
    指定-Wxxx强制输出,如下:
    g++ -o main main.cpp -Wunused-variable
    特别注意:一般情况,要输出全部的告警 -Wall,因为告警往往意味着潜在的风险。
编译报错的解决思路
1、cmake编译报错,如下:
    /home/niuzibin/work/heming/cryptdb/src/util/onions.hh:40:35: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11 [enabled by default]
    /home/niuzibin/work/heming/cryptdb/src/util/onions.hh:40:37: error: ‘SECLEVEL’ is not a class or namespace
2、我直接执行一把
    g++ -o KeyManager.o -c KeyManager.cpp -I../include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/include/ -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/include/ -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql/ -I/home/niuzibin/work/heming/cryptdb/src/ -I/usr/include/openssl
    同样的错误,如下:
    /home/niuzibin/work/heming/cryptdb/src/util/onions.hh:40:35: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11 [enabled by default]
    /home/niuzibin/work/heming/cryptdb/src/util/onions.hh:40:37: error: ‘SECLEVEL’ is not a class or namespace  
3、加上-std=c++11,再来测试一把,如下:
    g++ -std=c++11 -o KeyManager.o -c KeyManager.cpp -I../include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/include/ -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/include/ -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql/ -I/home/niuzibin/work/heming/cryptdb/src/ -I/usr/include/openssl
    报错如下:
    /home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql/field.h:236:49: error: invalid use of incomplete type ‘struct TABLE’
     my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values -
    /home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql/structs.h:29:8: error: forward declaration of ‘struct TABLE’
    struct TABLE;
    原因是:在前置声明的情况下,调用了方法。
4、那就要思考一个问题了,为什么直接在 heming/cryptdb中可以构建通过,把执行的构建命令拿出来,如下:
    make VERBOSE=1
    cd /home/niuzibin/work/heming/cryptdb/build/src/keymanager && /usr/bin/c++   -DDBUG_OFF -DEMBEDDED_LIBRARY -DHAVE_CONFIG_H -DKeyManager_EXPORTS -DMYSQL_BUILD_DIR=\"/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build\" -DMYSQL_SERVER -std=c++0x -fPIC -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/regex -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/sql -I/home/niuzibin/work/heming/cryptdb/src -I/usr/include/openssl    -g -O0 -fno-strict-aliasing -fno-rtti -fwrapv -fPIC -Wall -Werror -Wpointer-arith -Wendif-labels -Wformat=2 -Wextra -Wmissing-noreturn -Wwrite-strings -Wno-unused-parameter -Wno-deprecated -Wmissing-declarations -Woverloaded-virtual -Wunreachable-code -D_GNU_SOURCE -o CMakeFiles/KeyManager.dir/KeyManager.cpp.o -c /home/niuzibin/work/heming/cryptdb/src/keymanager/KeyManager.cpp
5、修改一下,拿到本地执行一把,
    /usr/bin/c++   -DDBUG_OFF -DEMBEDDED_LIBRARY -DHAVE_CONFIG_H -DKeyManager_EXPORTS -DMYSQL_BUILD_DIR=\"/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build\" -DMYSQL_SERVER -std=c++0x -fPIC -I../include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/regex -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/sql -I/home/niuzibin/work/heming/cryptdb/src -I/usr/include/openssl    -g -O0 -fno-strict-aliasing -fno-rtti -fwrapv -fPIC -Wall -Werror -Wpointer-arith -Wendif-labels -Wformat=2 -Wextra -Wmissing-noreturn -Wwrite-strings -Wno-unused-parameter -Wno-deprecated -Wmissing-declarations -Woverloaded-virtual -Wunreachable-code -D_GNU_SOURCE -o KeyManager.cpp.o -c KeyManager.cpp
    可以执行成功
6、那么对照一下,和上面的构建命令有啥区别?
    一点一点向正确的靠近。
    错误的
    g++ -std=c++11 -o KeyManager.o -c KeyManager.cpp -I../include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/include/ -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/include/ -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql/ -I/home/niuzibin/work/heming/cryptdb/src/ -I/usr/include/openssl
    正确的
    /usr/bin/c++   -DDBUG_OFF -DEMBEDDED_LIBRARY -DHAVE_CONFIG_H -DKeyManager_EXPORTS -DMYSQL_BUILD_DIR=\"/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build\" -DMYSQL_SERVER -std=c++0x -fPIC -I../include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/regex -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/sql -I/home/niuzibin/work/heming/cryptdb/src -I/usr/include/openssl    -g -O0 -fno-strict-aliasing -fno-rtti -fwrapv -fPIC -Wall -Werror -Wpointer-arith -Wendif-labels -Wformat=2 -Wextra -Wmissing-noreturn -Wwrite-strings -Wno-unused-parameter -Wno-deprecated -Wmissing-declarations -Woverloaded-virtual -Wunreachable-code -D_GNU_SOURCE -o KeyManager.cpp.o -c KeyManager.cpp 
7、最终找到错误的地方,需要 添加头文件的搜索路径,添加宏-DMYSQL_SERVER 以及 变异选项 -Wno-deprecated
    g++ -std=c++11 -o KeyManager.o -c KeyManager.cpp -DMYSQL_SERVER -Wno-deprecated -I../include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/include -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/sql -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/regex -I/home/niuzibin/work/heming/cryptdb/external_libs/mysql-src/build/sql -I/home/niuzibin/work/heming/cryptdb/src -I/usr/include/openssl
8、事后总结:
    这里编译报错 
        error: invalid use of incomplete type ‘struct TABLE’
        error: forward declaration of ‘struct TABLE’
    直接原因是 在前置声明的情况下,调用了方法。
    间接原因是 缺少预定义宏导致的问题,需要添加预定义宏 -DMYSQL_SERVER
编译的头文件和库文件路径
1、编译,引用的头文件路径使用 -I
    有一部分头文件路径,g++会默认引用,如何查看?
    `g++ -print-prog-name=cc1plus` -v
2、链接时使用-L指定库的路径,使用-l指定库,运行时使用LD_LIBRARY_PATH指定库的路径。
    目前没有找到方法,显示g++默认引用的库文件路径。步骤是:
    g++会去找-L
    再找gcc的环境变量LIBRARY_PATH
    再找内定的目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
3、由于g++搜索默认的头文件和库文件路径,所以有时候不指定,构建也是OK的。
编译示例
1、C++代码:
#include <iostream>
int main(int argc,char* argv[])
{
        printf("Hello,world\n");
}
2、编译 g++ -c hello.cpp 
    生成hello.o
3、链接 g++ -o hello hello.o
    生成hello,可执行程序
4、运行hello, 打印Hello world
5、编译链接,一起执行g++ -o hello hello.cpp
6、g++ hello.cpp 默认生成 a.out
7、编译链接的时候,需要知道头文件的路径,库的名称和路径,分别使用-I -l -L指定,如下:
    g++ -o main main.cpp -I/usr/include -lpthread -L/usr/lib
8、宏定义使用-D选项,如下:
    g++ -o main main.cpp -DDEBUG
9、特别注意:Windows下面包含头文件,可以使用斜杠或者反斜杠,不区分大小写。
    但是Linux下面,不能使用反斜杠,而且区分大小写。如下,#include "../include/aaa.h"
    因此,会出现Windows下面编译成功,而在Linux下面编译失败,需要注意。
编译链接的有关问题
1、普通方法,只声明,不定义,没有问题,只要不调用方法,就不会链接出错。
2、虚方法,只声明,不定义,即使不调用方法,链接也报错。为什么?
    为了实现多态性,必须在类的虚方法表里设置这个虚方法的地址(也就是虚方法的实现),没有定义当然报错。
    注意:子类重写会在他的虚方法表里重新设置这个地址。
3、纯虚方法,只声明,不定义,没有问题。为什么?
    对于纯虚方法,可以认为在类的虚方法表里,对于的纯虚方法slot设置0,也就是地址为0
    这个slot值为0,导致两个问题:
    a、当前类不能实例化,考虑如果可以实例化,调用对应的虚方法,就是引用地址为0的方法,当然不行。
        也就是说,纯虚方法会导致当前类为抽象类(即使提供方法实现),不能实例化。
        编译报错:不能实例化抽象类,并且会指出纯虚方法。
    b、子类必须重写虚方法,子类的虚方法表对父类进行整体拷贝。考虑如果没有重写,也就是子类对应的slot值也是0,这当然不行。
    注意:纯虚方法也可以提供实现,相当于对于的纯虚方法slot值由0再改为实际的方法地址。
编译间接依赖的库
1、main依赖test.so,test.so依赖link.so,示例代码如下:
    [root@localhost link]# more link.h
    #include <string>
    std::string GetAAA();
    
    [root@localhost link]# more link.cpp
    #include "link.h"
    std::string GetAAA()
    {
        return std::string("Andy");
    }
    
    [root@localhost link]# more test.h
    #include <string>
    std::string GetName();
    
    [root@localhost link]# more test.cpp
    #include "test.h"
    #include "link.h"
    std::string GetName()
    {
        return GetAAA();
    }
    
    [root@localhost link]# more main.cpp 
    #include "test.h"
    using namespace std;
    int main(int argc, char* argv[])
    {
        string name = GetName();
        printf("name[%s]\n",name.c_str());
        return 0;
    }
2、对于这种情况,构建有两种方式。
3、第一种方式:先生成link.so,然后生成test.so的时候,链接link.so,把符号表使用占位符关联起来。
    然后生成main的时候,只需要链接test.so,如下:
    [root@localhost link]# g++ -fPIC -shared -o liblink.so link.cpp
    [root@localhost link]# g++ -fPIC -shared -o libtest.so test.cpp -L./ -llink
    [root@localhost link]# g++ -o main main.cpp -L./ -ltest
    [root@localhost link]# ll
    total 44
    -rwxr-xr-x 1 root root 5157 Aug 18 18:07 liblink.so
    -rwxr-xr-x 1 root root 4557 Aug 18 18:08 libtest.so
    -rw-r--r-- 1 root root   74 Aug 18 17:42 link.cpp
    -rw-r--r-- 1 root root   40 Aug 18 17:42 link.h
    -rwxr-xr-x 1 root root 5992 Aug 18 18:08 main
    -rw-r--r-- 1 root root  153 Aug 18 17:33 main.cpp
    -rw-r--r-- 1 root root   81 Aug 18 17:42 test.cpp
    -rw-r--r-- 1 root root   41 Aug 18 17:33 test.h
    [root@localhost link]# ./main
    name[Andy]
4、第二种方式:先生成link.so,然后生成test.so的时候,不链接link.so。
    然后生成main的时候,同时链接test.so和link.so,如下:
    [root@localhost link]# g++ -fPIC -shared -o liblink.so link.cpp
    [root@localhost link]# g++ -fPIC -shared -o libtest.so test.cpp
    [root@localhost link]# g++ -o main main.cpp -L./ -ltest
    .//libtest.so: undefined reference to `GetAAA()'
    collect2: ld returned 1 exit status
    [root@localhost link]# g++ -o main main.cpp -L./ -ltest -llink
    [root@localhost link]# ll
    total 44
    -rwxr-xr-x 1 root root 5157 Aug 18 18:10 liblink.so
    -rwxr-xr-x 1 root root 4533 Aug 18 18:10 libtest.so
    -rw-r--r-- 1 root root   74 Aug 18 17:42 link.cpp
    -rw-r--r-- 1 root root   40 Aug 18 17:42 link.h
    -rwxr-xr-x 1 root root 6016 Aug 18 18:11 main
    -rw-r--r-- 1 root root  153 Aug 18 17:33 main.cpp
    -rw-r--r-- 1 root root   81 Aug 18 17:42 test.cpp
    -rw-r--r-- 1 root root   41 Aug 18 17:33 test.h
    [root@localhost link]# ./main
    name[Andy]
5、也就是,main-->test.so-->link.so,可以在每一步都链接,也可以在最后链接。
    每一步都链接,会导致test.so会稍微大一点,因为里面保存了链接信息。使用ldd可以看到二者的区别,如下:
    [root@localhost link]# g++ -fPIC -shared -o libtest.so test.cpp -L./ -llink
    [root@localhost link]# ldd libtest.so 
            linux-gate.so.1 =>  (0x00ab3000)
            liblink.so => ./liblink.so (0x00973000)
            libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00110000)
            libm.so.6 => /lib/libm.so.6 (0x005a2000)
            libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00812000)
            libc.so.6 => /lib/libc.so.6 (0x00ca3000)
            /lib/ld-linux.so.2 (0x006ab000)
    [root@localhost link]# g++ -fPIC -shared -o libtest.so test.cpp
    [root@localhost link]# ldd libtest.so 
            linux-gate.so.1 =>  (0x00d7a000)
            libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x009b9000)
            libm.so.6 => /lib/libm.so.6 (0x00c08000)
            libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00256000)
            libc.so.6 => /lib/libc.so.6 (0x00110000)
            /lib/ld-linux.so.2 (0x006ab000)
    每一步都链接,test中多了一项链接 liblink.so => ./liblink.so (0x00973000)
6、每一步都链接,只是相当于最后编译的时候少一个链接-llink,但是,编译的时候文件liblink.so还是必须需要的。
7、不管是每步都链接,还是最后一起链接,能否定位到方法的实现,都是在最后一步才确定下来的。
    也就是说,对于每步都链接,链接link,生成test.so的时候,即使找不到link的方法实现,生成test.so也不会报错,
    最后一步生成main的时候,才报错找不到方法实现。
8、特别注意一点:对于动态库,其中的代码实现不会合并到任何其他动态库或者可执行文件中。
    动态库中的代码段只会在内存中加载一次,所有依赖该动态库的主体,必须解决一个问题,就是定位到代码段的实现。
    比如,定位到方法的入口。解决这个问题,有两种办法:
    a、动态链接,编译的时候使用占位符,然后链接的时候修改占位符的取值,定位到方法实现的地址。
    b、动态加载,运行的时候,使用系统库根据方法名称直接找到方法的实现。
        被依赖的动态库,暴露出来的接口不能进行名称重整,否者找不到。
9、再次强调,对于动态库,其中的代码实现不会合并到任何其他动态库或者可执行文件中。
链接静态库只链接调用的方法
1、动态库或者可执行文件链接静态库,只会链接调用的方法,对于没有调用的方法,不会链接。
    比如静态库暴露了10个方法,动态库或者可执行文件只调用了一个方法,只会链接这一个方法。
2、示例代码如下:
    root@ubuntu:/home/disk1/CPP_2/Link# more test.h 
    int add(int a, int b);
    int sub(int a, int b);
    
    root@ubuntu:/home/disk1/CPP_2/Link# more test1.cpp 
    #include "test.h"
    int add(int a, int b)
    {
            return a+b;
    }
    
    root@ubuntu:/home/disk1/CPP_2/Link# more test2.cpp 
    #include "test.h"
    int sub(int a, int b)
    {
            return a-b;
    }
    
    root@ubuntu:/home/disk1/CPP_2/Link# more main.cpp 
    #include "test.h"
    #include <stdio.h>
    int main()
    {
            int c = add(1,2);
            printf("c[%d]\n", c);
            return 0;
    }
3、测试如下:
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -c test1.cpp  
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -c test2.cpp
    
    root@ubuntu:/home/disk1/CPP_2/Link# ar -rc libtest.a test1.o test2.o
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -o main main.cpp -L./ -ltest
    root@ubuntu:/home/disk1/CPP_2/Link# ./main
    c[3]
    root@ubuntu:/home/disk1/CPP_2/Link# objdump -t main|grep "add"
    0000000000400562 g     F .text  0000000000000014              _Z3addii
    root@ubuntu:/home/disk1/CPP_2/Link# objdump -t main|grep "sub"
    root@ubuntu:/home/disk1/CPP_2/Link# 
    可以看到,main中并没有sub方法的代码实现。
4、如果我想把静态库中的所有方法都链接进来(这种需求比较奇葩,也会存在),怎么办?
    使用 --whole-archive编译选项,如下:
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -o main main.cpp -L./ -ltest                                           
    root@ubuntu:/home/disk1/CPP_2/Link# objdump -t main|grep "sub"      
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -o main main.cpp -Wl,--whole-archive -L./ -ltest -Wl,--no-whole-archive
    root@ubuntu:/home/disk1/CPP_2/Link# objdump -t main|grep "sub"                                                 
    0000000000400576 g     F .text  0000000000000016              _Z3subii
    可以看到,加上--whole-archive链接静态库时,会把没有调用的方法也合并过来。
5、特别说明,上面是可执行文件链接静态库,动态库链接静态库,道理也是一样的。
    动态库和可执行文件没有本质区别,可执行文件多了一个程序入口的main方法。
    测试如下:
    root@ubuntu:/home/disk1/CPP_2/Link# more aaa.cpp 
    #include "test.h"
    #include <stdio.h>
    int aaa()
    {
            int c = add(1,2);
            printf("c[%d]\n", c);
            return 0;
    }
    
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -fPIC -shared -o libaaa.so aaa.cpp -L./ -ltest                                           
    root@ubuntu:/home/disk1/CPP_2/Link# objdump -t libaaa.so |grep "sub"                  
    root@ubuntu:/home/disk1/CPP_2/Link# ll |grep libaaa.so                                
    -rwxr-xr-x  1 root root 8082 Jun 14 15:47 libaaa.so*

    root@ubuntu:/home/disk1/CPP_2/Link# g++ -fPIC -shared -o libaaa.so aaa.cpp -Wl,--whole-archive -L./ -ltest -Wl,--no-whole-archive
    root@ubuntu:/home/disk1/CPP_2/Link# objdump -t libaaa.so |grep "sub"
    0000000000000780 g     F .text  0000000000000016              _Z3subii
    root@ubuntu:/home/disk1/CPP_2/Link# ll |grep libaaa.so 
    -rwxr-xr-x  1 root root 8149 Jun 14 15:47 libaaa.so*
    可以看到,加上--whole-archive,生成的动态库,包含所有的代码实现,并且文件大小也更大。
6、特别需要注意的是:
    链接静态库的最小单元是 o文件,上面的两个方法,分别在两个cpp文件实现,生成不同的o文件
    如果生成一个o文件呢?
    示例代码
    root@ubuntu:/home/disk1/CPP_2/Link# more test1.cpp 
    #include "test.h"
    int add(int a, int b)
    {
            return a+b;
    }
    root@ubuntu:/home/disk1/CPP_2/Link# more test2.cpp 
    #include "test.h"
    int sub(int a, int b)
    {
            return a-b;
    }
    root@ubuntu:/home/disk1/CPP_2/Link# more test.cpp 
    #include "test.h"
    int add(int a, int b)
    {
            return a+b;
    }
    int sub(int a, int b)
    {
            return a-b;
    }
    测试如下:
    root@ubuntu:/home/disk1/CPP_2/Link# rm -fr *.o *.a
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -c test1.cpp
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -c test2.cpp
    root@ubuntu:/home/disk1/CPP_2/Link# ar -rc libtest.a test1.o test2.o
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -o main main.cpp -L./ -ltest
    root@ubuntu:/home/disk1/CPP_2/Link# objdump -t main |grep "sub"
    root@ubuntu:/home/disk1/CPP_2/Link# 
    root@ubuntu:/home/disk1/CPP_2/Link# rm -fr *.o *.a
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -c test.cpp
    root@ubuntu:/home/disk1/CPP_2/Link# ar -rc libtest.a test.o
    root@ubuntu:/home/disk1/CPP_2/Link# g++ -o main main.cpp -L./ -ltest
    root@ubuntu:/home/disk1/CPP_2/Link# objdump -t main |grep "sub"
    0000000000400576 g     F .text  0000000000000016              _Z3subii
    也就是说,链接的最小单元是 o文件(目标文件)
7、得出的结论是:
    链接静态库的方法,默认情况下(不加--whole-archive),是按需链接,也就是说,只链接需要的方法。
    但是,链接的最小单元是目标文件,需要目标文件中的一个方法,会把整个目标文件链接进来。
参见
静态库与动态库的链接
测试场景,Test,lib1,lib2,dll1,dll2
分为下面四种情况:
1、Test->lib1->lib2
    lib1编译自己的代码,对lib2的部分,只需要lib2的头文件,对lib2的代码实现,使用占位符关联。
    生成Test链接的时候,把lib1的代码实现包含进来,再递归,把lib1中关联lib2的代码实现也包含进来。
    运行Test的时候,不再需要lib1和lib2。
    也就是说,lib2不合并到lib1中,等到exe的时候,一起合并到exe中。
2、Test->lib1->dll2
    lib1编译自己的代码,对dll2的部分,只需要dll2的头文件,对dll2的代码实现,使用占位符关联。
    生成Test链接的时候,把lib1的代码实现包含进来,但是,dll2中的代码不包含进来。
    运行Test的时候,不需要lib1,但是需要dll2【不需要dll的lib文件】
    也就是说,dll2不合并到lib1中,等到exe的时候,把lib1合并到exe中。
3、Test->dll1->lib2
    dll1编译自己的代码,对lib2的部分,需要lib2的头文件和实现,即lib2,把lib2的代码实现包含到dll1中
    生成Test链接的时候,dll1中的代码不包含进来,也不再需要lib2。
    【可以这样测试,生成lib2,生成dll1,生成test,删除lib2,删除test.exe,再生成test,
        可以成功,说明生成test.exe,链接的时候,根本不需要lib2,
        也就是说,静态库会被链接到动态库或者exe中,但是不会链接到其他的lib中】
    运行Test的时候,需要dll1,但是不需要lib2
    也就是说,lib2合并到dll1中,等到exe的时候,不需要合并dll1,运行时需要dll1。
4、Test->dll1->dll2
    dll1编译自己的代码,对dll2的部分,需要dll2的头文件和lib文件,对dll2的代码实现,使用占位符关联。
    生成Test链接的时候,Test需要dll1的lib,dll1需要dll2的lib,但是代码实现都不会包含在Test中。
    运行Test的时候,需要dll1和dll2【不需要他们的lib】
    也就是说,dll2不合并到dll1中,等到exe的时候,二者都不需要合并,运行时需要二者。
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.