разъяснение ошибки перемещения данных - PullRequest
0 голосов
/ 25 апреля 2018

В настоящее время я решаю задачу 3.3 из 3-го издания Computer System: взгляд программиста, и мне трудно понять, что означают эти ошибки ...

movb $0xF, (%ebx) выдает ошибку, потому что ebxне может использоваться в качестве регистра адресаневерный размер

почему мы не можем использовать ebx в качестве регистра адресов?Это потому что его 32-битный регистр?Будет ли работать следующая строка, если вместо этого будет movb $0xF, (%rbx)?поскольку rbx имеет 64-битный регистр?

для ошибки, касающейся несоответствия суффикса инструкции и идентификатора регистра, эта ошибка появляется, потому что это должны были быть movq %rax, (%rsp) и movew %si, 8(%rbp) вместо movl %rax, (%rsp) и movb %si, 8(%rbp)?

и, наконец, из-за ошибки, связанной с «неправильным размером операнда назначения», это потому, что регистр назначения был 64-битным вместо 32?так что если бы строка кода была movl %eax, %edx, ошибка бы не произошла?

будет приветствоваться любое просветление.

это для x86-64

Ответы [ 2 ]

0 голосов
/ 25 апреля 2018

movb $0xF, (%ebx) прекрасно собирается (с префиксом размера адреса 0x67) и выполняется правильно, если адрес в ebx действителен.

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

Единственная причина, по которой вы когда-либо использовали это вместо movb $0xF, (%rbx), заключается в том, что старшие байты %rbx потенциально содержали мусор, например в x32 ABI (ILP32 в длинном режиме) , или если вы тупой компилятор, который всегда использует префиксы размера адреса при нацеливании на режим 32-битного указателя, даже когда известно, что адреса безопасно расширен ноль .

32-битный размер адреса на самом деле полезен для ABI x32 для более распространенного случая, когда регистр индекса содержит большое количество мусора, например, movl $0x12345, (%edi, %esi,4).

gcc -mx32 может легко выдать movb $0xF, (%ebx) инструкцию в реальной жизни. (Обратите внимание, что -mx32 (32-разрядные указатели в длинном режиме) отличается от -m32 (i386 ABI))

int ext();          // can't inline
void foo(char *p) { 
    ext();          // clobbers arg-passing registers
    *p = 0xf;       // so gcc needs to save the arg for after the call
}

Компилируется с gcc7.3 -mx32 -O3 в проводнике компилятора Godbolt в

foo(char*):
    pushq   %rbx              # rbx is gcc's first choice of call-preserved reg.
    movq    %rdi, %rbx        # stupid gcc copies the whole 64 bits when only the low 32 are useful
    call    ext()
    movb    $15, (%ebx)       # $15 = $0xF
    popq    %rbx
    ret

mov $edi, %ebx было бы лучше; IDK, почему gcc хочет скопировать весь 64-битный регистр, когда он обрабатывает указатели как 32-битные значения. К сожалению, x32 ABI так и не завоевал популярность на x86, поэтому я полагаю, что никто не потратил время на то, чтобы gcc сгенерировал для него отличный код.

AArch64 также имеет ABI ILP32 для сохранения объема памяти / кэша на данных указателя, поэтому, возможно, gcc будет лучше работать с 32-разрядными указателями в 64-разрядном режиме в целом (также с преимуществами x86-64), если какая-либо работа для AArch64 ILP32 улучшает общие кросс-архитектурные части этого.


так что если бы строка кода была movl% eax, вместо% edx, ошибка бы не произошла?

Правильно, , который будет ноль расширять EAX до RDX . Если вы хотите подписать - расширить EAX в RDX, используйте movslq %eax, %rdx (он же Intel-синтаксис movsxd)

(Почти) все инструкции x86 требуют, чтобы все их операнды были одинакового размера. (С точки зрения размера операнда; многие инструкции имеют форму с 8-битным или 32-битным непосредственным значением, знак которого расширен до 64-битного или независимо от размера операнда инструкции. Например, add $1, %eax будет использовать 3-байтовый add imm8, r/m32 форма .)

Исключения включают shl %cl, %eax и movzx / movsx.

В синтаксисе AT & T размеры регистров должны соответствовать суффиксу размера операнда, если вы его используете. Если вы этого не сделаете, регистры подразумевают размер операнда. например mov %eax, %edx совпадает с movl.

Память + немедленные инструкции без источника или назначения регистра требуют явного размера: add $1, (%rdx) не собирается, потому что размер операнда неоднозначен, но add %eax, (%rdx) - это addl (32-битный размер операнда) .

movew %si, 8(%rbp)

Нет, movw %si, 8(%rbp) будет работать, хотя: P Но учтите, что если вы сделали традиционный фрейм стека с push %rbp / mov %rsp, %rbp при вводе функции, то это сохранение в 8(%rbp) перезапишет младшие 16 битов Ваш обратный адрес в стеке.

Но в коде x86-64 для Windows или Linux не требуется, чтобы вы указывали %rbp или содержали правильный указатель вообще. Это просто регистр с сохранением вызовов, такой как %rbx, который вы можете использовать для чего угодно, если только вы восстановите значение вызывающей стороны перед возвратом.

0 голосов
/ 25 апреля 2018
movb $0xF, (%ebx) gives an error because ebx can't be used as address register

Это правда, что ebx не может использоваться в качестве регистра адресов (для x86-64), но rbx может.ebx - это нижние 32 бита rbx.Весь смысл 64-битного кода в том, что адреса могут быть 64-битными, поэтому попытка ссылаться на память с помощью 32-битного регистра не имеет особого смысла.

movl %rax, (%rsp) and movb %si, 8(%rbp) gives error saying that 
theres a mismatch between instruction suffix and register I.D.

Да, потому что вы используете movl, 'l'означает длинный, что (в данном контексте) означает 32 бита.Однако rax является 64-битным регистром.Если вы хотите записать 64 бит из rax, вы должны использовать movq.Если вы хотите записать 32 бита, вы должны использовать eax.

movl %eax, %rdx gives an error saying that destination operand incorrect size

Вы пытаетесь переместить 32-битное значение в 64-битный регистр.Есть инструкции, чтобы сделать это преобразование для вас (см., Например, cdq), но movl не является одним из них.

...