x86 ส่วนของรหัสเครื่อง 32 บิต, 1 ไบต์
48 dec eax
อินพุตใน EAX, เอาต์พุตใน EAX: 0 สำหรับ true, ไม่ใช่ศูนย์สำหรับ false (ปล่อยให้แฟล็ก ZF ตั้งค่าเป็น true, ไม่ตั้งค่าเป็น false ดังนั้นคุณสามารถทำได้je was_equal
) ในฐานะ "โบนัส" คุณไม่ต้องกังวลเกี่ยวกับการห่อ x86 แบบ 32 บิตสามารถระบุหน่วยความจำ 4GiB ได้เท่านั้นดังนั้นคุณจึงไม่สามารถทำให้ M มีขนาดใหญ่พอที่จะล้อมรอบและค้นหา1 == 2**32 + 1
หรือบางสิ่งบางอย่าง
ในการทำให้ฟังก์ชั่น callable ต่อท้าย0xC3
ret
คำสั่งหลังจากทำซ้ำ0x48
M (ไม่นับรวมในการนับเพราะหลายภาษาจำเป็นต้องทำซ้ำเพียงฟังก์ชั่นร่างกายหรือการแสดงออกเพื่อให้สามารถแข่งขัน)
Calleable จาก GNU C พร้อมกับแอททริบิวต์ฟังก์ชั่น x86 __attribute__((regparm(1))) int checkeqM(int eax);
ของ GNU Cregparm
เช่น-mregparm
ใช้ EAX เพื่อผ่าน ARG จำนวนเต็มแรก
ตัวอย่างเช่นโปรแกรมที่สมบูรณ์นี้ใช้เวลา 2 args และ JITs M สำเนาของคำสั่ง + a 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
}
executables ที่ไม่ใช่ 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
และกลับมาในr0
เป็นมาตรฐานการประชุม ARM เรียก นอกจากนี้ยังตั้งค่าสถานะ ( s
คำต่อท้าย) สนุกกับความเป็นจริง; ไม่ใช่รุ่น -flag การตั้งค่าของ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
?