Есть ли где-нибудь список регистров, используемых различными операциями x86_64 и функциями C std lib? - PullRequest
2 голосов
/ 22 января 2020

Я начинаю изучать программирование на ассемблере x86_64 с помощью ассемблера NASM на Ubuntu linux. Одна из проблем, с которой у меня возникают проблемы, - это выяснить, какие регистры магическим образом используются операциями.

В книге, которую я читаю, есть примеры кода:

mov    rdi, fmt1
mov    rsi, strng
mov    rax, 0
call   printf

; How am I supposed to know which registers are used by the call to printf? 
; The libc printf function supports an arbitrary number of parameters. 
; Clearly there aren't an unlimited number of registers in x86_64 so how does this work
; as the parameter list grows?

И другая часть примера кода выглядит так:

xor    rax, rax
mov    rbx, strng
mov    rcx, strLen
mov    r12, 0
pushLoop:
    mov    al, byte[rbx + r12]
    push   rax
    inc    r12
    loop   pushLoop
; It took me a few seconds to find out where the exit condition is. I realized that
; rcx is being compared to r12 in some way, but I'm not sure how. Is it explained anywhere?

Я не уверен, где мне искать ответ на мой первый вопрос. Я догадываюсь, что ответ на мой второй вопрос находится где-то в документации NASM, но я не уверен, где его найти. Я пытаюсь связать эти конструкции с тем, что я знаю на языках высокого уровня, но я изо всех сил.

Спасибо!

Ответы [ 2 ]

4 голосов
/ 22 января 2020

Первая часть: все библиотечные функции соответствуют стандартному соглашению о вызовах . На всех платформах x86-64, кроме Windows, это x86-64 System V ABI.

Вы можете создавать свои собственные соглашения при написании собственных функций asm, например, возвращая несколько различных значения в нескольких регистрах вместо того, чтобы ограничивать себя только тем, что вы могли бы сделать компилятором C.

(например, вы могли бы написать memcmp, который возвращает позицию первой разницы в RDI и фактическую <= или> во FLAGS, например, от выполнения cmp для несовпадающих байтов.)

Но функции, сгенерированные компилятором, которые вы можете вызывать из asm (включая C стандартные функции библиотеки), всегда будут следовать ABI .


Вторая часть: неявное использование регистров некоторыми инструкциями : обратитесь к руководству по ISA для получения соответствующих инструкций. Если вы этого не знаете, не просто предполагайте по названию.

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

Посмотрите инструкции в руководстве Intel vol.2 (или аналоге AMD). например, HTML извлечение PDF-файла Intel по адресу https://www.felixcloutier.com/x86/, в частности запись для loop. Также Как именно работает инструкция x86 L OOP? объясняет, что это похоже на dec rcx / jnz, за исключением того, что без установки FLAGS.

Не так много инструкций с неявными операндами. Наиболее часто используемыми являются инструкции стека, такие как push / pop, неявно использующие RSP очевидным образом.

Другие известные включают E / RAX и E / RDX, используемые одним операндом [i]mul и [i]div. (И cdq, чтобы подписать расширение EAX в EDX: EAX, чтобы настроить для idiv, или cdqe в RAX)

CL для переменных значений смещения неявно присутствует в машинном коде, но явным образом в источнике asm. (например, shr rdx, cl).

команды rep- «string» неявно используют RCX, плюс RSI и / или RDI.

Большинство из этих неявных применений пришло из старых 8086 история. См. Почему нет регистра, содержащего старшие байты EAX? . Инструкции, такие как loop и jrcxz, не используются компиляторами , потому что они медленные , а форма с 2 операндами imul как imul ecx, edx быстрее, когда вам не нужно высокая половина результата в EDX / RDX.

Дополнительное чтение:

Это не исчерпывающий список. cmpxchg / cmpxchg16b, xlat, cpuid, rdts c, rdpm c и многие другие имеют неявные операнды, но только некоторые из инструкций, которые регулярно используются компиляторами, делают.

Обратите внимание, что FLAGS является неявный ввод для многих инструкций, таких как adc и cmov.


NASM имеет приложение, в котором перечислены все инструкции , но, как правило, ассемблеры оставляют это на усмотрение поставщиков ЦП. Все ассемблеры x86-64 выдают машинный код по одним и тем же инструкциям. Эта исправленная ошибка ветка более старой версии этого do c содержит описания инструкций Engli sh. (Mainline NASM удалил это из-за недостатка места после добавления инструкций SSE; сейчас слишком много возможностей сделать больше, чем просто перечислить на одной плоской странице с AVX2 и особенно с AVX512.)

3 голосов
/ 22 января 2020
  1. Вы спрашиваете о соглашениях о вызовах , используемых в Linux x86-64. Они следуют за System V ABI . Этот документ объясняет все эти детали. Соглашения о вызовах приведены в разделе 3.2 документа v1.0. Краткий и упрощенный ответ на заданный вами c вопрос состоит в том, что первые 6 аргументов передаются в регистрах; если их больше, они помещаются в стек. (Жизнь усложняется, если некоторые аргументы имеют типы, отличные от целых или указателей.)

    Здесь также можно найти сведения о том, какие регистры могут или не могут быть изменены вызываемой функцией. Например, вызов printf может изменить регистр rdx, но не rbx (или, если это так, он сохранит предыдущее значение и восстановит его перед возвратом).

  2. Подробности выполнения инструкций обычно считаются частью документации процессора, а не ассемблера. Таким образом, официальным источником будет руководство разработчика программного обеспечения от производителя процессора. Здесь Intel и здесь AMD (см. Документы "Архитектура AMD64"). Есть также много сторонних руководств, объясняющих набор инструкций. felixcloutier.com является популярным. Вот инструкция loop ; Вы можете видеть, что он уменьшается rcx на каждой итерации и завершается, когда достигает нуля.

...