tlink是turbo c 2.0中的链接器,可以将编译生成的obj文件和lib文件进行链接,生成可执行文件exe,查看tlink的使用帮助:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
D:\c>tlink
Turbo Link  Version 2.0  Copyright (c) 1987, 1988 Borland International
Syntax:  TLINK objfiles, exefile, mapfile, libfiles
@xxxx indicates use response file xxxx
Options: /m = map file with publics
         /x = no map file at all
         /i = initialize all segments
         /l = include source line numbers
         /s = detailed map of segments
         /n = no default libraries
         /d = warn if duplicate symbols in libraries
         /c = lower case significant in symbols
         /3 = enable 32-bit processing
         /v = include full symbolic debug information
         /e = ignore Extended Dictionary
         /t = create COM file

编写了如下代码:

1
2
3
4
main()
{
    printf("hello world!");
}

首先,使用编译器tcc.exe编译生成obj文件:

1
tcc -c a.c

然后,将需要用到的库文件cs.obj、c0s.lib、maths.lib、emu.lib拷贝到同一文件夹下,用tlink.exe链接obj和需要的lib文件。

1
2
D:\c>tlink C.OBJ c0s.obj , c.exe , c.map , maths.lib emu.lib cs.lib
Turbo Link  Version 2.0  Copyright (c) 1987, 1988 Borland International

链接生成了可执行文件exe,运行结果:

1
2
3
D:\c>C.EXE
hello world!
Null pointer assignment

问题

为什么会输出“Null pointer assignment”呢?

调试

使用turbo debug加载c.exe,反汇编单步调试。

跟踪到产生“Null pointer assignment”的代码:

tlink

前一页代码如下:

tlink

运行到上图红框所示代码时,数据段内容:

tlink

通过求数据段0000-002f中的字节内容之和,决定是否call 01E3,该子程序代码:

tlink

在tlink链接生成的exe中,显然调用了上述代码。

查看8086CPU中断大全,功能号为40h的int 21h中断——向设备或文件写制定数目的字节,接收的参数——bx=文件句柄,ds:dx指向缓冲区,cx=要写的字节数,返回——如果进位标志DF设置,ax=错误代码,否则ax=写入的字节数。

在此处的具体作用是:将数据段中003f开始的19h个字节写入屏幕,并且cld清除进位标志,单步跟踪得到ax=19h,也就是“Null pointer assignemt ”。

之后,代码运行至int 21h的4ch号中断,程序结束。

分析

为什么会输出“Null pointer assignment”呢?从上面的调试可以看到字符串“hello world!”插入到了整个数据段之前,改写了本应该是程序版本信息的位置,所以输出“Null pointer assignment”。考虑到进入程序后,最先执行c0s.obj中的代码,可能的原因是obj文件的调用顺序出错,调换c0s.obj和c.obj的顺序,正确的tlink语句顺序是:

1
2
D:\c>tlink c0s.obj C.OBJ , c.exe , c.map , maths.lib emu.lib cs.lib
Turbo Link  Version 2.0  Copyright (c) 1987, 1988 Borland International

在此运行生成的exe文件,结果正确:

tlink

拓展

进入程序后最先执行c0s.obj中的代码,所以c0s.obj放到最前头。tlink连接obj文件的顺序决定了代码在程序中的位置,而对于lib文件中函数的调用顺序肯定也有要求,这有待进一步探究。