Линкер заменяет действительный код недействительным кодом - PullRequest
0 голосов
/ 31 января 2012

Так что у меня тут очень странная и специфическая проблема. Мы помещаем, казалось бы, действительный код в компоновщик, а затем компоновщик делает некоторые ошибки, такие как удаление действительной метки ptr и вместо замены ее значением, вместо этого он вставляет 0. Но и не везде. Это начинается в довольно произвольной точке.

Итак, немного предыстории: у нас есть интерпретируемый язык, который преобразуется в ассемблер путем объединения созданных вручную кусков ассемблера (с помощью собственного приложения, подобного компилятору) и добавления переменных, где это необходимо. Это система, которая работает около 10 лет, если не дольше, в значительной степени в ее нынешнем виде, поэтому в настоящее время вопрос о пригодности этого метода не ставится под сомнение. Эта сборка собирается в файл obj с помощью Microsoft Assembler (ml.exe или MASM).

Затем на отдельном шаге этот файл obj связывается с помощью Microsoft Incremental Linker вместе с некоторыми другими библиотеками (статическими и библиотеками импорта для dll) для создания исполняемого файла.

Ниже приведена часть файла сборки (.asm), которую ассемблер выводит при создании файла obj на первом шаге:

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 все выглядит правильно, но каким-то образом компоновщик просто разбивает все это, когда компилирует исполняемый файл. и в очень конкретном месте, поскольку в файле есть аналогичные фрагменты кода, которые были сконструированы правильно.

Мы получаем некоторые предупреждения компоновщика в виде: «LINK: предупреждение LNK4049: локально определенный символ« _smfv1_ptr »импортирован». Но мы получали те же предупреждения, даже когда это работало.

Используемый ассемблер был ml.exe версии 6.11, а компоновщик был link.exe версии 5.10.7303, обе версии были из Visual C ++ 5.0. Поскольку собранный код кажется правильным, я собираюсь попробовать компоновщик из Visual Studio 2005, 2008 и 2010 и посмотреть, изменится ли что-нибудь.

Я не могу себе представить, что может создать такого рода ошибки, и я подумал, что, возможно, это были испорченные символы, но есть переходы к местам (для небольших операторов if), которые сохраняются как символы, которые все еще работают нормально после того, как они пройдут через компоновщик.

Возможно ли, что таблица символов или что-то подобное перегружается внутри компоновщика, и он просто возвращается к неправильным значениям или значениям по умолчанию?

Ответы [ 2 ]

1 голос
/ 31 января 2012

Вызов по следующему адресу является явным признаком неразрешенной ссылки на символ (это имеет смысл, если вы заметите, что все вызовы в файлах .obj отправляются как E8 00 00 00 00).У вас также есть нули для некоторых ссылок на данные (sym2151, некоторые ссылки на _smfv1_ptr, sym2159).Странно то, что первый вызов _c_rt_strcmp действительно был решен.Я бы предложил включить все переключатели предупреждения / отладки / многословия, которые вы можете найти, и генерировать все виды файлов списков и карт.Может быть, что-то выскочит.

0 голосов
/ 02 февраля 2012

Хорошо, так что в итоге получается, что это ошибка с версией Visual C ++ ассемблера masm "ml.exe" (большой сюрприз, да?)

Итак, переход на версии masm и link, представленные в Visual Studio 2005, представляется нам лучшим решением.

Спасибо за вашу помощь, ребята:)

...