Так что у меня тут очень странная и специфическая проблема. Мы помещаем, казалось бы, действительный код в компоновщик, а затем компоновщик делает некоторые ошибки, такие как удаление действительной метки 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), которые сохраняются как символы, которые все еще работают нормально после того, как они пройдут через компоновщик.
Возможно ли, что таблица символов или что-то подобное перегружается внутри компоновщика, и он просто возвращается к неправильным значениям или значениям по умолчанию?