Адресация переменных (или что генерирует ML64?)
У меня есть файл ASM, написанный для X64. Это обеспечивает функцию листа. Файл был собран с MASM64/ML64.
C-псевдо-код с подписью:
int GenerateBlock(byte* ptr, size_t size, unsigned int safety)
{
if (ptr == NUL) return 0; /*FAIL*/
...
}
Вот та же часть кода ASM:
;; RCX (in): byte* buffer
;; RDX (in): size_t bsize
;; R8d (in): unsigned int safety
;; RAX (out): bool, success (1), failure (0)
ASM_GenerateBlock PROC buffer:QWORD,bsize:QWORD,safety:DWORD
LOCAL val:QWORD ;; local variables
MWSIZE EQU 8 ;; machine word size
;; Validate pointer
cmp buffer, 0
je ASM_GenerateBlock_Cleanup
;; Cleanup will set RAX to 0 and return
...
ENDP
Когда я перехожу на звонок, появляется fastcall
используется, что согласуется с документами. Первые два аргумента появляются в RCX
а также RDX
, что также соответствует.
Но контрольный пример с NULL
Указатель дает неожиданные результаты. Вот тестовый пример, который используется:
ASM_GenerateBlock(NULL /*ptr*/, 64 /*size*/, 20 /*safety*/);
Когда я делаю код, RCX
по-видимому buffer
(его NULL
), RDX
по-видимому bsize
(его 0x40
), но сравнить cmp buffer, 0
происходит против значения, которое мне неизвестно. Из ближайшего окна:
buffer
0x000000013f82bcd0
*(__int64*)buffer
0x000000013f62aba8
bsize
0x000000013f14871b
*(__int64*)bsize
0xccccccc348c48348
13f82bcd0
выглядит примерно как адрес указателя инструкции (EIP 13F50D268
). Это не похоже на ESP
или же EBP
,
У меня есть несколько вопросов...
- Какой режим адресации использует ML64 для переменной
buffer
? - Где значение переменной
buffer
приходящий из? - Почему
ML64
не используяECX
для переменнойbuffer
? - Как я могу это исправить?
Тот же код, сокращенный до 32-разрядных, прекрасно собирается и выполняется. Тем не менее, ML ставит buffer
а также bsize
в стеке и адресует их относительно EBP
,
Я также пытался перейти на cmp QWORD PTR buffer, 0
, но это не помогло.
2 ответа
Из разборки в финальном скриншоте,
cmp buffer, 0
собирается в
cmp qword ptr [buffer], 0 # memory operand. rip-relative? or stack-relative? Not enough insn bytes for an absolute 32bit address
вместо
cmp RCX, 0
Таким образом, используемый вами синтаксис сборки все еще объявляет buffer
как символ или смещение памяти или что-то, а не как псевдоним для регистра. И да, x86-64 Windows ABI использует соглашение о вызовах регистра (к сожалению, отличающееся от Linux). Я думаю, что это похоже на 32-битный fastcall
ABI. У Агнера Фога есть документ, объясняющий различные соглашения о вызовах для 32- и 64-битных ОС.
Обратите внимание, что cmp с немедленным нулем почти всегда является худшим выбором, чем test rcx, rcx
, Более короткое кодирование insn, и все же макро-плавкие предохранители со следующим jcc
на Intel и AMD.
Как я могу это исправить?
Я не могу ответить на некоторые вопросы, но я знаю, как это исправить. Ниже и базовым требованием является одинаковая работа с исходным кодом для X86 (MASM/ML) и X64 (MASM64/ML64) с минимальными изменениями.
Вот оригинальная сигнатура функции C:
int GenerateBlock(byte* ptr, size_t size, unsigned int safety)
В X86 MASM используется относительная адресация, и код ASM будет выглядеть следующим образом:
;; Base relative (in): byte* buffer
;; Base relative (in): size_t bsize
;; Base relative (in): unsigned int safety
ASM_GenerateBlock PROC buffer:DWORD,bsize:DWORD,safety:DWORD
LOCAL val:DWORD ;; local variables
MWSIZE EQU 4 ;; machine word size
;; Validate pointer
cmp buffer, 0
je MSC_ASM_GenerateBlock_Cleanup
...
;; Write byte to buffer from AL
mov BYTE PTR [buffer], al
inc buffer
...
Для X64 с fastcall
требуется несколько незначительных взломов:
;; RCX (in): byte* buffer
;; RDX (in): size_t bsize
;; R8d (in): unsigned int safety
ASM_GenerateBlock PROC bufferX:QWORD,bsizeX:QWORD,safetyX:DWORD
LOCAL val:QWORD ;; local variables
MWSIZE EQU 8 ;; machine word size
;; Fastcall workaround
buffer EQU ecx
bsize EQU edx
safety EQU r8d
;; Validate pointer
cmp buffer, 0
je MSC_ASM_GenerateBlock_Cleanup
...
;; Write byte to buffer from AL
mov BYTE PTR [buffer], al
inc buffer
...
Выше произошли два исправления. Сначала были имена переменных в прототипе процедуры. Buffer
был изменен на bufferX
и т. д. Второе EQU
был использован как C-язык #define
приравнять buffer
в ecx
,