用无效代码替换有效代码的链接器



所以我遇到了一个非常奇怪和具体的问题。我们将看似有效的代码放入链接器,然后链接器会犯一些错误,比如删除一个有效的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和链接版本似乎是我们最好的解决方案

感谢你们的帮助:)

最新更新