x86 32位机器代码片段,1个字节
48 dec eax
在EAX中输入,在EAX中输出:0为true,非零为false。(还将ZF标志设置为true,未设置为false,因此可以je was_equal
)。作为“奖励”,您不必担心包装。32位x86只能寻址4GiB的内存,因此您不能使M足够大以至于不能一路回绕并找到内容1 == 2**32 + 1
。
要使函数可调用,请0xC3
ret
在重复0x48
M次后追加一条指令。(不计入总数中,因为许多语言只需重复函数体或表达式即可竞争)。
Calleable从GNU C与原型__attribute__((regparm(1))) int checkeqM(int eax);
GNU C的regparm
x86函数属性调用,例如-mregparm
,使用EAX传递第一个整数arg。
例如,此完整程序将2个args和JIT将指令+ a的M个副本复制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
第一个arg输入r0
和in的返回值r0
是标准的ARM调用约定。这还将设置标志(s
后缀)。有趣的事实; 的非标志设置版本sub
是32位宽的指令。
您需要附加的返回指令为bx lr
。
AArch64机器代码片段,4个字节
d1001000 sub x0, x0, #0x4
适用于64位整数。x0
按照标准调用约定,在中输入/输出。 int64_t foo(uint64_t);
AArch64还没有Thumb模式,所以我们最好的就是一条指令。
L
在其自身M
时间之后连接起来应该返回其输入N
是否等于L*M
?