
x86/x86-64 निर्देश एन्कोडिंग के लिए हेडर-ओनली C++2x कम्पाइल-टाइम असेंबलर
एक हेडर-ओनली C++20 लाइब्रेरी जो संकलन-समय पर x86/x86-64 निर्देश एन्कोडिंग के लिए है।
यह प्रोजेक्ट बूस्ट सॉफ़्टवेयर लाइसेंस 1.0 और MIT लाइसेंस के तहत दोहरे-लाइसेंस है। आप कोई भी लाइसेंस चुन सकते हैं।
#include "static_asm.hpp"
using namespace static_asm::x86::registers;
using namespace static_asm::x86::instructions;
// संकलन समय पर मशीन कोड बनाएं
constexpr auto code = core::assemble(
mov(rax, 0x12345678), // mov rax, imm32
add(rax, rcx), // add rax, rcx
xor_(r8, r8), // xor r8, r8
call(rax), // call rax
ret() // ret
);
// code एक std::array<uint8_t, N> है - पूरी तरह constexpr!
// रजिस्टर से रजिस्टर
add(rax, rbx); // 48 01 D8
sub(ecx, edx); // 29 D1
and_(r8, r9); // 4D 21 C8
or_(rsi, rdi); // 48 09 FE
xor_(eax, eax); // 31 C0 (रजिस्टर को शून्य करने का सामान्य तरीका)
cmp(rax, rcx); // 48 39 C8
// रजिस्टर के साथ तत्काल मान
add(rax, 0x10); // 48 83 C0 10 (हस्ताक्षर-विस्तारित imm8)
add(rax, 0x10000); // 48 05 00 00 01 00 (imm32)
sub(ecx, 100); // 83 E9 64
and_(rdx, 0xFF); // 48 83 E2 FF
// मेमोरी ऑपरेंड के साथ
add(eax, dword_ptr(rbx)); // 03 03
add(rax, qword_ptr(rcx + 0x10)); // 48 03 41 10
sub(dword_ptr(rsp + 0x20), eax); // 29 44 24 20
// रजिस्टर से रजिस्टर
mov(rax, rbx); // 48 89 D8
mov(eax, ecx); // 89 C8
mov(r8, r9); // 4D 89 C8
// तत्काल मान से रजिस्टर
mov(rax, 0x12345678); // 48 C7 C0 78 56 34 12
mov(eax, 0xDEADBEEF); // B8 EF BE AD DE
// मेमोरी संचालन
mov(rax, qword_ptr(rbx)); // 48 8B 03
mov(eax, dword_ptr(rcx + 0x10)); // 8B 41 10
mov(qword_ptr(rsp + 0x8), rax); // 48 89 44 24 08
mov(dword_ptr(rbp - 0x20), 0x100); // C7 45 E0 00 01 00 00
// शून्य/हस्ताक्षर विस्तार
movzx(eax, bl); // 0F B6 C3 (बाइट को dword में शून्य-विस्तार)
movzx(rax, bx); // 48 0F B7 C3 (शब्द को qword में शून्य-विस्तार)
movsx(eax, cl); // 0F BE C1 (बाइट को dword में हस्ताक्षर-विस्तार)
movsx(rax, dx); // 48 0F BF C2 (शब्द को qword में हस्ताक्षर-विस्तार)
movsxd(rax, ecx); // 48 63 C1 (dword को qword में हस्ताक्षर-विस्तार)
// प्रभावी पता लोड करें
lea(rax, qword_ptr(rbx + rcx * s4)); // 48 8D 04 8B
lea(rax, qword_ptr(rbx + rcx * s8 + 0x10)); // 48 8D 44 CB 10
// आदान-प्रदान
xchg(rax, rbx); // 48 87 D8
// [base + index*scale]
mov(eax, dword_ptr(rbx + rcx * s1)); // 8B 04 0B
mov(eax, dword_ptr(rbx + rcx * s2)); // 8B 04 4B
mov(eax, dword_ptr(rbx + rcx * s4)); // 8B 04 8B
mov(eax, dword_ptr(rbx + rcx * s8)); // 8B 04 CB
// [base + index*scale + displacement]
mov(rax, qword_ptr(rbx + rcx * s4 + 0x10)); // 48 8B 44 8B 10
mov(rax, qword_ptr(r12 + r13 * s8 + 0x1000)); // 4B 8B 84 EC 00 10 00 00
// SIB पते पर स्टोर करें
mov(dword_ptr(rax + rdx * s4), ecx); // 89 0C 90
mov(qword_ptr(rbx + rsi * s8 + 0x20), rax); // 48 89 44 F3 20
// SIB के साथ LEA (पता गणना के लिए उपयोगी)
lea(rax, qword_ptr(rbx + rcx * s4)); // 48 8D 04 8B
lea(rax, qword_ptr(rdi + rsi * s8 + 0x100)); // 48 8D 84 F7 00 01 00 00
// 1 से शिफ्ट
shl(eax, 1); // D1 E0
shr(rax, 1); // 48 D1 E8
sar(ecx, 1); // D1 F9
// तत्काल मान से शिफ्ट
shl(eax, 4); // C1 E0 04
shr(rax, 8); // 48 C1 E8 08
sar(rdx, 16); // 48 C1 FA 10
// CL रजिस्टर से शिफ्ट
shl(eax, cl); // D3 E0
shr(rax, cl); // 48 D3 E8
// रोटेट
rol(eax, 1); // D1 C0
ror(rax, 8); // 48 C1 C8 08
rcl(ecx, cl); // D3 D1
rcr(rdx, 1); // 48 D1 DA
// एकल ऑपरेंड (परिणाम rdx:rax में)
mul(rbx); // 48 F7 E3 (अहस्ताक्षरित: rdx:rax = rax * rbx)
imul(rcx); // 48 F7 E9 (हस्ताक्षरित: rdx:rax = rax * rcx)
div(rbx); // 48 F7 F3 (अहस्ताक्षरित: rax = rdx:rax / rbx, rdx = शेष)
idiv(rcx); // 48 F7 F9 (हस्ताक्षरित विभाजन)
// दो-ऑपरेंड IMUL (dest = dest * src)
imul(rax, rbx); // 48 0F AF C3
imul(ecx, edx); // 0F AF CA
// तीन-ऑपरेंड IMUL (dest = src * imm)
imul(rax, rbx, 10); // 48 6B C3 0A
imul(ecx, edx, 1000); // 69 CA E8 03 00 00
// बिना शर्त कूद
jmp(0x10); // EB 10 (छोटा, 8-बिट ऑफ़सेट)
jmp(0x1000); // E9 00 10 00 00 (निकट, 32-बिट ऑफ़सेट)
jmp(rax); // FF E0 (अप्रत्यक्ष)
jmp(here); // EB FE (jmp $, अनंत लूप)
// सशर्त कूद (8-बिट ऑफ़सेट)
jz(0x10); // 74 10
jnz(0x20); // 75 20
jb(0x08); // 72 08 (नीचे/कैरी)
jae(0x08); // 73 08 (ऊपर या बराबर/कोई कैरी नहीं)
jl(0x10); // 7C 10 (कम से, हस्ताक्षरित)
jge(0x10); // 7D 10 (अधिक या बराबर, हस्ताक्षरित)
// सशर्त कूद (लंबी शाखाओं के लिए 32-बिट ऑफ़सेट)
jz_near(0x10000); // 0F 84 00 00 01 00
jnz_near(0x20000); // 0F 85 00 00 02 00
// कॉल और रिटर्न
call(rax); // FF D0 (अप्रत्यक्ष कॉल)
call(0x100); // E8 00 01 00 00 (सापेक्ष कॉल)
ret(); // C3
ret(0x10); // C2 10 00 (वापसी और 16 बाइट पॉप)
// शर्त सत्य होने पर मूव करें (कोई शाखा दंड नहीं!)
cmovz(rax, rbx); // 48 0F 44 C3 (शून्य होने पर मूव)
cmovnz(eax, ecx); // 0F 45 C1 (शून्य न होने पर मूव)
cmovl(rax, rdx); // 48 0F 4C C2 (कम होने पर मूव, हस्ताक्षरित)
cmovge(ecx, esi); // 0F 4D CE (अधिक या बराबर होने पर मूव, हस्ताक्षरित)
cmovb(rax, rbx); // 48 0F 42 C3 (नीचे होने पर मूव, अहस्ताक्षरित)
cmovae(edx, edi); // 0F 43 D7 (ऊपर या बराबर होने पर मूव, अहस्ताक्षरित)
// मेमोरी स्रोत के साथ
cmovz(rax, qword_ptr(rbx)); // 48 0F 44 03
cmovnz(eax, dword_ptr(rcx + 0x10)); // 0F 45 41 10
// बिट परीक्षण
bt(eax, 5); // 0F BA E0 05
bt(rax, rbx); // 48 0F A3 D8
// बिट परीक्षण और सेट/रीसेट/पूरक
bts(eax, 10); // 0F BA E8 0A (परीक्षण और सेट)
btr(rax, rcx); // 48 0F B3 C8 (परीक्षण और रीसेट)
btc(edx, 3); // 0F BA FA 03 (परीक्षण और पूरक)
// बिट स्कैन
bsf(eax, ecx); // 0F BC C1 (पहले 1 के लिए आगे स्कैन)
bsr(rax, rbx); // 48 0F BD C3 (पहले 1 के लिए पीछे स्कैन)
// जनसंख्या गणना और अग्रणी/अनुगामी शून्य
popcnt(eax, ecx); // F3 0F B8 C1
lzcnt(rax, rbx); // F3 48 0F BD C3
tzcnt(eax, edx); // F3 0F BC C2
// बाइट स्वैप
bswap(eax); // 0F C8 (बाइट क्रम उलटें)
bswap(rax); // 48 0F C8
// मूल स्ट्रिंग ऑप्स ([rsi] और/या [rdi] पर काम करते हैं)
movsb(); // A4 (बाइट [rsi] -> [rdi] ले जाएं)
movsw(); // 66 A5
movsd(); // A5
movsq(); // 48 A5
cmpsb(); // A6 ([rsi] की [rdi] से तुलना करें)
stosb(); // AA (al को [rdi] में स्टोर करें)
lodsb(); // AC ([rsi] को al में लोड करें)
scasb(); // AE (al की [rdi] से तुलना करें)
// REP उपसर्ग के साथ (rcx बार दोहराएं)
rep_movsb(); // F3 A4 (memcpy)
rep_movsq(); // F3 48 A5 (तेज़ memcpy, एक बार में 8 बाइट)
rep_stosb(); // F3 AA (memset)
rep_stosq(); // F3 48 AB
// REPE/REPNE के साथ (बराबर होने/न होने तक दोहराएं)
repe_cmpsb(); // F3 A6 (बेमेल होने तक स्ट्रिंग की तुलना)
repne_scasb(); // F2 AE (स्ट्रिंग में बाइट खोजें)
push(rax); // 50
push(rbx); // 53
push(r8); // 41 50
push(0x10); // 6A 10 (push imm8)
push(0x1000); // 68 00 10 00 00 (push imm32)
pop(rax); // 58
pop(rbx); // 5B
pop(r15); // 41 5F
// सिस्टम कॉल
syscall_(); // 0F 05 (64-बिट syscall)
sysenter(); // 0F 34
sysexit(); // 0F 35
// इंटरप्ट
int3(); // CC (ब्रेकपॉइंट)
int_(0x80); // CD 80 (Linux 32-बिट syscall)
int_(0x21); // CD 21 (DOS इंटरप्ट)
// CPU जानकारी
cpuid(); // 0F A2
rdtsc(); // 0F 31
rdtscp(); // 0F 01 F9
// विशेषाधिकार
cli(); // FA (इंटरप्ट साफ़ करें)
sti(); // FB (इंटरप्ट सेट करें)
hlt(); // F4 (रुकें)
// इंटरप्ट रिटर्न
iret(); // CF (16-बिट)
iretd(); // CF (32-बिट)
iretq(); // 48 CF (64-बिट)
निर्देश बाइट सरणियों को संयोजित करने के लिए core::assemble() का उपयोग करें:
constexpr auto prologue = core::assemble(
push(rbp),
mov(rbp, rsp),
sub(rsp, 0x20)
);
constexpr auto epilogue = core::assemble(
add(rsp, 0x20),
pop(rbp),
ret()
);
// इन्हें मिलाएं
constexpr auto full_function = core::assemble(prologue, epilogue);
अपने CMakeLists.txt में जोड़ें:
include(FetchContent)
FetchContent_Declare(
static_asm
GIT_REPOSITORY https://github.com/mahmoudimus/static_asm.git
GIT_TAG v1.0.0 # या विशिष्ट कमिट
)
FetchContent_MakeAvailable(static_asm)
target_link_libraries(your_target PRIVATE static_asm::static_asm)
क्लोन करें या git सबमॉड्यूल के रूप में जोड़ें:
git submodule add https://github.com/mahmoudimus/static_asm.git external/static_asm
फिर अपने CMakeLists.txt में:
add_subdirectory(external/static_asm)
target_link_libraries(your_target PRIVATE static_asm::static_asm)
जब add_subdirectory या FetchContent के माध्यम से शामिल किया जाता है, तो केवल static_asm::static_asm इंटरफ़ेस लाइब्रेरी लक्ष्य आपके प्रोजेक्ट में जोड़ा जाता है। परीक्षण और उदाहरण तब तक नहीं बनाए जाते जब तक -DSTATIC_ASM_BUILD_TESTS=ON के साथ स्पष्ट रूप से सक्षम न किया जाए।
रिलीज़ पेज से static_asm.hpp डाउनलोड करें और इसे सीधे शामिल करें:
#include "static_asm.hpp"
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --install build --prefix /usr/local
फिर find_package का उपयोग करें:
find_package(static_asm REQUIRED)
target_link_libraries(your_target PRIVATE static_asm::static_asm)
# कॉन्फ़िगर करें
cmake -B build -DCMAKE_BUILD_TYPE=Release
# निर्माण
cmake --build build
# परीक्षण चलाएं
ctest --test-dir build --output-on-failure
# उदाहरणों के साथ निर्माण (केवल Clang, इनलाइन असेंबली का उपयोग करता है)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DSTATIC_ASM_BUILD_EXAMPLES=ON
प्लेटफ़ॉर्म समर्थन:
नोट: core::emit() इनलाइन असेंबली सुविधा के लिए -O2 अनुकूलन के साथ Clang की आवश्यकता है।
| श्रेणी | निर्देश |
|---|---|
| ALU | ADD, ADC, SUB, SBB, AND, OR, XOR, CMP, TEST |
| यूनरी | INC, DEC, NEG, NOT |
| गुणा/भाग | MUL, IMUL (1/2/3 ऑपरेंड रूप), DIV, IDIV |
| डेटा मूवमेंट | MOV, MOVABS, MOVZX, MOVSX, MOVSXD, LEA, XCHG, PUSH, POP |
| शिफ्ट/रोटेट | SHL, SHR, SAL, SAR, ROL, ROR, RCL, RCR |
| नियंत्रण प्रवाह | JMP, CALL, RET, RETF |
| सशर्त कूद | JZ/JE, JNZ/JNE, JB/JC, JNB/JNC, JBE/JNA, JNBE/JA, JL, JNL, JLE, JNLE, JO, JNO, JS, JNS, JP, JNP (8-बिट और 32-बिट ऑफ़सेट) |
| सशर्त मूव | CMOVA, CMOVAE, CMOVB, CMOVBE, CMOVE, CMOVG, CMOVGE, CMOVL, CMOVLE, CMOVNE, CMOVNO, CMOVNP, CMOVNS, CMOVO, CMOVP, CMOVS |
| बिट संचालन | BT, BTC, BTR, BTS |
| बिट स्कैन/गणना | BSF, BSR, POPCNT, LZCNT, TZCNT, BSWAP |
| स्ट्रिंग संचालन | MOVSB/W/D/Q, CMPSB/W/D/Q, LODSB/W/D/Q, STOSB/W/D/Q, SCASB/W/D/Q (REP/REPE/REPNE उपसर्गों के साथ) |
| सिस्टम | SYSCALL, SYSENTER, SYSEXIT, INT, INT3, IRET/D/Q, CLI, STI, HLT, CPUID, RDTSC, RDTSCP |
| विविध | NOP, UD2 |
ऑपरेंड समर्थन:
[base + index*scale + disp] स्केल कारक 1, 2, 4, 8 के साथनोट: अभी तक कोई SIMD/AVX एक्सटेंशन नहीं।
यह प्रोजेक्ट Python टूलिंग (कोड जनरेशन, सिंगल-हेडर अमलगमेशन) के लिए uv का उपयोग करता है।
# uv स्थापित करें (यदि पहले से स्थापित नहीं है)
curl -LsSf https://astral.sh/uv/install.sh | sh
# स्थापना सत्यापित करें
uv --version
# सभी Python स्क्रिप्ट को uv के साथ सीधे चलाया जा सकता है (निर्भरताएं स्वचालित रूप से प्रबंधित होती हैं)
uv run scripts/gen_from_x86ref.py --help
uv run scripts/amalgamate.sh
आवश्यक उपकरण:
विकास के लिए वैकल्पिक उपकरण:
| उपकरण | उद्देश्य | स्थापना |
|---|---|---|
| clang-format | कोड फ़ॉर्मेटिंग | LLVM या सिस्टम पैकेज के माध्यम से |
| clang-tidy | स्थैतिक विश्लेषण | LLVM या सिस्टम पैकेज के माध्यम से |
प्रोजेक्ट में एक जनरेटर शामिल है जो x86reference XML डेटाबेस को पार्स करता है:
# निर्देश डेटाबेस सारांश दिखाएं
uv run scripts/gen_from_x86ref.py
# किसी विशिष्ट निर्देश का विवरण दिखाएं
uv run scripts/gen_from_x86ref.py -i lea
uv run scripts/gen_from_x86ref.py -i imul
# निर्देश डेटाबेस फ़ाइलें उत्पन्न करें
uv run scripts/gen_from_x86ref.py --generate-db
# विस्तृत परीक्षण फ़ाइल उत्पन्न करें
uv run scripts/gen_from_x86ref.py --generate-tests
instdb, prefix_db, prefix_0fdb सरणियाँ) में जोड़ेंencoder.hpp में एन्कोडर को कोड करें या मौजूदा एन्कोडर का विस्तार करेंकोई भी स्वचालित उपकरण किसी मनमानी मल्टी-फ़ाइल C++ लाइब्रेरी को बिना कुछ मैन्युअल तैयारी के विश्वसनीय रूप से स्वच्छ, हेडर-ओनली संस्करण में परिवर्तित नहीं कर सकता है। निम्नलिखित तकनीकें यह सुनिश्चित करने में मदद करती हैं कि आपकी लाइब्रेरी को एक एकल हेडर फ़ाइल में सफलतापूर्वक समामेलित किया जा सकता है, जबकि यह सही, बनाए रखने योग्य और मानक-अनुपालन बनी रहे।
नोट: यह प्रोजेक्ट समामेलन के लिए quom और इन नियमों के प्रवर्तन को स्वचालित करने के लिए
google-build-using-namespaceचेक के साथ clang-tidy का उपयोग करता है। quom अपनी प्रसंस्करण क्षमताओं के माध्यम से तकनीक #4 (इनलाइन मार्कर) को भी आंशिक रूप से संभालता है।
using namespace से बचें.cpp फ़ाइलों में फ़ाइल स्कोप पर using namespace खतरनाक है जब वे फ़ाइलें बाद में एक हेडर में शामिल की जाती हैं — यह आपके हेडर को शामिल करने वाले प्रत्येक अनुवाद इकाई के लिए वैश्विक नामस्थान को प्रदूषित करता है।
इसके बजाय अनुशंसित पैटर्न:
// पसंदीदा: कार्यान्वयन को नामस्थान में लपेटें
namespace MyLib {
Foo::Foo() {
// ...
}
}
या
// स्पष्ट योग्यता (अधिक वर्बोज़ लेकिन बहुत स्पष्ट)
MyLib::Foo::Foo() {
// ...
}
सार्वजनिक एपीआई मुख्य नामस्थान में होनी चाहिए। वह सब कुछ जो अंत-उपयोगकर्ताओं के लिए नहीं है, उसे detail या impl जैसे नेस्टेड नामस्थान में छिपाया जाना चाहिए।
सामान्य परिपाटी:
namespace MyLib {
namespace detail { // बहुत व्यापक रूप से उपयोग किया जाता है
// आंतरिक वर्ग, फ़ंक्शन, आदि।
}
}
या
namespace MyLib::impl { // छोटा, सामान्य भी
// आंतरिक कार्यान्वयन विवरण
}
C++17 और बाद के संस्करण इनलाइन नेस्टेड नामस्थान परिभाषाओं का समर्थन करते हैं, जो अधिक स्वच्छ हैं:
namespace MyLib::detail {
class InternalHelper { /* ... */ };
}
static inline वर्ग सदस्यों में बदलें.cpp फ़ाइलों में परिभाषित फ़ाइल-स्कोप static चर हेडर-ओनली दुनिया में समस्याग्रस्त हो जाते हैं (एकाधिक परिभाषाएं, ODR उल्लंघन)।
आधुनिक (C++17+) समाधान:
// पहले (.cpp में)
namespace MyLib {
static int s_counter = 0;
int next_id() {
return ++s_counter;
}
}
// बाद में (हेडर के लिए सुरक्षित)
namespace MyLib::detail {
struct Globals {
static inline int counter = 0;
};
}
inline int MyLib::next_id() {
return ++detail::Globals::counter;
}
static inline चर की गारंटी है कि कई बार शामिल होने पर भी एक ही परिभाषा होगी।
inline के रूप में चिह्नित करेंकोई भी फ़ंक्शन, सदस्य फ़ंक्शन, कंस्ट्रक्टर, या डिस्ट्रक्टर जिसका बॉडी हेडर में दिखाई देता है (लेकिन वर्ग परिभाषा के अंदर नहीं) अवश्य ही inline के रूप में चिह्नित किया जाना चाहिए ताकि One Definition Rule (ODR) उल्लंघनों से बचा जा सके।
क्योंकि कई समामेलन स्क्रिप्ट विशुद्ध रूप से पाठ्य होती हैं और C++ शब्दार्थ को पार्स नहीं करती हैं, विकास के दौरान एक प्लेसहोल्डर मैक्रो (जैसे inline_t) का उपयोग करना एक सामान्य परिपाटी है:
// MyLib.h (या सामान्य हेडर)
#define inline_t /* सामान्य निर्माण के दौरान खाली */
// MyLib.cpp (विकास के दौरान)
namespace MyLib::detail {
inline_t void Helper::do_work() {
// कार्यान्वयन
}
}
समामेलन के दौरान, उपकरण inline_t को inline से बदल देता है:
// समामेलन / परिवर्तन के बाद
inline void MyLib::detail::Helper::do_work() {
// ...
}
आप कोई भी मैक्रो नाम चुन सकते हैं (जैसे MYLIB_INLINE, INLINE_IMP, आदि) और अपनी समामेलन स्क्रिप्ट को तदनुसार कॉन्फ़िगर करें।
नोट: quom जैसे उपकरण C++ शामिल करने के शब्दार्थ को समझकर और समामेलन के दौरान फ़ंक्शन परिभाषाओं को ठीक से संभालकर इसे आंशिक रूप से स्वचालित कर सकते हैं, जिससे कई मामलों में मैन्युअल
inlineमार्करों की आवश्यकता कम हो जाती है।
using namespace … न लिखें।detail / impl) में रखें।static डेटा को एक संरचना/वर्ग के static inline सदस्यों से बदलें।inline मार्कर मैक्रो (समामेलन के दौरान प्रतिस्थापित) के साथ चिह्नित करें।इन चार प्रथाओं का पालन करने से एकल-हेडर वितरण में संक्रमण अधिक सुचारू और बहुत कम त्रुटि-प्रवण हो जाता है — भले ही विशुद्ध रूप से पाठ-आधारित समामेलन उपकरणों का उपयोग किया जा रहा हो।
यह प्रोजेक्ट Midi12 द्वारा cx_assembler पर आधारित है। मूल लाइब्रेरी ने C++ में संकलन-समय x86 असेंबली एन्कोडिंग के लिए आधार प्रदान किया।