用实例程序再聊makefile指定头文件和库出错的那点破事

时间:2021-06-05 12:43:09

        在文章http://blog.csdn.net/stpeace/article/details/50985578中, 我说过makefile指定头文件和库文件出错的那点破事, 今天破事重提, 用实际例子来聊一下.

taoge@localhost Desktop> cat basic_add.c
int basic_add(int x, int y)
{
    return x + y;
}
taoge@localhost Desktop> cat taoge_add.c 
int basic_add(int x, int y);

int add(int x, int y)
{
    basic_add(x, y);
}

taoge@localhost Desktop> cat main.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

typedef int (*pFUN)(int, int); // 函数指针类型

int main()
{
    void *pHandle = NULL;
    char *pError = NULL;
    pFUN pfun = NULL;

    pHandle = dlopen("./libtaoge.so", RTLD_NOW); // 打开动态链接库
    if (NULL == pHandle) 
        {
            printf("dlopen:%s\n", dlerror());
            return -1;
    }

    pfun = dlsym(pHandle, "add");
    if ((pError = dlerror()) != NULL)  // 这里不要直接用pfun和NULL比较
        {
            printf("dlsym:%s\n", pError);
            return -2;
    }

    printf("sum is : %d\n", (*pfun)(1, 2));

    dlclose(pHandle);
        return 0;
}
       我们分别来看看之前说的三个问题(之前那篇文章说过的):

      1. 如果在代码中包含了某头文件test.h,  但在makefle中没有指定头文件路径/或者头文件根本不存在, 会出现编译错误, 会提示缺少test.h.
      2. 如果包含了test.h头文件,且指定了头文件路径,  且指定libtest.a库, 但实际此库并不存在(比如没有提前编译出来), 会出现编译错误, 提示缺少 cannot find -ltest
      3.如果包含了头文件,且指定了头文件路径,  但makefile没有指定libtest.a库, 编译的时候不会出问题, 但运行的时候会出问题: dlopen ... failed,  undefined symbol ...


      分别来看一下(因程序比较简单, 我就不用makefile了):

       1. 缺少头文件或者没有指定头文件, 肯定不行啊。这个比较好理解, 但我们本文中没有涉及到自己的头文件, 所以就不多说了。 

       2. 我们来看看:

taoge@localhost Desktop> gcc -c basic_add.c 
taoge@localhost Desktop> ar rcs libBasicAdd.a basic_add.o
taoge@localhost Desktop> rm libBasicAdd.a 
taoge@localhost Desktop> gcc -shared -fPIC taoge_add.c -o libtaoge.so -L./ -lBasicAdd
/usr/lib/gcc/i586-suse-linux/4.1.2/../../../../i586-suse-linux/bin/ld: cannot find -lBasicAdd
collect2: ld returned 1 exit status
       果然如此。

      3. 我们俩看看:

taoge@localhost Desktop> gcc -c basic_add.c 
taoge@localhost Desktop> ar rcs libBasicAdd.a basic_add.o
taoge@localhost Desktop> gcc -shared -fPIC taoge_add.c -o libtaoge.so
taoge@localhost Desktop> gcc main.c -o a.out -ldl
taoge@localhost Desktop> ./a.out 
dlopen:./libtaoge.so: undefined symbol: basic_add
      oh my god, 在运行期间出错了, 那有办法在编译期提前发现吗? 有的, 用ldd, nm, readelf, objdump (当然用ldd -r最简单啦!)

taoge@localhost Desktop> ldd -r libtaoge.so 
undefined symbol: basic_add     (./libtaoge.so)
        linux-gate.so.1 =>  (0xbfffe000)
        /lib/libonion.so (0xb7f99000)
        libc.so.6 => /lib/libc.so.6 (0xb7e4f000)
        libdl.so.2 => /lib/libdl.so.2 (0xb7e4a000)
        libpthread.so.0 => /lib/libpthread.so.0 (0xb7e33000)
        /lib/ld-linux.so.2 (0x80000000)
taoge@localhost Desktop> nm -u libtaoge.so 
         U basic_add
         w __cxa_finalize@@GLIBC_2.1.3
         w __gmon_start__
         w _Jv_RegisterClasses
taoge@localhost Desktop> readelf -a libtaoge.so | grep UND
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
    12: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND basic_add
    15: 00000000   231 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.1.3 (2)
    18: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    19: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
    55: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND basic_add
    58: 00000000   231 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.1
    61: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    62: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
taoge@localhost Desktop> objdump -x libtaoge.so | grep UND
00000000         *UND*  00000000              basic_add
00000000  w    F *UND*  000000e7              __cxa_finalize@@GLIBC_2.1.3
00000000  w      *UND*  00000000              _Jv_RegisterClasses
00000000  w      *UND*  00000000              __gmon_start__
        可以看到, basic_add没有定义!!!


        如上问题的解决方法, 我们早都说过了, 也就不赘述了。