obj文件的连接问题以及tlib的基本用法
1、基础研究

用tcc将程序编译为.obj文件。


这里也可以使用tcc -linclude run.c来将run.c文件编译成run.obj文件。
再用tcc对下面的程序进行编译链接,发现提示错误:


提示标志f在程序中未定义。这说明如果程序里出现未定义的变量或函数,编译器能够正常将原文件编译成.obj文件,只是会提示而已。
要怎么生成正确的exe文件呢,我们要把run1.c中未定义的f函数链接进来,但是我们之前的链接都是链接的系统提供的相关文件,怎么链接自带文件呢?我们先研究一下tlib.exe。
tlib.exe使用方法如下;

即使用tlib的格式为tlib libname [/C] [/E] commands, listfile
libname:要建立的用户目标模块库,缺省的扩展名为.LIB
/C:大小写敏感标志。该选项不常用。
/E:建立扩展字典。建立扩展字典可以加速大的库文件的连接过程。
commands: 操作列表,由若干个动作符以及每个动作符后面的文件名或模块名组成。TILB支持的动作符有5种:“+"、“-"、“*"、“-*"或“*-"、“-+"或“+-"。“+"是把指定的文件加到指定的库中; “-"从库中删除指定的模块;“*"将相应的模块从库中抽取并写到指定的文件中,原库不变;“-*"或“*-"是将库中指定的模块拷贝到指定的文件中,然后把该模块从库中删除;“-+"或“+-"是将指定的模块用指定的文件或模块代替。
Listfile:建立列表文件。列表文件按字母顺序将库中各模块列表,为文本文件,可用DOS的TYPE命令查看。
那么将含有f函数的run.obj添加到cs.lib里的语句如下:

之后用tcc编译链接文件run1.c,没有出现错误提示。用debug加载生成的exe文件:
这是main函数的代码
这是f函数的代码
所以,我们将函数f写在程序run.c中,编译成run.obj,再将run.obj用tlib链接入cs.lib,这样tcc编译时发现原文件中没有定义函数f,就会在c语言默认的函数库cs.lib中寻找,找到后将其链接,生成run1.exe文件,所以我们在run1.exe文件中可以看到函数f的代码,它是在tlink连接时加入的。
用tlib cs.lib cs.txt可以在cs.txt或者cs.lst文件中导入cs.lib的函数的目录。

将此程序编译成f.obj,并加入cs.lib中。


将上面的程序编译链接成b.exe,用debug加载:
Main函数的内容为:


f1函数的内容为:

f2函数的内容为:

函数func的内容为;

程序b.c中并没有写f1、f2和printf函数,这些函数的代码同样是在连接的时候加入的。
b.exe中有f3的代码,因为f3和f1、f2一起被加入了cs.lib中,而cs.lib被连接入了b.exe,所以b.exe含有它的全部函数,只是main函数中只调用了f1、f2、printf函数而已。

函数f3的代码紧接着函数f2的代码,地址为1056.
那么有没有一种方案,使得在编译连接时能够动态地装入函数的代码,而不是将库函数全部装入exe文件呢?
我们看题目要求的是f.c中的三个函数要装入cs.lib,在编译连接时动态载入代码。但是如果我们用tcc原来的连接方式,就会把cs.lib整个载入代码中。那应该怎么样才不会出现这种情况呢?我觉得应该是改变tlink的连接方式,查看tlink连接选项:

我用tcc将b.c编译成obj文件,再用tlink b.obj/n进行连接,结果出现如下错误:

很显然是没有连接cs.lib所致,但是为什么先用tcc编译再用tlink连接会出错呢?
经过查找资料和实验,tlink连接obj文件生成exe文件的正确指令如下:

第一个意思是用小模式连接b.obj,第二个是指生成的目标文件是b.exe,第三个是指没有使用到映像文件,第四个是指连接需要用到的库文件有cs.lib、emu.lib、maths.lib。
函数f3包含在f.obj里,而后者被载入了cs.lib文件,cs.lib在连接时被载入b.exe文件,那么是不是cs.lib里的所有函数都载入了b.exe文件呢?查找资料有这么一段话:
不会。当启动连接程序时,它会寻找“未定义的外部函数”,也就是说,它将在每一个库文件中查找源代码文件中未定义的函数。当它找到一个未定义的外部函数后,它会引入包含该函数定义的目标代码(obj)。不幸的是,如果这个函数是在一个包含其它函数定义的源文件中被编译的话,那么这些函数也会被包含进来,你的可执行代码中将包含一些不需要的代码。因此,将库函数放到各自的源文件中是很重要的——否则会浪费宝贵的程序空间。有些编译程序包含特殊的“精明的”连接程序,这些连接程序能查出不需要的函数并去掉它们,从而使这些函数不再进入你的程序。
很显然tlink并不是上面资料里所说的“精明的”连接程序,它会将和源代码中未定义函数一起编译的所有函数都载入exe文件中,所以f3会被载入b.exe中。
那么我们的问题就很好解决了,我们可以将f1、f2函数写在一个程序里编译成obj文件,将f3写在一个程序里编译成obj文件,再将这两个文件加入cs.lib中,然后进行下面的连接,b.exe文件中就不会出现f3函数的代码了。实验结果发现,原来紧跟在f2的代码后面的f3的代码现在没有了:

所以其实是我之前的思路重点错了,只觉得函数f1、f2、f3都在cs.lib里面不管怎么放都是一样的,但是其实它们编译的方法不同,在cs.lib里面存放的位置或机制也应该不同,我们思考时,应该更加全面地想问题。
用下面的函数替换cs.lib里的printf函数:

将函数编译成obj文件,再用如下语句替换:

这样再使用printf函数就会输出“Do you want to use printf?No printf here.”
如下图程序:

在正常情况下应该输出3,但是用更改后的cs.lib连接后,输出结果如下:

这时printf函数是一个不接受参数、只输出固定语句的函数。
2、拓展研究
1、我们在本章研究里是把要添加的obj文件插入cs.lib中再连接,那么能否自己建立lib库,并使tcc编译时对它进行连接呢?
2、我们知道cs.lib里在被连接时是将源文件中未被定义的函数及其一起编译的函数全部加入生成的exe文件中,那么cs.lib里的其他文件是怎么编译的?都是单独编译的吗?
3、Printf和put函数有什么区别?
4、obj文件给出的是偏移地址,exe文件给出了段地址和偏移地址?
5、include头文件在预编译过程中把其他文件合成一个文件。尝试#include<f.c>也是可以正确运行的,看汇编代码这种和加入cs.lib有什么不一样。
6、加入obj文件后之后cs.lib的大小减小了,这是为什么?
3、研究总结
本章研究了obj文件的连接问题,掌握了将obj文件加入cs.lib从而连接进文件的方法,熟悉了tlib.exe的基本用法。
obj文件的连接问题以及tlib的基本用法的更多相关文章
- delphi 连接 c++ builder 生成obj文件
delphi 连接 c++ builder 生成obj文件 delphi 可以连接c++ builder 生成OMF格式的obj文件,会报一个错.[DCC Error] E2065 Unsatisfi ...
- 目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包
前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”) ...
- OBJ文件
OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模 ...
- 工作总结 1 sql写法 insert into select from 2 vs中 obj文件和bin文件 3 npoi 模板copy CopySheet 最好先全部Copy完后 再根据生成sheet写数据 4 sheet.CopyRow(rowsindex, rowsindex + x); 5 npoi 复制模板如果出现单元格显示问题
我们可以从一个表中复制所有的列插入到另一个已存在的表中: INSERT INTO table2SELECT * FROM table1; 或者我们可以只复制希望的列插入到另一个已存在的表中: INSE ...
- 关于bin和obj文件夹。debug 和release的区别(转)
关于bin和obj文件夹. 楼主hcaihao(影子男孩)2002-05-29 20:04:24 在 .NET技术 / C# 提问 VS.Net会生成bin和obj文件夹以及它们下面的Debug和Re ...
- 记一次C++编程引用obj文件作为静态库文件
简介 常用静态库文件的名字一般是 ***.lib ,例如 nisyscfg.lib 就是一个静态库文件,但是一个例程居然是引用 **.obj 文件作为静态库,有点非常规啊. 这是一个NI488.2 的 ...
- CSharpGL(9)解析OBJ文件并用CSharpGL渲染
CSharpGL(9)解析OBJ文件并用CSharpGL渲染 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo ...
- [计算机图形学] OpenGL读取obj文件并显示其3D效果
读取三维网格模型(Wavefront OBJ文件) 无法向立方体:cube.obj 有法向兔子模型:bunny.obj 有法向有纹理八字模型:Eight.obj OBJ文件的格式可参考:http: ...
- 通过udl文件得到连接字符串
1.新建一个文件,文件名任意,扩展名为udl 2.双击打开这个udl文件 3.点下一步: 4.测试连接成功后点击确定 5.用记事本打开这个udl文件: 连接字符串就出来了
随机推荐
- C++ STL之string常用指令
string,大小可变的字符串,有些类似于C中的字符数组. 只记载本人在ACM中常用的函数,并且全部经过程序测试. 1.初始化 string s1;——默认构造函数s1为空串 string s2(s1 ...
- Hbase 设计与开发实战
Hbase 概述 大数据及 NoSQL 的前世今生 传统的关系型数据库处理方式是基于全面的 ACID 保证,遵循 SQL92 的标准表设计模式(范式)和数据类型,基于 SQL 语言的 DML 数据交互 ...
- WKWebview点击图片查看大图
大家都知道,WKWebview是没有查看大图的属性或者方法的,所以只能通过js与之交互来实现这一功能,原理:通过js获取页面的图片,把它存放到数组,给图片添加点击事件,通过index显示大图就行了 其 ...
- java的 IO流之缓冲流(转载)
java缓冲流本身不具IO功能,只是在别的流上加上缓冲提高效率,像是为别的流装上一种包装.当对文件或其他目标频繁读写或操作效率低,效能差.这时使用缓冲流能够更高效的读写信息.因为缓冲流先将数据缓存起来 ...
- [Angular 2] Managing State in RxJS with StartWith and Scan
The scan operator in RxJS is the main key to managing values and states in your stream. Scan behaves ...
- Java基础知识强化79:被遗忘的Java Math类
1. Math类概述 Math类包含用于执行基本数学运算的方法,如初等指数.对数.平方根和三角函数. 2. 成员变量 和 成员方法(常用的) (1)成员变量 public static final d ...
- [转] JS nodeType返回类型
将HTML DOM中几个容易常用的属性做下记录: nodeName.nodeValue 以及 nodeType 包含有关于节点的信息. nodeName 属性含有某个节点的名称. 元素节点的 node ...
- Linux下multipath多路径配置
一.什么是多路径 普通的电脑主机都是一个硬盘挂接到一个总线上,这里是一对一的关系.而到了有光纤组成的SAN环境,或者由iSCSI组成的IPSAN环境,由于主机和存 储通过了光纤交换机或者多块网卡及IP ...
- [CSAPP笔记][第九章虚拟存储器][吐血1500行]
9.虚拟存储器 为了更加有效地管理存储器且少出错,现代系统提供了对主存的抽象概念,叫做虚拟存储器(VM). 虚拟存储器是硬件异常,硬件地址翻译,主存,磁盘文件和内核软件的完美交互. 为每个进程提供一个 ...
- 利用PCA来简化数据
13.2.2 在NUmpy中实现PCA 将数据转换成前N个主成分的伪代码大致如下: 去除平均值 计算协方差矩阵 计算协方差矩阵的特征值和特征向量 将特征值从大到小排列 保留最上面的N个特征向量 将数据 ...