Fragmento de código de máquina x86 de 32 bits, 1 byte
48 dec eax
Entrada en EAX, salida en EAX: 0 para verdadero, no cero para falso. (También deja el indicador ZF establecido en verdadero, sin establecer en falso, para que pueda je was_equal). Como "bono", no tiene que preocuparse por la envoltura; El x86 de 32 bits solo puede direccionar 4GiB de memoria, por lo que no puede hacer que M sea lo suficientemente grande como para envolverlo todo y encontrar 1 == 2**32 + 1o algo así.
Para realizar una función invocable, agregue una 0xC3 retinstrucción después de repetir 0x48M veces. (No se cuenta en el recuento total, porque muchos idiomas necesitan repetir solo el cuerpo de la función, o una expresión, para poder competir).
Se puede comercializar desde GNU C con el prototipo __attribute__((regparm(1))) int checkeqM(int eax); de regparmatributo de función x86 de GNU C , como -mregparm, usa EAX para pasar el primer argumento entero.
Por ejemplo, este programa completo toma 2 args, y JITs M copias de la instrucción + a reten un búfer, y luego lo llama como una función. (Requiere montón ejecutable; compilar 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
}
los ejecutables que no son PIE se cargan más abajo en la memoria virtual; puede hacer un malloc contiguo más 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
Tenga en cuenta que GNU C no admite tamaños de objeto mayores que ptrdiff_t(con signo de 32 bits), pero mallocymemset hacer aún el trabajo, por lo que este programa tiene éxito.
Fragmento de código de máquina ARM Thumb, 2 bytes
3802 subs r0, #2
El primer argumento de entrada r0y valor de retorno r0es la convención de llamada ARM estándar. Esto también establece banderas (el ssufijo). Hecho de la diversión; la no versión de establecer -flag desub es una instrucción de 32 bits de ancho.
La instrucción de devolución que debe agregar es bx lr .
Fragmento de código de máquina AArch64, 4 bytes
d1001000 sub x0, x0, #0x4
Funciona para enteros de 64 bits. Entrada / salida enx0 , según la convención de llamada estándar. int64_t foo(uint64_t);
AArch64 no tiene un modo Thumb (todavía), por lo que 1 instrucción es lo mejor que podemos hacer.
Lconcatenado después de sí mismoMdebería devolver si su entradaNes igual aL*M?