所以我遇到了一个非常奇怪和具体的问题。我们将看似有效的代码放入链接器,然后链接器会犯一些错误,比如删除一个有效的ptr标签,而不是用值替换它,而是放入0。但也不是所有地方。它从一个相当武断的点开始。
因此,有一些背景:我们有一种解释语言,通过将手工生成的汇编块(由内部类似编译器的应用程序)放在一起,并在需要时添加变量,将其转换为汇编。这是一个已经运行了大约10年甚至更长时间的系统,基本上是目前的形式,所以这种方法的有效性目前没有问题。此程序集使用Microsoft汇编程序(ml.exe或MASM)汇编为obj文件。
然后在另一个步骤中,使用Microsoft增量链接器将此obj文件与其他一些库(dll的静态库和导入库)链接在一起,以创建可执行文件。
以下是汇编程序在第一步创建obj文件时输出的汇编程序(.asm)文件的一部分:
call _c_rt_strcmp
mov di, 1
mov ebp, esp
cmp ax, 0
je sym2148
dec di
sym2148: mov [ebp+6], di
add esp, 6
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 0bb49h
pop ax
mov byte ptr [ebx],al
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 012656h
push ebx
mov eax, OFFSET sym2151
push eax
call _c_rt_strcmp
mov di, 1
mov ebp, esp
cmp ax, 0
je sym2152
dec di
sym2152: mov [ebp+6], di
add esp, 6
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 0bb32h
pop ax
mov byte ptr [ebx],al
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 012656h
push ebx
mov eax, OFFSET sym2155
push eax
call _c_rt_strcmp
mov di, 1
mov ebp, esp
cmp ax, 0
je sym2156
dec di
sym2156: mov [ebp+6], di
add esp, 6
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 0bb4ah
pop ax
mov byte ptr [ebx],al
mov ebx, dword ptr [_smfv1_ptr]
add ebx, 0126bbh
push ebx
mov eax, OFFSET sym2159
push eax
call _c_rt_strcmp
在我看来,这是正确的,也是有道理的。我相信生成这个的代码会进行字符串比较,然后根据字符串比较的结果存储一个值,这是一个由20-30个组组成的部分,所以在我发布的第一个比特之前有10个左右的组,在我发布最后一个比特之后大约有15-20个。
以下是Visual C++5.0(我知道以前的版本,但不幸的是,这是在遗留系统上)在可执行文件崩溃时显示的崩溃位置的分解视图:
004093D2 call 00629570
004093D7 mov di,1
004093DB mov ebp,esp
004093DD cmp ax,0
004093E1 je 004093E5
004093E3 dec di
004093E5 mov word ptr [ebp+6],di
004093E9 add esp,6
004093EC mov ebx,dword ptr ds:[64174Ch]
004093F2 add ebx,0BB49h
004093F8 pop ax
004093FA mov byte ptr [ebx],al
004093FC mov ebx,dword ptr ds:[64174Ch]
00409402 add ebx,12656h
00409408 push ebx
00409409 mov eax,0
0040940E push eax
0040940F call 00409414
00409414 mov di,1
00409418 mov ebp,esp
0040941A cmp ax,0
0040941E je 00409422
00409420 dec di
00409422 mov word ptr [ebp+6],di
00409426 add esp,6
00409429 mov ebx,dword ptr ds:[0]
0040942F add ebx,0BB32h
00409435 pop ax
00409437 mov byte ptr [ebx],al
00409439 mov ebx,dword ptr ds:[0]
0040943F add ebx,12656h
00409445 push ebx
00409446 mov eax,0
0040944B push eax
0040944C call 00409451
00409451 mov di,1
00409455 mov ebp,esp
00409457 cmp ax,0
0040945B je 0040945F
0040945D dec di
0040945F mov word ptr [ebp+6],di
00409463 add esp,6
00409466 mov ebx,dword ptr ds:[0]
0040946C add ebx,0BB4Ah
00409472 pop ax
00409474 mov byte ptr [ebx],al
00409476 mov ebx,dword ptr ds:[0]
0040947C add ebx,126BBh
00409482 push ebx
00409483 mov eax,0
00409488 push eax
00409489 call 0040948E
实际崩溃位置为0x00409429。
这两个代码位匹配,因为它们是同一段代码,除了.asm文件中的第一个是进入链接器的内容,第二个是从链接器中出来的内容。
据我所见,它在那个位置崩溃是因为它试图取消引用0的地址,对吧?所以这当然会失败。此外,如果你看一下0x004093D2、0x0040940F、0x0040944C和0x00409489的不同函数调用,只有第一个是有效的,其他的只是直接对它们后面的行进行函数调用!在该文件中定义的接下来的15-20个类似的代码段中,这个中断的代码将继续。
如果你查看这两个部分中函数调用和坏指针的对应行,你会发现在.asm文件中,一切似乎都是正确的,但不知何故,链接器在编译exe时只是在一个非常特定的地方破坏了它,因为文件中以前有类似的代码块,它们都是正确构建的。
我们确实收到了一些链接器警告,格式为:"LINK:warning LNK4049:local-defined symbol"_smfv1_ptr"imported"。但即使在它起作用的时候,我们也收到了同样的警告。
使用的汇编程序是ml.exe版本6.11,链接器是link.exe版本5.10.7303,这两个版本都是Visual C++5.0的版本。由于汇编的代码似乎是正确的,我将尝试VisualStudio200520082010中的链接器,看看是否有任何变化。
我真的无法想象是什么造成了这种错误,我想可能是符号搞砸了,但有一些位置(对于小的"if"语句)被存储为符号,在它们通过链接器后仍然可以正常工作。
是否有可能在链接器中重载了符号表或类似的东西,而它只是恢复到坏值或默认值?
对以下地址的调用是未解析符号引用的明确标志(如果您注意到.obj文件中的所有调用都是以E8 00 00 00 00发出的,则这是有意义的)。一些数据引用也有零(sym2151,一些对_smfv1_ptr的引用,sym2159)。奇怪的是,对_c_rt_strcmp的第一个调用确实得到了解决。我建议打开所有可以找到的警告/调试/详细开关,并生成各种列表和映射文件。也许会有什么东西跳出来。
好的,所以最终结果似乎是它是Visual C++版本的masm汇编程序"ml.exe"的一个错误(大惊喜,是吗?)
因此,转移到VisualStudio2005中提供的masm和链接版本似乎是我们最好的解决方案
感谢你们的帮助:)