Đoạn mã máy x86 32 bit, 1 byte
48 dec eax
Đầu vào trong EAX, đầu ra trong EAX: 0 cho đúng, khác không cho sai. (Cũng để cờ ZF được đặt thành true, không đặt thành false, vì vậy bạn có thể je was_equal
). Là một "phần thưởng", bạn không phải lo lắng về việc gói; X86 32 bit chỉ có thể giải quyết 4GiB bộ nhớ, vì vậy bạn không thể làm cho M đủ lớn để quấn quanh và tìm 1 == 2**32 + 1
hoặc một cái gì đó.
Để thực hiện chức năng có thể gọi được, hãy thêm một 0xC3
ret
lệnh sau khi lặp lại 0x48
M lần. (Không được tính trong tổng số đếm, vì nhiều ngôn ngữ chỉ cần lặp lại thân hàm hoặc biểu thức để có thể cạnh tranh).
Có thể gọi được từ GNU C với thuộc tính hàm x86 __attribute__((regparm(1))) int checkeqM(int eax);
của GNU Cregparm
, giống như -mregparm
, sử dụng EAX để truyền tham số nguyên đầu tiên.
Ví dụ, chương trình hoàn chỉnh này có 2 đối số và JIT M sao chép lệnh + a ret
vào bộ đệm, sau đó gọi nó là hàm. (Yêu cầu heap thực thi; biên dịch với 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
}
các tệp thực thi không phải PIE được tải thấp hơn trong bộ nhớ ảo; có thể làm một malloc tiếp giáp lớn hơn.
$ 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
Lưu ý rằng GNU C không hỗ trợ đối tượng kích thước lớn hơn ptrdiff_t
(đã ký 32-bit), nhưng malloc
và memset
làm vẫn làm việc, vì vậy chương trình này thành công.
Đoạn mã ARM ARM, 2 byte
3802 subs r0, #2
Đầu tiên arg in r0
và return value in r0
là quy ước gọi ARM tiêu chuẩn. Điều này cũng đặt cờ ( s
hậu tố). Sự thật thú vị; các phi phiên bản -flag-thiết của sub
một hướng dẫn rộng 32-bit.
Các hướng dẫn trở lại bạn cần phải thêm là bx lr
.
Đoạn mã máy AArch64, 4 byte
d1001000 sub x0, x0, #0x4
Hoạt động cho số nguyên 64 bit. Đầu vào / đầu ra x0
, theo quy ước gọi tiêu chuẩn. int64_t foo(uint64_t);
AArch64 không có chế độ Thumb (chưa), vì vậy 1 hướng dẫn là cách tốt nhất chúng ta có thể làm.
L
nối sauM
lần chính nó sẽ trả về liệu đầu vào của nóN
có bằngL*M
không?