x86 32ビットマシンコードフラグメント、1バイト
48 dec eax
EAXでの入力、EAXでの出力:trueの場合は0、falseの場合はゼロ以外。(ZFフラグはtrueに設定されたまま、falseに設定されていないため、可能ですje was_equal
)。「ボーナス」として、ラッピングを心配する必要はありません。32ビットx86は4GiBのメモリのみをアドレス指定できるため、Mを十分に大きくして、すべてをラップして1 == 2**32 + 1
何かを見つけることはできません。
呼び出し可能な関数を作成するには、M回0xC3
ret
繰り返した後に命令を追加します0x48
。(多くの言語は、競合するために関数本体または式のみを繰り返す必要があるため、合計数には含まれません)。
プロトタイプを使用してGNU Cから呼び出し可能 __attribute__((regparm(1))) int checkeqM(int eax);
GNU Cさんregparm
のx86関数属性など-mregparm
使用、EAXを使用して最初の整数argを渡します。
たとえば、この完全なプログラムは2つの引数を取り、JITは命令の+ 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 in 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モードがないため、1命令が最善です。
L
M
N
L*M
?