32-битный фрагмент машинного кода x86, 1 байт
48 dec eax
Вход в EAX, вывод в EAX: 0 для истины, ненулевое значение для ложных. (Также оставляет флаг ZF установленным для true, не установленным для false, чтобы вы могли 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 аргумента, и JIT помещает 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 исполняемые файлы загружаются ниже в виртуальной памяти; может сделать больший смежный malloc.
$ 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 Thumb, 2 байта
3802 subs r0, #2
Первый аргумент in r0и возвращаемое значение in r0- это стандартное соглашение о вызовах ARM. Это также устанавливает флаги ( sсуффикс). Забавный факт; не -flag устанавливающих версия subявляется 32-битной инструкции.
Команда возврата вам необходимо добавить это bx lr.
Фрагмент машинного кода AArch64, 4 байта
d1001000 sub x0, x0, #0x4
Работает для 64-битных целых чисел. Ввод / вывод в x0соответствии со стандартным соглашением о вызовах. int64_t foo(uint64_t);
У AArch64 нет режима Thumb (пока), поэтому 1 инструкция - лучшее, что мы можем сделать.
Lпосле себя,Mдолжен возвращать,Nравен ли его входL*M?