背景

在GCC中已经指定链接库,然而编译时却提示动态库函数未定义!

测试出现的错误提示如下:

[GMPY@13:48 tmp]$gcc -o test -L. -lmylib test.c
/tmp/ccysQZI3.o:在函数‘main’中:
test.c:(.text+0x1a):对‘func_lib’未定义的引用
collect2: error: ld returned 1 exit status

而在测试用的动态库libmylib.so中是有定义函数func_lib

[GMPY@13:55 tmp]$cat mylib.c
#include <stdio.h> int func_lib(void)
{
printf("In share library\n");
return 0;
}
[GMPY@13:56 tmp]$gcc -fPIC -shared mylib.c -o libmylib.so

GCC的链接坑

此处的"坑"指对不熟悉GCC机制的童鞋而言,会出现无法理解的不符合预期的效果

在用gcc编译时,我们可以用-L指定链接库位置,用-l指定。

man gcc查询时,我发现这么一段描述:

-llibrary
-l library
... ## 这里为了方便阅读,对原文进行了换行排版优化
It makes a difference where in the command you write this option;
the linker searches and processes libraries and object files in the order they are specified.
Thus, foo.o -lz bar.o searches library z after file foo.o but before bar.o.
If bar.o refers to functions in z, those functions may not be loaded.
...

嗯,这段话什么意思呢? 如果-l链接库在源码之前,就会链接不到库!!

就像下面两个命令的差别:

异常:gcc -o test -L. -lmylib test.c
正常:gcc -o test -L. test.c -lmylib

竟然对执行时参数的位置都有要求,也是醉了

GCC的链接步骤

感谢 @firstrose 提供的原理说明链接

GCC是怎么样理解-l的呢?

A library is a collection (an archive) of object files. When you add a library using the -l option,
the linker does not unconditionally take all object files from the library. It only takes those object
files that are currently needed, i.e. files that resolve some currently unresolved (pending) symbols.
After that, the linker completely forgets about that library. The list of pending symbols is continuously maintained by the linker as the linker processes input
object files, one after another from left to right. As it processes each object file, some symbols get
resolved and removed from the list, other newly discovered unresolved symbols get added to the list. So, if you included some library by using -l, the linker uses that library to resolve as many currently
pending symbols as it can, and then completely forgets about that library. If it later suddenly
discovers that it now needs some additional object file(s) from that library, the linker will not "return"
to that library to retrieve those additional object files. It is already too late.

什么个意思呢?就是说,GCC链接器按下面的步骤链接文件:

  1. 从左往右链接源文件
  2. 在链接时,如果出现源文件调用了却没有定义的函数、变量等,则记录下来
  3. 如果遇到-l指定的库,则在库中尽可能检索所有记录下来的没有定义的函数、变量,只从库中提取用到的部分,其他完全抛弃
  4. 在全部链接完后,如果依然存在未定义的函数、变量,则报错

正因为GCC链接器的"始乱终弃",在检索-l的库后,就抛弃了这个库,后面还需要用时,自然就找不到了

GCC并不会回过头来检索之前链接过的库

从这样的链接步骤,我猜测还会有个特性:

由于GCC链接库会在首先指定的库中"贪婪匹配"所有未定义的函数、变量,因此,

即使两个库有相同的函数、变量的定义,只会使用最先找到的库中的定义

GCC链接库的一个坑:动态库存在却提示未定义动态库的函数的更多相关文章

  1. node中通过orm2链接mysql的一个坑

    代码是orm上的例子,出现如下错误: ORMError: Connection protocol not supported - have you installed the database dri ...

  2. Visual Studio 安装easyX且导入graphics库后,outtextxy提示未定义标示符

    1.点击 “项目” ,然后点击 “属性”. 2. 然后点击左侧 “配置与属性” 下的 “常规” ,在点击 “字符集” ,选择 “使用多字节字符集” 即可解决问题

  3. GCC链接时库顺序问题

    GCC或G++在编译链接时,如果命令行中含有库,则要特别注意了.根据<C专家编程>5.3节中的提示,GCC在链接时对命令行时的处理顺序是从左到右.证据是GCC的MAN: -l librar ...

  4. xamarin绑定原生库的一些坑

    最近一个项目涉及到较多的第三方库的绑定技术,中间遇到了几个坑,记录下来与大家分享 绑定Jar库 monoandroid对原生库的调用都通过Android.Runtime.JNIEnv进行调入(http ...

  5. Linux gcc链接动态库出错:LIBRARY_PATH和LD_LIBRARY_PATH的区别

    昨天在自己的CentOs7.1上写makefile的时候,发现在一个C程序在编译并链接一个已生成好的lib动态库的时候出错.链接命令大概是这样的: [root@typecodes tcpmsg]# g ...

  6. gcc链接g++编译生成的静态库和动态库的makefile示例

    使用c++开发程序或者库时,将库提供给其他人使用. 然而使用者是使用c开发的程序,链接g++编译生成的库时,于链接gcc生成的库,有所不同. 首先是静态库,以链接g++编译生成的libmylib.a为 ...

  7. 目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包

    前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”) ...

  8. g++/gcc 链接头文件 库 PATH

    转自http://blog.csdn.net/kankan231/article/details/24243871 在Linux下编译链接或运行c/c++程序时可能会遇到找不到头文件,找不到库文件的错 ...

  9. 记前端状态管理库Akita中的一个坑

    记状态管理库Akita中的一个坑 Akita是什么 Akita是一种基于RxJS的状态管理模式,它采用Flux中的多个数据存储和Redux中的不可变更新的思想,以及流数据的概念,来创建可观察的数据存储 ...

随机推荐

  1. clipboard.js小说明

    github主页 clipboard.js是一个github上的开源项目,可以实现纯 JavaScript (无 Flash)的浏览器内容复制到系统剪贴板的功能. 用法 <script type ...

  2. PAT 1011-1020 题解

    早期部分代码用 Java 实现.由于 PAT 虽然支持各种语言,但只有 C/C++标程来限定时间,许多题目用 Java 读入数据就已经超时,后来转投 C/C++.浏览全部代码:请戳 本文谨代表个人思路 ...

  3. servlet-显示器

    1.什么是监听器 监听器是实现一个特定的接口java规划,该计划的目的是还调用类方法监听器.java的awt大量使用该模式,如的能力button点击事件.当鼠标点击时,就会调用事件处理程序.又如:在j ...

  4. Android--数据持久化存储概述

    Android数据持久化存储共有四种方式,分别是文件存储.SharedPreferences.Sqlite数据库和ContentProvider.在本篇幅中只介绍前面三种存储方式,因为ContentP ...

  5. Hadoop源代码分析:HDFS读取和写入数据流控制(DataTransferThrottler类别)

    DataTransferThrottler类别Datanode读取和写入数据时控制传输数据速率.这个类是线程安全的,它可以由多个线程共享. 用途是构建DataTransferThrottler对象,并 ...

  6. windows消息值全部定义,从消息值得到消息名称(系统消息定义从0到1023,从1024开始就是WM_USER,但是中间有325个WM_undefined消息,估计是备用,另外各控件都有一些reserved消息,也是为了备用)LostSpeed

    前言 在逆向算法扫描插件时, 遇到一个windows消息值在msdn中没有定义. 去查资料, 有个老外将全部windows消息值和消息名称定义都码好了:) 写个测试程序, 封装了一个接口, 从消息值得 ...

  7. python3 基本使用多线程

    #coding=utf-8 import threading #进口threading from time import sleep import time def task1(): print (& ...

  8. Fedora15下搭建QT开发环境及编译QT(提前一键安装完,qt编译所有必需库 yum install gcc-c++ libXtst-devel freetype freetype-devel fontconfig-devel libXrender-devel )

    看了不少linux上编译qt的文章,实际上直接通过yum 安装qt是最方便的,请参考<yum安装qt> 不过初步接触fedora,为了了解一下如何在linux上编译.安装开源代码,所以必须 ...

  9. WPF中Binding使用StringFormat格式化字符串方法

    原文:WPF中Binding使用StringFormat格式化字符串方法 货币格式 <TextBlock Text="{Binding Price, StringFormat={}{0 ...

  10. x86汇编指令脚本虚拟机

    简介 这是一个可以直接解释执行从ida pro里面提取出来的x86汇编代码的虚拟机. 非常精简,整体架构上不能跟那些成熟的虚拟机相比,主要目标是够用.能用.轻量就行,如果觉得代码架构设计的不是很好的话 ...