frammento di codice macchina x86 a 32 bit, 1 byte
48 dec eax
Input in EAX, output in EAX: 0 per vero, diverso da zero per falso. (Inoltre, lascia il flag ZF impostato su true, non impostato su false, quindi è possibile je was_equal). Come "bonus", non devi preoccuparti di avvolgere; L'x86 a 32 bit può indirizzare solo 4GiB di memoria, quindi non puoi rendere M abbastanza grande da avvolgere completamente e trovare 1 == 2**32 + 1o qualcosa del genere.
Per rendere una funzione richiamabile, aggiungere 0xC3 retun'istruzione dopo aver ripetuto 0x48M volte. (Non conteggiato nel conteggio totale, perché molte lingue devono ripetere solo il corpo della funzione, o un'espressione, per poter competere).
Chiamabile da GNU C con il prototipo dell'attributo della funzione x86 di __attribute__((regparm(1))) int checkeqM(int eax); GNU Cregparm , come -mregparm, usa EAX per passare il primo numero intero arg.
Ad esempio, questo programma completo accetta 2 arg e JITs M copia l'istruzione + a retin un buffer e quindi lo chiama come funzione. (Richiede heap eseguibile; compilare con 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
}
gli eseguibili non PIE vengono caricati più in basso nella memoria virtuale; può fare un malloc contiguo più grande.
$ 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
Si noti che GNU C non supporta oggetti di dimensioni superiori a ptrdiff_t(segno a 32 bit), ma malloce memsetfare ancora lavoro, quindi questo programma ha successo.
Frammento di codice macchina pollice ARM, 2 byte
3802 subs r0, #2
Il primo argomento in r0e il valore restituito in r0è la convenzione di chiamata ARM standard. Questo imposta anche flag (il ssuffisso). Fatto divertente; la versione non -flag di subè un'istruzione larga a 32 bit.
L'istruzione di ritorno che devi aggiungere è bx lr.
Frammento di codice macchina AArch64, 4 byte
d1001000 sub x0, x0, #0x4
Funziona con numeri interi a 64 bit. Ingresso / uscita in x0, secondo la convenzione di chiamata standard. int64_t foo(uint64_t);
AArch64 non ha una modalità Thumb (ancora), quindi 1 istruzione è la migliore che possiamo fare.
Lconcatenato dopo iMtempi stessi dovrebbe restituire se il suo inputNè uguale aL*M?