写入可执行内存时避免自修改代码 (SMC) 机器清除

问题描述

我遇到了一个奇怪的问题,cpu 认为我正在修改当前执行的代码,并反复触发 self-modifying code (SMC) machine clears

我的(简化的)程序执行以下操作:

  1. 分配一个可执行缓冲区。
  2. 将 64 字节的有效负载复制到缓冲区中的某个位置 X。
  3. 在位置 X 调用有效载荷。
  4. 返回 2。

... 100'000'000 次迭代。

ma​​in.c

#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

extern void smc(void *bufferPtr,void *bufferEndPtr);

int main()
{
    const int BUFFER_LENGTH = 4096;
    
    void *bufferPtr = mmap(0,BUFFER_LENGTH,PROT_READ | PROT_WRITE | PROT_EXEC,MAP_ANONYMOUS | MAP_PRIVATE,0);
    void *bufferEndPtr = bufferPtr + BUFFER_LENGTH;
    printf("Instruction block buffer: %p,%s\n",bufferPtr,strerror(errno));
    
    smc(bufferPtr,bufferEndPtr);
    
    return 0;
}

smc.asm

[section .text]

align 64
payload:
    ret

%define BUFFER_STEP 64

align 64
[global smc]
; rdi: bufferPtr
; rsi: bufferEndPtr
smc:
    push r10
    push r11
    push r12

    mov rax,100_000_000
    mov r10,rdi ; r10 points to begin of buffer
    mov r11,rdi ; r11 points to current buffer position
    mov r12,rsi ; r12 points to end of buffer

.loop:
    ; Done?
    dec rax
    je .end
    
    mov rcx,64
    mov rdi,r11
    lea rsi,[rel payload]
    
    ; Store
    rep movsb
    
    ; Call
    call r11
    
    ; Move buffer pointer
    lea r11,[r11 + BUFFER_STEP]
    cmp r11,r12
    jb .next
    mov r11,r10

.next:
    jmp .loop

.end:
    pop r12
    pop r11
    pop r10
    ret

编译

nasm smc.asm -f elf64 -o smc.o
gcc -c main.c -O2 -o main.o
gcc main.o smc.o -o prog

我使用

测量程序的执行时间和MACHINE_CLEARS.SMC performance counter
sudo perf stat -e r04c3 ./prog

在英特尔酷睿 i7-7567U 上的结果:

BUFFER_LENGTH(字节) BUFFER_STEP(字节) MACHINE_CLEARS.SMC 执行时间(秒)
1 x 4K 0 199'999'982 14.53
1 x 4K 64 199'999'740 14.91
256 x 4K 2048 105'550'699 7.89
256 x 4K 4096 130'573'069 9.83

尽管我正在移动存储目的地(每次写入不同的位置),但我仍然清除了数百万个 SMC 机器,这导致了巨大的性能损失。

在 store 之前/之后添加各种围栏和/或序列化指令不会产生任何显着的改进。请注意,虽然移位在一定程度上减少了机器清除的次数,但也会导致 call 指令处出现大量分支目标错误预测。

当我使用 4K 缓冲区、0 字节步长、mfence 存储后和 call payload 而不是 call r11 运行相同的程序时,它只需要大约 1.74 秒,即考虑到已执行指令的总数。

是什么导致了如此大量的机器清除,我该如何解决

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)