Харесвам примери, така че написах малко самопроменящ се код в c...
#include <stdio.h>
#include <sys/mman.h> // linux
int main(void) {
unsigned char *c = mmap(NULL, 7, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|
MAP_ANONYMOUS, -1, 0); // get executable memory
c[0] = 0b11000111; // mov (x86_64), immediate mode, full-sized (32 bits)
c[1] = 0b11000000; // to register rax (000) which holds the return value
// according to linux x86_64 calling convention
c[6] = 0b11000011; // return
for (c[2] = 0; c[2] < 30; c[2]++) { // incr immediate data after every run
// rest of immediate data (c[3:6]) are already set to 0 by MAP_ANONYMOUS
printf("%d ", ((int (*)(void)) c)()); // cast c to func ptr, call ptr
}
putchar('\n');
return 0;
}
... което работи, очевидно:
>>> gcc -Wall -Wextra -std=c11 -D_GNU_SOURCE -o test test.c; ./test
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Но честно казано, изобщо не очаквах да работи. Очаквах инструкцията, съдържаща c[2] = 0
, да се кешира при първото извикване на c
, след което всички последователни извиквания на c
ще игнорират повтарящите се промени, направени в c
(освен ако по някакъв начин изрично съм обезсилил кеша). За щастие процесорът ми изглежда по-умен от това.
Предполагам, че процесорът сравнява RAM (ако приемем, че c
дори се намира в RAM) с кеша на инструкциите всеки път, когато указателят на инструкциите направи голям скок (както при извикването на mmapped паметта по-горе), и прави кеша невалиден, когато не съвпада (всичко?), но се надявам да получа по-точна информация за това. По-специално, бих искал да знам дали това поведение може да се счита за предсказуемо (с изключение на разликите в хардуера и операционната система) и да се разчита?
(Вероятно трябва да се позова на ръководството на Intel, но това нещо е дълго хиляди страници и имам склонност да се изгубя в него...)
mmap
и нечетният двоичен синтаксис0b...
(невалиден C)? - person R.. GitHub STOP HELPING ICE   schedule 12.06.2012mmap
е чист POSIX, но нещата0b...
изглеждаха като нещо от наследен DOS компилатор... Нямах представа, че GCC го има. - person R.. GitHub STOP HELPING ICE   schedule 12.06.2012-D_POSIX_C_SOURCE=200809L
или-D_XOPEN_SOURCE=700
) е необходим, за да получите POSIX интерфейси. - person R.. GitHub STOP HELPING ICE   schedule 12.06.2012