Адресация переменных (или что генерирует 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,

Другие вопросы по тегам