Наблюдение за устаревшими инструкциями на x86 с самоизменяющимся кодом

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

В руководстве по разработке программного обеспечения Intel из раздела 11.6 говорится, что

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

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

int fd = open("code_area", O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
assert(fd>=0);
write(fd, zeros, 0x1000);
uint8_t *a1 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_FILE | MAP_SHARED, fd, 0);
uint8_t *a2 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_FILE | MAP_SHARED, fd, 0);
assert(a1 != a2);

У меня есть функция сборки, которая принимает один аргумент, указатель на инструкцию, которую я хочу изменить.

fun:
    push %rbp
    mov %rsp, %rbp

    xorq %rax, %rax # Return value 0

# A far jump simulated with a far return
# Push the current code segment %cs, then the address we want to far jump to

    xorq %rsi, %rsi
    mov %cs, %rsi
    pushq %rsi
    leaq copy(%rip), %r15
    pushq %r15
    lretq

copy:
# Overwrite the two nops below with `inc %eax'. We will notice the change if the
# return value is 1, not zero. The passed in pointer at %rdi points to the same physical
# memory location of fun_ins, but the linear addresses will be different.
    movw $0xc0ff, (%rdi)

fun_ins:
    nop   # Two NOPs gives enough space for the inc %eax (opcode FF C0)
    nop
    pop %rbp
    ret
fun_end:
    nop

В C я копирую код в отображенный файл памяти. Я вызываю функцию с линейного адресаa1но я передаю указатель наa2 как цель модификации кода.

#define DIFF(a, b) ((long)(b) - (long)(a))
long sz = DIFF(fun, fun_end);
memcpy(a1, fun, sz);
void *tochange = DIFF(fun, fun_ins);
int val = ((int (*)(void*))a1)(tochange);

Если процессор подобрал измененный код, val == 1. В противном случае, если устаревшие инструкции были выполнены (два nops), val == 0.I '

мы работали с 1,7-ГГц процессором Intel Core i5 (MacBook Air 2011) и процессором Intel® Xeon® X3460 с частотой 2,80 ГГц. Однако каждый раз, когда я вижу val == 1, CPU указывает на новую инструкцию.

Кто-нибудь сталкивался с поведением, которое я хочу наблюдать? Правильно ли мои рассуждения? Я'Я немного сбит с толку из-за того, что в руководстве упоминаются процессоры P6 и Pentium, а также из-за отсутствия упоминания моего процессора Core i5. Возможно, происходит что-то еще, что заставляет ЦП сбрасывать свою очередь предварительной выборки команд? Любое понимание было бы очень полезно!

Ответы на вопрос(3)

Ваш ответ на вопрос