32-бітний фрагмент машинного коду x86, 1 байт
48 dec eax
Введення в EAX, вихід у EAX: 0 для істинного, не нульове значення для помилкового. (Також прапор ZF залишається встановленим як істинний, невідомий для помилкового, щоб ви могли je was_equal). Як "бонус", ви не повинні турбуватися про обгортання; 32-розрядний x86 може адресувати лише 4 Гбіт пам'яті, тому ви не можете зробити M достатньо великим, щоб обернутись навколо і знайти 1 == 2**32 + 1щось подібне.
Щоб зробити функцію дзвінка, додайте 0xC3 retінструкцію після повторення 0x48M разів. (Не зараховується до загальної кількості, оскільки для багатьох мов потрібно повторювати лише функціональне тіло або вираз, щоб мати можливість змагатися).
Викликається з GNU C за допомогою прототипу функції атрибута x86 функції __attribute__((regparm(1))) int checkeqM(int eax); GNU Cregparm , наприклад -mregparm, використовує EAX для передачі першого цілого аргументу.
Наприклад, ця повна програма займає 2 аргументи, а JITs M копіює інструкцію + a retв буфер, а потім викликає її як функцію. (Потрібна виконувана купа; компілювати з gcc -O3 -m32 -z execstack)
/******* Test harness: JIT into a buffer and call it ******/
// compile with gcc -O3 -no-pie -fno-pie -m32 -z execstack
// or use mprotect or VirtualProtect instead of -z execstack
// or mmap(PROT_EXEC|PROT_READ|PROT_WRITE) instead of malloc
// declare a function pointer to a regparm=1 function
// The special calling convention applies to this function-pointer only
// So main() can still get its args properly, and call libc functions.
// unlike if you compile with -mregparm=1
typedef int __attribute__((regparm(1))) (*eax_arg_funcptr_t)(unsigned arg);
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc<3) return -1;
unsigned N=strtoul(argv[1], NULL, 0), M = strtoul(argv[2], NULL, 0);
char *execbuf = malloc(M+1); // no error checking
memset(execbuf, 0x48, M); // times M dec eax
execbuf[M] = 0xC3; // ret
// Tell GCC we're about to run this data as code. x86 has coherent I-cache,
// but this also stops optimization from removing these as dead stores.
__builtin___clear_cache (execbuf, execbuf+M+1);
// asm("" ::: "memory"); // compiler memory barrier works too.
eax_arg_funcptr_t execfunc = (eax_arg_funcptr_t) execbuf;
int res = execfunc(N);
printf("%u == %u => %d\n", N,M, res );
return !!res; // exit status only takes the low 8 bits of return value
}
виконувані файли, що не належать до PIE , завантажуються нижче у віртуальну пам'ять; може зробити більший суміжний молоток.
$ gcc -g -O3 -m32 -no-pie -fno-pie -fno-plt -z execstack coderepeat-i386.c
$ time ./a.out 2747483748 2747483748 # 2^31 + 600000100 is close to as big as we can allocate successfully
2747483748 == 2747483748 => 0
real 0m1.590s # on a 3.9GHz Skylake with DDR4-2666
user 0m0.831s
sys 0m0.755s
$ echo $?
0
# perf stat output:
670,816 page-faults # 0.418 M/sec
6,235,285,157 cycles # 3.885 GHz
5,370,142,756 instructions # 0.86 insn per cycle
Зверніть увагу , що GNU C не підтримує об'єкт розміром більше ptrdiff_t(32-розрядний), але mallocі memsetзробити ще роботи, так що ця програма успішно.
Фрагмент машинного коду великого пальця ARM, 2 байти
3802 subs r0, #2
Перший аргумент r0і значення повернення в r0- це стандартна конвенція про виклик ARM. Тут також встановлюються прапори ( sсуфікс). Кумедний факт; версія, що не встановлює флаг, sub- це 32-бітова інструкція.
Інструкція повернення, яку потрібно додати, - це bx lr.
Фрагмент машинного коду AArch64, 4 байти
d1001000 sub x0, x0, #0x4
Працює для 64-бітних цілих чисел. Введення / виведення в x0, відповідно до стандартного режиму виклику. int64_t foo(uint64_t);
AArch64 не має режиму великого пальця (поки що), тому 1 інструкція - це найкраще, що ми можемо зробити.
Lз'єднаний після себе,Mповинен повертати, чиNрівний його вхідL*M?