From 10dca7ba97d9cba190d1d5e2b160aa5df9072ca1 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:33:24 +0200 Subject: [PATCH 01/44] Add CPUID support --- src/asm/x86/instructions/cpu.cpp | 5 ++++ src/asm/x86/writer.hpp | 1 + test/x86.cpp | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/asm/x86/instructions/cpu.cpp b/src/asm/x86/instructions/cpu.cpp index 02d9499..2ba5544 100644 --- a/src/asm/x86/instructions/cpu.cpp +++ b/src/asm/x86/instructions/cpu.cpp @@ -1446,6 +1446,11 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operand"}; } + void BufferWriter::put_cpuid() { + put_byte(LONG_OPCODE); + put_byte(0xA2); + } + void BufferWriter::put_xadd(Location dst, Location src) { if (dst.is_memreg() && src.is_simple()) { diff --git a/src/asm/x86/writer.hpp b/src/asm/x86/writer.hpp index a16f250..783b6a2 100644 --- a/src/asm/x86/writer.hpp +++ b/src/asm/x86/writer.hpp @@ -261,6 +261,7 @@ namespace asmio::x86 { INST put_test(Location src); ///< Sets flags accordingly to the value of register given, ASMIOV extension INST put_ret(); ///< Return from procedure INST put_ret(Location bytes); ///< Return from procedure and pop X bytes + INST put_cpuid(); ///< Return CPU information in EAX, EBX, ECX, and EDX registers // i486 INST put_xadd(Location dst, Location src); ///< Exchange and Add diff --git a/test/x86.cpp b/test/x86.cpp index 1073be1..bcd9652 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -3758,5 +3758,46 @@ namespace test { }; + TEST (exec_cpuid) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + struct Result { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + }; + + writer.label("cpuid"); + writer.put_mov(RAX, ref(RDI)); // page + writer.put_cpuid(); + writer.put_mov(RDI, ref(RDI + 8)); // result* + + writer.put_mov(ref(RDI), EAX); // result->eax + writer.put_mov(ref(RDI + 4), EBX); // result->ebx + writer.put_add(RDI, 8); + writer.put_mov(ref(RDI), ECX); // result->ecx + writer.put_mov(ref(RDI + 4), EDX); // result->edx + writer.put_ret(); + + Result result {}; + + ExecutableBuffer exe = to_executable(buffer); + + // EAX:EDX:ECX should contain vendor name + // exe.scall("cpuid", uint64_t(0), &result); + + exe.scall("cpuid", uint64_t(1), &result); + + // check for support of some expected features + ASSERT_MSG(result.edx & (1 << 23), "MMX not supprted or CPUID failed!"); + ASSERT_MSG(result.edx & (1 << 0), "FPU not supprted or CPUID failed!"); + ASSERT_MSG(result.edx & (1 << 15), "CMOV not supprted or CPUID failed!"); + ASSERT_MSG(result.ecx & (1 << 23), "POPCNT not supprted or CPUID failed!"); + + }; + #endif ;} From c3aacdf44391b602417732b91da8d89b9051d5c2 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sat, 4 Apr 2026 16:57:16 +0200 Subject: [PATCH 02/44] Restructure project --- CMakeLists.txt | 4 ++-- src/{asm => asmio}/aarch64/argument/condition.hpp | 2 +- src/{asm => asmio}/aarch64/argument/pattern.cpp | 2 +- src/{asm => asmio}/aarch64/argument/pattern.hpp | 7 +++---- src/{asm => asmio}/aarch64/argument/registry.hpp | 6 +++--- src/{asm => asmio}/aarch64/argument/shift.hpp | 2 +- src/{asm => asmio}/aarch64/argument/sizing.hpp | 2 ++ src/{asm => asmio}/aarch64/module.cpp | 0 src/{asm => asmio}/aarch64/module.hpp | 4 ++-- src/{asm => asmio}/aarch64/writer.cpp | 0 src/{asm => asmio}/aarch64/writer.hpp | 2 +- .../basic.cpp => asmio/aarch64/writer_basic.cpp} | 4 ++-- .../branch.cpp => asmio/aarch64/writer_branch.cpp} | 4 ++-- src/{out => asmio}/elf/dwarf/abbrev.cpp | 0 src/{out => asmio}/elf/dwarf/abbrev.hpp | 8 ++++---- src/{out => asmio}/elf/dwarf/attribute.hpp | 2 +- src/{out => asmio}/elf/dwarf/emitter.cpp | 0 src/{out => asmio}/elf/dwarf/emitter.hpp | 2 +- src/{out => asmio}/elf/dwarf/encoding.hpp | 2 +- src/{out => asmio}/elf/dwarf/form.hpp | 0 src/{out => asmio}/elf/dwarf/info.cpp | 0 src/{out => asmio}/elf/dwarf/info.hpp | 2 +- src/{out => asmio}/elf/dwarf/lang.hpp | 0 src/{out => asmio}/elf/dwarf/lines.cpp | 0 src/{out => asmio}/elf/dwarf/lines.hpp | 2 +- src/{out => asmio}/elf/dwarf/tag.hpp | 0 src/{out => asmio}/elf/dwarf/unit.hpp | 0 src/{out => asmio}/elf/dwarf/version.hpp | 0 src/{out => asmio}/elf/export.cpp | 2 +- src/{out => asmio}/elf/export.hpp | 2 +- src/{out => asmio}/elf/header.hpp | 4 ++-- src/{out => asmio}/elf/model.cpp | 0 src/{out => asmio}/elf/model.hpp | 7 ++++--- src/{out => asmio}/elf/object.cpp | 0 src/{out => asmio}/elf/object.hpp | 2 +- src/{out => asmio}/elf/relocation.hpp | 4 ++-- src/{out => asmio}/elf/section.hpp | 5 ++--- src/{out => asmio}/elf/segment.hpp | 4 ++-- src/{out => asmio}/elf/symbol.hpp | 4 ++-- src/{ => asmio}/external.hpp | 3 +++ src/{asm => asmio}/module.cpp | 4 ++-- src/{asm => asmio}/module.hpp | 6 ++---- src/{out/buffer => asmio/program}/executable.cpp | 0 src/{out/buffer => asmio/program}/executable.hpp | 2 +- src/{out/buffer => asmio/program}/label.cpp | 0 src/{out/buffer => asmio/program}/label.hpp | 8 +++----- src/{out/buffer => asmio/program}/linkage.cpp | 0 src/{out/buffer => asmio/program}/linkage.hpp | 0 src/{out/buffer => asmio/program}/memory.cpp | 5 +++-- src/{out/buffer => asmio/program}/memory.hpp | 4 ++-- src/{out/buffer => asmio/program}/segmented.cpp | 0 src/{out/buffer => asmio/program}/segmented.hpp | 8 ++++---- src/{out/buffer => asmio/program}/sizes.hpp | 2 +- src/{out/buffer => asmio/program}/writer.cpp | 0 src/{out/buffer => asmio/program}/writer.hpp | 2 +- src/{ => asmio}/util.hpp | 5 +---- src/{out/chunk/buffer.cpp => asmio/util/chunk.cpp} | 2 +- src/{out/chunk/buffer.hpp => asmio/util/chunk.hpp} | 4 +--- src/{out/chunk => asmio/util}/codecs.cpp | 0 src/{out/chunk => asmio/util}/codecs.hpp | 2 +- src/{ => asmio}/util/indexer.hpp | 1 + src/{asm/util.hpp => asmio/util/macro.hpp} | 10 ++++++++-- src/{ => asmio}/util/pool.hpp | 1 + src/{ => asmio}/util/refcnt.hpp | 2 +- src/{ => asmio}/util/set.hpp | 2 +- src/{ => asmio}/util/strings.hpp | 0 src/{ => asmio}/util/tmp.cpp | 2 +- src/{ => asmio}/util/tmp.hpp | 2 +- src/{asm => asmio}/x86/argument/location.cpp | 1 - src/{asm => asmio}/x86/argument/location.hpp | 9 +++++---- src/{asm => asmio}/x86/argument/registry.hpp | 9 ++++----- src/{asm => asmio}/x86/argument/scaled.hpp | 5 +++-- src/{asm => asmio}/x86/const.hpp | 2 +- src/{asm => asmio}/x86/module.cpp | 2 +- src/{asm => asmio}/x86/module.hpp | 5 +++-- src/{asm => asmio}/x86/writer.cpp | 5 +---- src/{asm => asmio}/x86/writer.hpp | 8 ++++---- .../cpu.cpp => asmio/x86/writer_cpu.cpp} | 2 +- .../fpu.cpp => asmio/x86/writer_fpu.cpp} | 2 +- src/macro.hpp | 9 --------- src/tasml/args.hpp | 4 ++-- src/tasml/error.hpp | 4 ++-- src/tasml/main.cpp | 8 ++++---- src/tasml/stream.hpp | 2 +- src/tasml/token.cpp | 2 +- src/tasml/token.hpp | 2 +- src/tasml/tokenizer.cpp | 2 +- src/tasml/tokenizer.hpp | 2 +- src/tasml/top.cpp | 4 ++-- src/tasml/top.hpp | 4 ++-- test/aarch64.cpp | 8 ++++---- test/elf.cpp | 12 ++++++------ test/tasml.cpp | 6 +++--- test/unit.cpp | 12 ++++++------ test/x86.cpp | 8 ++++---- 95 files changed, 150 insertions(+), 157 deletions(-) rename src/{asm => asmio}/aarch64/argument/condition.hpp (96%) rename src/{asm => asmio}/aarch64/argument/pattern.cpp (99%) rename src/{asm => asmio}/aarch64/argument/pattern.hpp (96%) rename src/{asm => asmio}/aarch64/argument/registry.hpp (97%) rename src/{asm => asmio}/aarch64/argument/shift.hpp (82%) rename src/{asm => asmio}/aarch64/argument/sizing.hpp (91%) rename src/{asm => asmio}/aarch64/module.cpp (100%) rename src/{asm => asmio}/aarch64/module.hpp (86%) rename src/{asm => asmio}/aarch64/writer.cpp (100%) rename src/{asm => asmio}/aarch64/writer.hpp (99%) rename src/{asm/aarch64/instructions/basic.cpp => asmio/aarch64/writer_basic.cpp} (99%) rename src/{asm/aarch64/instructions/branch.cpp => asmio/aarch64/writer_branch.cpp} (97%) rename src/{out => asmio}/elf/dwarf/abbrev.cpp (100%) rename src/{out => asmio}/elf/dwarf/abbrev.hpp (95%) rename src/{out => asmio}/elf/dwarf/attribute.hpp (99%) rename src/{out => asmio}/elf/dwarf/emitter.cpp (100%) rename src/{out => asmio}/elf/dwarf/emitter.hpp (85%) rename src/{out => asmio}/elf/dwarf/encoding.hpp (95%) rename src/{out => asmio}/elf/dwarf/form.hpp (100%) rename src/{out => asmio}/elf/dwarf/info.cpp (100%) rename src/{out => asmio}/elf/dwarf/info.hpp (95%) rename src/{out => asmio}/elf/dwarf/lang.hpp (100%) rename src/{out => asmio}/elf/dwarf/lines.cpp (100%) rename src/{out => asmio}/elf/dwarf/lines.hpp (99%) rename src/{out => asmio}/elf/dwarf/tag.hpp (100%) rename src/{out => asmio}/elf/dwarf/unit.hpp (100%) rename src/{out => asmio}/elf/dwarf/version.hpp (100%) rename src/{out => asmio}/elf/export.cpp (99%) rename src/{out => asmio}/elf/export.hpp (85%) rename src/{out => asmio}/elf/header.hpp (97%) rename src/{out => asmio}/elf/model.cpp (100%) rename src/{out => asmio}/elf/model.hpp (97%) rename src/{out => asmio}/elf/object.cpp (100%) rename src/{out => asmio}/elf/object.hpp (98%) rename src/{out => asmio}/elf/relocation.hpp (99%) rename src/{out => asmio}/elf/section.hpp (96%) rename src/{out => asmio}/elf/segment.hpp (95%) rename src/{out => asmio}/elf/symbol.hpp (95%) rename src/{ => asmio}/external.hpp (94%) rename src/{asm => asmio}/module.cpp (98%) rename src/{asm => asmio}/module.hpp (95%) rename src/{out/buffer => asmio/program}/executable.cpp (100%) rename src/{out/buffer => asmio/program}/executable.hpp (99%) rename src/{out/buffer => asmio/program}/label.cpp (100%) rename src/{out/buffer => asmio/program}/label.hpp (97%) rename src/{out/buffer => asmio/program}/linkage.cpp (100%) rename src/{out/buffer => asmio/program}/linkage.hpp (100%) rename src/{out/buffer => asmio/program}/memory.cpp (92%) rename src/{out/buffer => asmio/program}/memory.hpp (95%) rename src/{out/buffer => asmio/program}/segmented.cpp (100%) rename src/{out/buffer => asmio/program}/segmented.hpp (97%) rename src/{out/buffer => asmio/program}/sizes.hpp (82%) rename src/{out/buffer => asmio/program}/writer.cpp (100%) rename src/{out/buffer => asmio/program}/writer.hpp (98%) rename src/{ => asmio}/util.hpp (99%) rename src/{out/chunk/buffer.cpp => asmio/util/chunk.cpp} (99%) rename src/{out/chunk/buffer.hpp => asmio/util/chunk.hpp} (99%) rename src/{out/chunk => asmio/util}/codecs.cpp (100%) rename src/{out/chunk => asmio/util}/codecs.hpp (97%) rename src/{ => asmio}/util/indexer.hpp (99%) rename src/{asm/util.hpp => asmio/util/macro.hpp} (60%) rename src/{ => asmio}/util/pool.hpp (99%) rename src/{ => asmio}/util/refcnt.hpp (96%) rename src/{ => asmio}/util/set.hpp (96%) rename src/{ => asmio}/util/strings.hpp (100%) rename src/{ => asmio}/util/tmp.cpp (95%) rename src/{ => asmio}/util/tmp.hpp (94%) rename src/{asm => asmio}/x86/argument/location.cpp (99%) rename src/{asm => asmio}/x86/argument/location.hpp (98%) rename src/{asm => asmio}/x86/argument/registry.hpp (98%) rename src/{asm => asmio}/x86/argument/scaled.hpp (95%) rename src/{asm => asmio}/x86/const.hpp (98%) rename src/{asm => asmio}/x86/module.cpp (99%) rename src/{asm => asmio}/x86/module.hpp (86%) rename src/{asm => asmio}/x86/writer.cpp (99%) rename src/{asm => asmio}/x86/writer.hpp (99%) rename src/{asm/x86/instructions/cpu.cpp => asmio/x86/writer_cpu.cpp} (99%) rename src/{asm/x86/instructions/fpu.cpp => asmio/x86/writer_fpu.cpp} (99%) delete mode 100644 src/macro.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c2575e6..46ea29e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,8 +30,8 @@ FetchContent_Declare( FetchContent_MakeAvailable(vstl) set(ASMIO_WRITERS - src/asm/x86/writer.hpp - src/asm/aarch64/writer.hpp + src/asmio/x86/writer.hpp + src/asmio/aarch64/writer.hpp ) set(ASMIO_BRIDGES "${ASMIO_WRITERS}") diff --git a/src/asm/aarch64/argument/condition.hpp b/src/asmio/aarch64/argument/condition.hpp similarity index 96% rename from src/asm/aarch64/argument/condition.hpp rename to src/asmio/aarch64/argument/condition.hpp index ae1b600..d9b5108 100644 --- a/src/asm/aarch64/argument/condition.hpp +++ b/src/asmio/aarch64/argument/condition.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include namespace asmio::arm { diff --git a/src/asm/aarch64/argument/pattern.cpp b/src/asmio/aarch64/argument/pattern.cpp similarity index 99% rename from src/asm/aarch64/argument/pattern.cpp rename to src/asmio/aarch64/argument/pattern.cpp index dce171e..a95b212 100644 --- a/src/asm/aarch64/argument/pattern.cpp +++ b/src/asmio/aarch64/argument/pattern.cpp @@ -1,6 +1,6 @@ #include "pattern.hpp" -#include +#include namespace asmio::arm { diff --git a/src/asm/aarch64/argument/pattern.hpp b/src/asmio/aarch64/argument/pattern.hpp similarity index 96% rename from src/asm/aarch64/argument/pattern.hpp rename to src/asmio/aarch64/argument/pattern.hpp index fbf28ae..c937bc7 100644 --- a/src/asm/aarch64/argument/pattern.hpp +++ b/src/asmio/aarch64/argument/pattern.hpp @@ -1,9 +1,8 @@ #pragma once -#include -#include - -#include "external.hpp" +#include +#include +#include namespace asmio::arm { diff --git a/src/asm/aarch64/argument/registry.hpp b/src/asmio/aarch64/argument/registry.hpp similarity index 97% rename from src/asm/aarch64/argument/registry.hpp rename to src/asmio/aarch64/argument/registry.hpp index 40d805b..fbedf2f 100644 --- a/src/asm/aarch64/argument/registry.hpp +++ b/src/asmio/aarch64/argument/registry.hpp @@ -1,8 +1,8 @@ #pragma once -#include "external.hpp" -#include "../../util.hpp" -#include "out/buffer/sizes.hpp" +#include +#include +#include namespace asmio::arm { diff --git a/src/asm/aarch64/argument/shift.hpp b/src/asmio/aarch64/argument/shift.hpp similarity index 82% rename from src/asm/aarch64/argument/shift.hpp rename to src/asmio/aarch64/argument/shift.hpp index 8d135be..592ef71 100644 --- a/src/asm/aarch64/argument/shift.hpp +++ b/src/asmio/aarch64/argument/shift.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include "../../../asmio/external.hpp" enum struct ShiftType : uint8_t { LSL = 0b00, ///< shift left diff --git a/src/asm/aarch64/argument/sizing.hpp b/src/asmio/aarch64/argument/sizing.hpp similarity index 91% rename from src/asm/aarch64/argument/sizing.hpp rename to src/asmio/aarch64/argument/sizing.hpp index 8c15b36..46deb15 100644 --- a/src/asm/aarch64/argument/sizing.hpp +++ b/src/asmio/aarch64/argument/sizing.hpp @@ -1,5 +1,7 @@ #pragma once +#include + enum struct Sizing : uint8_t { UB = 0b000, ///< add unsigned byte UH = 0b001, ///< add unsigned word diff --git a/src/asm/aarch64/module.cpp b/src/asmio/aarch64/module.cpp similarity index 100% rename from src/asm/aarch64/module.cpp rename to src/asmio/aarch64/module.cpp diff --git a/src/asm/aarch64/module.hpp b/src/asmio/aarch64/module.hpp similarity index 86% rename from src/asm/aarch64/module.hpp rename to src/asmio/aarch64/module.hpp index b82cb46..4644161 100644 --- a/src/asm/aarch64/module.hpp +++ b/src/asmio/aarch64/module.hpp @@ -1,6 +1,6 @@ #pragma once -#include -#include +#include +#include namespace asmio::arm { diff --git a/src/asm/aarch64/writer.cpp b/src/asmio/aarch64/writer.cpp similarity index 100% rename from src/asm/aarch64/writer.cpp rename to src/asmio/aarch64/writer.cpp diff --git a/src/asm/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp similarity index 99% rename from src/asm/aarch64/writer.hpp rename to src/asmio/aarch64/writer.hpp index d326724..f84ba6c 100644 --- a/src/asm/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include #include "argument/sizing.hpp" #include "argument/registry.hpp" diff --git a/src/asm/aarch64/instructions/basic.cpp b/src/asmio/aarch64/writer_basic.cpp similarity index 99% rename from src/asm/aarch64/instructions/basic.cpp rename to src/asmio/aarch64/writer_basic.cpp index 375ca80..4f3c303 100644 --- a/src/asm/aarch64/instructions/basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -1,5 +1,5 @@ -#include "../writer.hpp" -#include "out/buffer/linkage.hpp" +#include "writer.hpp" +#include namespace asmio::arm { diff --git a/src/asm/aarch64/instructions/branch.cpp b/src/asmio/aarch64/writer_branch.cpp similarity index 97% rename from src/asm/aarch64/instructions/branch.cpp rename to src/asmio/aarch64/writer_branch.cpp index 0dd9ced..aca0d5c 100644 --- a/src/asm/aarch64/instructions/branch.cpp +++ b/src/asmio/aarch64/writer_branch.cpp @@ -1,6 +1,6 @@ -#include "../writer.hpp" -#include "out/buffer/linkage.hpp" +#include "writer.hpp" +#include namespace asmio::arm { diff --git a/src/out/elf/dwarf/abbrev.cpp b/src/asmio/elf/dwarf/abbrev.cpp similarity index 100% rename from src/out/elf/dwarf/abbrev.cpp rename to src/asmio/elf/dwarf/abbrev.cpp diff --git a/src/out/elf/dwarf/abbrev.hpp b/src/asmio/elf/dwarf/abbrev.hpp similarity index 95% rename from src/out/elf/dwarf/abbrev.hpp rename to src/asmio/elf/dwarf/abbrev.hpp index bc0514a..ea3a71e 100644 --- a/src/out/elf/dwarf/abbrev.hpp +++ b/src/asmio/elf/dwarf/abbrev.hpp @@ -1,10 +1,10 @@ #pragma once #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include "attribute.hpp" #include "form.hpp" diff --git a/src/out/elf/dwarf/attribute.hpp b/src/asmio/elf/dwarf/attribute.hpp similarity index 99% rename from src/out/elf/dwarf/attribute.hpp rename to src/asmio/elf/dwarf/attribute.hpp index 2f33e1a..de19117 100644 --- a/src/out/elf/dwarf/attribute.hpp +++ b/src/asmio/elf/dwarf/attribute.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace asmio { diff --git a/src/out/elf/dwarf/emitter.cpp b/src/asmio/elf/dwarf/emitter.cpp similarity index 100% rename from src/out/elf/dwarf/emitter.cpp rename to src/asmio/elf/dwarf/emitter.cpp diff --git a/src/out/elf/dwarf/emitter.hpp b/src/asmio/elf/dwarf/emitter.hpp similarity index 85% rename from src/out/elf/dwarf/emitter.hpp rename to src/asmio/elf/dwarf/emitter.hpp index 67a03ae..2a19d93 100644 --- a/src/out/elf/dwarf/emitter.hpp +++ b/src/asmio/elf/dwarf/emitter.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace asmio { diff --git a/src/out/elf/dwarf/encoding.hpp b/src/asmio/elf/dwarf/encoding.hpp similarity index 95% rename from src/out/elf/dwarf/encoding.hpp rename to src/asmio/elf/dwarf/encoding.hpp index 4ede892..73460a0 100644 --- a/src/out/elf/dwarf/encoding.hpp +++ b/src/asmio/elf/dwarf/encoding.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace asmio { diff --git a/src/out/elf/dwarf/form.hpp b/src/asmio/elf/dwarf/form.hpp similarity index 100% rename from src/out/elf/dwarf/form.hpp rename to src/asmio/elf/dwarf/form.hpp diff --git a/src/out/elf/dwarf/info.cpp b/src/asmio/elf/dwarf/info.cpp similarity index 100% rename from src/out/elf/dwarf/info.cpp rename to src/asmio/elf/dwarf/info.cpp diff --git a/src/out/elf/dwarf/info.hpp b/src/asmio/elf/dwarf/info.hpp similarity index 95% rename from src/out/elf/dwarf/info.hpp rename to src/asmio/elf/dwarf/info.hpp index ea18c99..dcd721b 100644 --- a/src/out/elf/dwarf/info.hpp +++ b/src/asmio/elf/dwarf/info.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "abbrev.hpp" namespace asmio { diff --git a/src/out/elf/dwarf/lang.hpp b/src/asmio/elf/dwarf/lang.hpp similarity index 100% rename from src/out/elf/dwarf/lang.hpp rename to src/asmio/elf/dwarf/lang.hpp diff --git a/src/out/elf/dwarf/lines.cpp b/src/asmio/elf/dwarf/lines.cpp similarity index 100% rename from src/out/elf/dwarf/lines.cpp rename to src/asmio/elf/dwarf/lines.cpp diff --git a/src/out/elf/dwarf/lines.hpp b/src/asmio/elf/dwarf/lines.hpp similarity index 99% rename from src/out/elf/dwarf/lines.hpp rename to src/asmio/elf/dwarf/lines.hpp index 7f65fb1..34950d7 100644 --- a/src/out/elf/dwarf/lines.hpp +++ b/src/asmio/elf/dwarf/lines.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include #include "emitter.hpp" diff --git a/src/out/elf/dwarf/tag.hpp b/src/asmio/elf/dwarf/tag.hpp similarity index 100% rename from src/out/elf/dwarf/tag.hpp rename to src/asmio/elf/dwarf/tag.hpp diff --git a/src/out/elf/dwarf/unit.hpp b/src/asmio/elf/dwarf/unit.hpp similarity index 100% rename from src/out/elf/dwarf/unit.hpp rename to src/asmio/elf/dwarf/unit.hpp diff --git a/src/out/elf/dwarf/version.hpp b/src/asmio/elf/dwarf/version.hpp similarity index 100% rename from src/out/elf/dwarf/version.hpp rename to src/asmio/elf/dwarf/version.hpp diff --git a/src/out/elf/export.cpp b/src/asmio/elf/export.cpp similarity index 99% rename from src/out/elf/export.cpp rename to src/asmio/elf/export.cpp index cd2a71e..af6a233 100644 --- a/src/out/elf/export.cpp +++ b/src/asmio/elf/export.cpp @@ -1,7 +1,7 @@ #include "export.hpp" #include "dwarf/lines.hpp" -#include "out/buffer/segmented.hpp" +#include namespace asmio { diff --git a/src/out/elf/export.hpp b/src/asmio/elf/export.hpp similarity index 85% rename from src/out/elf/export.hpp rename to src/asmio/elf/export.hpp index ae65ae8..198c7c3 100644 --- a/src/out/elf/export.hpp +++ b/src/asmio/elf/export.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "object.hpp" #include "model.hpp" diff --git a/src/out/elf/header.hpp b/src/asmio/elf/header.hpp similarity index 97% rename from src/out/elf/header.hpp rename to src/asmio/elf/header.hpp index db4968d..0c2c46d 100644 --- a/src/out/elf/header.hpp +++ b/src/asmio/elf/header.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include namespace asmio { diff --git a/src/out/elf/model.cpp b/src/asmio/elf/model.cpp similarity index 100% rename from src/out/elf/model.cpp rename to src/asmio/elf/model.cpp diff --git a/src/out/elf/model.hpp b/src/asmio/elf/model.hpp similarity index 97% rename from src/out/elf/model.hpp rename to src/asmio/elf/model.hpp index 265f2c6..1398105 100644 --- a/src/out/elf/model.hpp +++ b/src/asmio/elf/model.hpp @@ -1,7 +1,8 @@ #pragma once -#include -#include -#include + +#include +#include +#include #include "object.hpp" #include "header.hpp" diff --git a/src/out/elf/object.cpp b/src/asmio/elf/object.cpp similarity index 100% rename from src/out/elf/object.cpp rename to src/asmio/elf/object.cpp diff --git a/src/out/elf/object.hpp b/src/asmio/elf/object.hpp similarity index 98% rename from src/out/elf/object.hpp rename to src/asmio/elf/object.hpp index 5fa4c2f..45e4a37 100644 --- a/src/out/elf/object.hpp +++ b/src/asmio/elf/object.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include #include "dwarf/abbrev.hpp" #define DEFAULT_ELF_MOUNT 0x08048000 diff --git a/src/out/elf/relocation.hpp b/src/asmio/elf/relocation.hpp similarity index 99% rename from src/out/elf/relocation.hpp rename to src/asmio/elf/relocation.hpp index 91fc0e2..839e1eb 100644 --- a/src/out/elf/relocation.hpp +++ b/src/asmio/elf/relocation.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include namespace asmio { diff --git a/src/out/elf/section.hpp b/src/asmio/elf/section.hpp similarity index 96% rename from src/out/elf/section.hpp rename to src/asmio/elf/section.hpp index 8be72ca..702deff 100644 --- a/src/out/elf/section.hpp +++ b/src/asmio/elf/section.hpp @@ -1,8 +1,7 @@ #pragma once -#include -#include -#include +#include +#include namespace asmio { diff --git a/src/out/elf/segment.hpp b/src/asmio/elf/segment.hpp similarity index 95% rename from src/out/elf/segment.hpp rename to src/asmio/elf/segment.hpp index d38f84e..58e3619 100644 --- a/src/out/elf/segment.hpp +++ b/src/asmio/elf/segment.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include namespace asmio { diff --git a/src/out/elf/symbol.hpp b/src/asmio/elf/symbol.hpp similarity index 95% rename from src/out/elf/symbol.hpp rename to src/asmio/elf/symbol.hpp index c95fe0a..ce9f548 100644 --- a/src/out/elf/symbol.hpp +++ b/src/asmio/elf/symbol.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include enum struct ElfSymbolVisibility : uint8_t { DEFAULT = 0, ///< Visible to other modules, local references can use symbols from other modules diff --git a/src/external.hpp b/src/asmio/external.hpp similarity index 94% rename from src/external.hpp rename to src/asmio/external.hpp index 22ee9ba..e97ce67 100644 --- a/src/external.hpp +++ b/src/asmio/external.hpp @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include // systems #ifdef __linux__ diff --git a/src/asm/module.cpp b/src/asmio/module.cpp similarity index 98% rename from src/asm/module.cpp rename to src/asmio/module.cpp index 420f6df..0aeb07f 100644 --- a/src/asm/module.cpp +++ b/src/asmio/module.cpp @@ -1,7 +1,7 @@ #include "module.hpp" -#include -#include +#include +#include #include #include diff --git a/src/asm/module.hpp b/src/asmio/module.hpp similarity index 95% rename from src/asm/module.hpp rename to src/asmio/module.hpp index 771233d..08ac242 100644 --- a/src/asm/module.hpp +++ b/src/asmio/module.hpp @@ -1,9 +1,7 @@ #pragma once -#include -#include -#include -#include "util.hpp" +#include +#include // TODO decouple from tasml, those classes should be moved to asmio namespace tasml { diff --git a/src/out/buffer/executable.cpp b/src/asmio/program/executable.cpp similarity index 100% rename from src/out/buffer/executable.cpp rename to src/asmio/program/executable.cpp diff --git a/src/out/buffer/executable.hpp b/src/asmio/program/executable.hpp similarity index 99% rename from src/out/buffer/executable.hpp rename to src/asmio/program/executable.hpp index 4641eaa..b64d99b 100644 --- a/src/out/buffer/executable.hpp +++ b/src/asmio/program/executable.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include #include "segmented.hpp" #include "label.hpp" diff --git a/src/out/buffer/label.cpp b/src/asmio/program/label.cpp similarity index 100% rename from src/out/buffer/label.cpp rename to src/asmio/program/label.cpp diff --git a/src/out/buffer/label.hpp b/src/asmio/program/label.hpp similarity index 97% rename from src/out/buffer/label.hpp rename to src/asmio/program/label.hpp index 91f6356..f8c1b70 100644 --- a/src/out/buffer/label.hpp +++ b/src/asmio/program/label.hpp @@ -1,10 +1,8 @@ #pragma once -#include - -#include "external.hpp" -#include "asm/util.hpp" -#include "util/refcnt.hpp" +#include +#include +#include namespace asmio { diff --git a/src/out/buffer/linkage.cpp b/src/asmio/program/linkage.cpp similarity index 100% rename from src/out/buffer/linkage.cpp rename to src/asmio/program/linkage.cpp diff --git a/src/out/buffer/linkage.hpp b/src/asmio/program/linkage.hpp similarity index 100% rename from src/out/buffer/linkage.hpp rename to src/asmio/program/linkage.hpp diff --git a/src/out/buffer/memory.cpp b/src/asmio/program/memory.cpp similarity index 92% rename from src/out/buffer/memory.cpp rename to src/asmio/program/memory.cpp index a42c518..ac0abf3 100644 --- a/src/out/buffer/memory.cpp +++ b/src/asmio/program/memory.cpp @@ -1,6 +1,7 @@ #include "memory.hpp" -#include -#include + +#include +#include namespace asmio { diff --git a/src/out/buffer/memory.hpp b/src/asmio/program/memory.hpp similarity index 95% rename from src/out/buffer/memory.hpp rename to src/asmio/program/memory.hpp index 6e98c86..ff667e4 100644 --- a/src/out/buffer/memory.hpp +++ b/src/asmio/program/memory.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include namespace asmio { diff --git a/src/out/buffer/segmented.cpp b/src/asmio/program/segmented.cpp similarity index 100% rename from src/out/buffer/segmented.cpp rename to src/asmio/program/segmented.cpp diff --git a/src/out/buffer/segmented.hpp b/src/asmio/program/segmented.hpp similarity index 97% rename from src/out/buffer/segmented.hpp rename to src/asmio/program/segmented.hpp index 5b630cc..02357f1 100644 --- a/src/out/buffer/segmented.hpp +++ b/src/asmio/program/segmented.hpp @@ -1,10 +1,10 @@ #pragma once -#include -#include -#include +#include +#include +#include +#include -#include "external.hpp" #include "label.hpp" #include "memory.hpp" diff --git a/src/out/buffer/sizes.hpp b/src/asmio/program/sizes.hpp similarity index 82% rename from src/out/buffer/sizes.hpp rename to src/asmio/program/sizes.hpp index a13358e..7bd5513 100644 --- a/src/out/buffer/sizes.hpp +++ b/src/asmio/program/sizes.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include namespace asmio { diff --git a/src/out/buffer/writer.cpp b/src/asmio/program/writer.cpp similarity index 100% rename from src/out/buffer/writer.cpp rename to src/asmio/program/writer.cpp diff --git a/src/out/buffer/writer.hpp b/src/asmio/program/writer.hpp similarity index 98% rename from src/out/buffer/writer.hpp rename to src/asmio/program/writer.hpp index 0b54544..1cf22c7 100644 --- a/src/out/buffer/writer.hpp +++ b/src/asmio/program/writer.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include #include "label.hpp" #include "segmented.hpp" diff --git a/src/util.hpp b/src/asmio/util.hpp similarity index 99% rename from src/util.hpp rename to src/asmio/util.hpp index a74d751..4c43e48 100644 --- a/src/util.hpp +++ b/src/asmio/util.hpp @@ -1,9 +1,6 @@ #pragma once -#include -#include -#include -#include +#include template concept trivially_copyable = std::is_trivially_copyable_v; diff --git a/src/out/chunk/buffer.cpp b/src/asmio/util/chunk.cpp similarity index 99% rename from src/out/chunk/buffer.cpp rename to src/asmio/util/chunk.cpp index c134c29..ebe3997 100644 --- a/src/out/chunk/buffer.cpp +++ b/src/asmio/util/chunk.cpp @@ -1,4 +1,4 @@ -#include "buffer.hpp" +#include "chunk.hpp" namespace asmio { diff --git a/src/out/chunk/buffer.hpp b/src/asmio/util/chunk.hpp similarity index 99% rename from src/out/chunk/buffer.hpp rename to src/asmio/util/chunk.hpp index f129927..476064f 100644 --- a/src/out/chunk/buffer.hpp +++ b/src/asmio/util/chunk.hpp @@ -1,8 +1,6 @@ #pragma once -#include -#include -#include "util.hpp" +#include namespace asmio { diff --git a/src/out/chunk/codecs.cpp b/src/asmio/util/codecs.cpp similarity index 100% rename from src/out/chunk/codecs.cpp rename to src/asmio/util/codecs.cpp diff --git a/src/out/chunk/codecs.hpp b/src/asmio/util/codecs.hpp similarity index 97% rename from src/out/chunk/codecs.hpp rename to src/asmio/util/codecs.hpp index bc75519..9e808c5 100644 --- a/src/out/chunk/codecs.hpp +++ b/src/asmio/util/codecs.hpp @@ -1,6 +1,6 @@ #pragma once -#include "buffer.hpp" +#include "chunk.hpp" namespace asmio { diff --git a/src/util/indexer.hpp b/src/asmio/util/indexer.hpp similarity index 99% rename from src/util/indexer.hpp rename to src/asmio/util/indexer.hpp index f735dde..68e1d73 100644 --- a/src/util/indexer.hpp +++ b/src/asmio/util/indexer.hpp @@ -1,4 +1,5 @@ #pragma once + #include template diff --git a/src/asm/util.hpp b/src/asmio/util/macro.hpp similarity index 60% rename from src/asm/util.hpp rename to src/asmio/util/macro.hpp index 13718c7..c20dfa8 100644 --- a/src/asm/util.hpp +++ b/src/asmio/util/macro.hpp @@ -1,7 +1,5 @@ #pragma once -#include - struct StaticBlock { int operator +(const std::function& block) { @@ -11,6 +9,14 @@ struct StaticBlock { }; +#define JOIN_PARTS_RESOLVED(first, second) first##second + +/// Allows one to merge two tokens, even macro values +#define JOIN_PARTS(first, second) JOIN_PARTS_RESOLVED(first, second) + +/// Mark that no padding should be used in marked struct, ever +#define PACKED __attribute__((__packed__)) + /// Used to mark prefixes for the python codegen #define PREFIX BufferWriter& diff --git a/src/util/pool.hpp b/src/asmio/util/pool.hpp similarity index 99% rename from src/util/pool.hpp rename to src/asmio/util/pool.hpp index 0b3e0b3..19af802 100644 --- a/src/util/pool.hpp +++ b/src/asmio/util/pool.hpp @@ -1,4 +1,5 @@ #pragma once + #include #include #include diff --git a/src/util/refcnt.hpp b/src/asmio/util/refcnt.hpp similarity index 96% rename from src/util/refcnt.hpp rename to src/asmio/util/refcnt.hpp index 5259b24..485f5c2 100644 --- a/src/util/refcnt.hpp +++ b/src/asmio/util/refcnt.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include namespace asmio { diff --git a/src/util/set.hpp b/src/asmio/util/set.hpp similarity index 96% rename from src/util/set.hpp rename to src/asmio/util/set.hpp index 1cd24a4..39fae84 100644 --- a/src/util/set.hpp +++ b/src/asmio/util/set.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include namespace asmio::util { diff --git a/src/util/strings.hpp b/src/asmio/util/strings.hpp similarity index 100% rename from src/util/strings.hpp rename to src/asmio/util/strings.hpp diff --git a/src/util/tmp.cpp b/src/asmio/util/tmp.cpp similarity index 95% rename from src/util/tmp.cpp rename to src/asmio/util/tmp.cpp index cc9421c..2b21fc0 100644 --- a/src/util/tmp.cpp +++ b/src/asmio/util/tmp.cpp @@ -1,6 +1,6 @@ #include "tmp.hpp" -#include +#include namespace asmio::util { diff --git a/src/util/tmp.hpp b/src/asmio/util/tmp.hpp similarity index 94% rename from src/util/tmp.hpp rename to src/asmio/util/tmp.hpp index bd4a7f2..20dd584 100644 --- a/src/util/tmp.hpp +++ b/src/asmio/util/tmp.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include namespace asmio::util { diff --git a/src/asm/x86/argument/location.cpp b/src/asmio/x86/argument/location.cpp similarity index 99% rename from src/asm/x86/argument/location.cpp rename to src/asmio/x86/argument/location.cpp index 3a3b0bf..ff58d68 100644 --- a/src/asm/x86/argument/location.cpp +++ b/src/asmio/x86/argument/location.cpp @@ -1,4 +1,3 @@ - #include "location.hpp" namespace asmio::x86 { diff --git a/src/asm/x86/argument/location.hpp b/src/asmio/x86/argument/location.hpp similarity index 98% rename from src/asm/x86/argument/location.hpp rename to src/asmio/x86/argument/location.hpp index 386fd1c..10189d1 100644 --- a/src/asm/x86/argument/location.hpp +++ b/src/asmio/x86/argument/location.hpp @@ -1,11 +1,12 @@ #pragma once -#include "external.hpp" -#include "out/buffer/sizes.hpp" +#include +#include +#include +#include + #include "registry.hpp" #include "scaled.hpp" -#include "out/buffer/label.hpp" -#include "../../util.hpp" namespace asmio::x86 { diff --git a/src/asm/x86/argument/registry.hpp b/src/asmio/x86/argument/registry.hpp similarity index 98% rename from src/asm/x86/argument/registry.hpp rename to src/asmio/x86/argument/registry.hpp index 068009b..c4762ae 100644 --- a/src/asm/x86/argument/registry.hpp +++ b/src/asmio/x86/argument/registry.hpp @@ -1,10 +1,9 @@ #pragma once -#include "external.hpp" -#include "asm/x86/const.hpp" -#include "out/buffer/sizes.hpp" -#include "asm/util.hpp" -#include +#include +#include +#include +#include namespace asmio::x86 { diff --git a/src/asm/x86/argument/scaled.hpp b/src/asmio/x86/argument/scaled.hpp similarity index 95% rename from src/asm/x86/argument/scaled.hpp rename to src/asmio/x86/argument/scaled.hpp index b26560b..061fc74 100644 --- a/src/asm/x86/argument/scaled.hpp +++ b/src/asmio/x86/argument/scaled.hpp @@ -1,8 +1,9 @@ #pragma once -#include "external.hpp" +#include +#include + #include "registry.hpp" -#include "asm/x86/const.hpp" namespace asmio::x86 { diff --git a/src/asm/x86/const.hpp b/src/asmio/x86/const.hpp similarity index 98% rename from src/asm/x86/const.hpp rename to src/asmio/x86/const.hpp index c0c20f7..7ca16e5 100644 --- a/src/asm/x86/const.hpp +++ b/src/asmio/x86/const.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include namespace asmio::x86 { diff --git a/src/asm/x86/module.cpp b/src/asmio/x86/module.cpp similarity index 99% rename from src/asm/x86/module.cpp rename to src/asmio/x86/module.cpp index a6e082c..7669221 100644 --- a/src/asm/x86/module.cpp +++ b/src/asmio/x86/module.cpp @@ -1,7 +1,7 @@ #include "module.hpp" #include "writer.hpp" -#include "src/tasml/stream.hpp" +#include namespace asmio::x86 { diff --git a/src/asm/x86/module.hpp b/src/asmio/x86/module.hpp similarity index 86% rename from src/asm/x86/module.hpp rename to src/asmio/x86/module.hpp index 51fe2e0..81c5126 100644 --- a/src/asm/x86/module.hpp +++ b/src/asmio/x86/module.hpp @@ -1,6 +1,7 @@ #pragma once -#include -#include + +#include +#include namespace asmio::x86 { diff --git a/src/asm/x86/writer.cpp b/src/asmio/x86/writer.cpp similarity index 99% rename from src/asm/x86/writer.cpp rename to src/asmio/x86/writer.cpp index 3e99d4c..dcd40c9 100644 --- a/src/asm/x86/writer.cpp +++ b/src/asmio/x86/writer.cpp @@ -1,9 +1,6 @@ #include "writer.hpp" -// private libs -#include - -#include "out/buffer/linkage.hpp" +#include namespace asmio::x86 { diff --git a/src/asm/x86/writer.hpp b/src/asmio/x86/writer.hpp similarity index 99% rename from src/asm/x86/writer.hpp rename to src/asmio/x86/writer.hpp index 783b6a2..4e8ae6c 100644 --- a/src/asm/x86/writer.hpp +++ b/src/asmio/x86/writer.hpp @@ -1,10 +1,10 @@ #pragma once -#include "external.hpp" +#include +#include +#include + #include "argument/location.hpp" -#include "out/buffer/segmented.hpp" -#include "../util.hpp" -#include "out/buffer/writer.hpp" namespace asmio::x86 { diff --git a/src/asm/x86/instructions/cpu.cpp b/src/asmio/x86/writer_cpu.cpp similarity index 99% rename from src/asm/x86/instructions/cpu.cpp rename to src/asmio/x86/writer_cpu.cpp index 2ba5544..b2ff023 100644 --- a/src/asm/x86/instructions/cpu.cpp +++ b/src/asmio/x86/writer_cpu.cpp @@ -1,4 +1,4 @@ -#include "asm/x86/writer.hpp" +#include "writer.hpp" namespace asmio::x86 { diff --git a/src/asm/x86/instructions/fpu.cpp b/src/asmio/x86/writer_fpu.cpp similarity index 99% rename from src/asm/x86/instructions/fpu.cpp rename to src/asmio/x86/writer_fpu.cpp index 33ce12a..ce48523 100644 --- a/src/asm/x86/instructions/fpu.cpp +++ b/src/asmio/x86/writer_fpu.cpp @@ -1,4 +1,4 @@ -#include "asm/x86/writer.hpp" +#include "writer.hpp" namespace asmio::x86 { diff --git a/src/macro.hpp b/src/macro.hpp deleted file mode 100644 index 3a4bb74..0000000 --- a/src/macro.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#define JOIN_PARTS_RESOLVED(first, second) first##second - -/// Allows one to merge two tokens, even macro values -#define JOIN_PARTS(first, second) JOIN_PARTS_RESOLVED(first, second) - -/// Mark that no padding should be used in marked struct, ever -#define PACKED __attribute__((__packed__)) \ No newline at end of file diff --git a/src/tasml/args.hpp b/src/tasml/args.hpp index 1a2d31f..729448b 100644 --- a/src/tasml/args.hpp +++ b/src/tasml/args.hpp @@ -1,7 +1,7 @@ #pragma once -#include "external.hpp" -#include "util.hpp" +#include "../asmio/external.hpp" +#include "../asmio/util.hpp" namespace tasml { diff --git a/src/tasml/error.hpp b/src/tasml/error.hpp index 7147d97..39c1227 100644 --- a/src/tasml/error.hpp +++ b/src/tasml/error.hpp @@ -1,7 +1,7 @@ #pragma once -#include "external.hpp" -#include "out/buffer/segmented.hpp" +#include +#include namespace tasml { diff --git a/src/tasml/main.cpp b/src/tasml/main.cpp index ba18a2d..a729196 100644 --- a/src/tasml/main.cpp +++ b/src/tasml/main.cpp @@ -1,13 +1,13 @@ -#include "external.hpp" -#include "util.hpp" +#include +#include #include "args.hpp" #include "error.hpp" -#include +#include // private libs #include #include -#include +#include #include "top.hpp" diff --git a/src/tasml/stream.hpp b/src/tasml/stream.hpp index 000be6c..88bbb76 100644 --- a/src/tasml/stream.hpp +++ b/src/tasml/stream.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include "../asmio/external.hpp" #include "predicate.hpp" #include "token.hpp" diff --git a/src/tasml/token.cpp b/src/tasml/token.cpp index 22ca45d..91fd918 100644 --- a/src/tasml/token.cpp +++ b/src/tasml/token.cpp @@ -1,7 +1,7 @@ #include "token.hpp" -#include +#include namespace tasml { diff --git a/src/tasml/token.hpp b/src/tasml/token.hpp index b25811f..4383f55 100644 --- a/src/tasml/token.hpp +++ b/src/tasml/token.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include "../asmio/external.hpp" namespace tasml { diff --git a/src/tasml/tokenizer.cpp b/src/tasml/tokenizer.cpp index 415147c..0bc4ad2 100644 --- a/src/tasml/tokenizer.cpp +++ b/src/tasml/tokenizer.cpp @@ -1,5 +1,5 @@ #include "tokenizer.hpp" -#include "util.hpp" +#include "../asmio/util.hpp" // private library #include diff --git a/src/tasml/tokenizer.hpp b/src/tasml/tokenizer.hpp index 2699539..0d56175 100644 --- a/src/tasml/tokenizer.hpp +++ b/src/tasml/tokenizer.hpp @@ -1,6 +1,6 @@ #pragma once -#include "external.hpp" +#include "../asmio/external.hpp" #include "token.hpp" #include "error.hpp" diff --git a/src/tasml/top.cpp b/src/tasml/top.cpp index fa195ca..12f79c6 100644 --- a/src/tasml/top.cpp +++ b/src/tasml/top.cpp @@ -1,7 +1,7 @@ #include "top.hpp" -#include -#include +#include +#include #include "tokenizer.hpp" diff --git a/src/tasml/top.hpp b/src/tasml/top.hpp index b5b66a7..bdd0ac9 100644 --- a/src/tasml/top.hpp +++ b/src/tasml/top.hpp @@ -1,6 +1,6 @@ #pragma once -#include -#include +#include +#include #include "error.hpp" #include "stream.hpp" diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 5dda06a..26bbd87 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -4,11 +4,11 @@ #define VSTL_SUBMODULE true #include "vstl.hpp" -#include "asm/aarch64/writer.hpp" -#include "out/buffer/executable.hpp" -#include +#include +#include +#include +#include #include -#include #include "test.hpp" diff --git a/test/elf.cpp b/test/elf.cpp index 48c2850..e31f4af 100644 --- a/test/elf.cpp +++ b/test/elf.cpp @@ -4,14 +4,14 @@ #define VSTL_SUBMODULE true #include -#include -#include -#include -#include +#include +#include +#include +#include #include -#include "util/tmp.hpp" -#include "out/elf/export.hpp" +#include +#include #include "test.hpp" namespace test { diff --git a/test/tasml.cpp b/test/tasml.cpp index bf9d7ca..90e8ade 100644 --- a/test/tasml.cpp +++ b/test/tasml.cpp @@ -8,12 +8,12 @@ #include #include #include -#include -#include +#include +#include #include "test.hpp" #include "vstl.hpp" -#include "out/buffer/executable.hpp" +#include namespace test { diff --git a/test/unit.cpp b/test/unit.cpp index cb34f9f..40b935d 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -3,12 +3,12 @@ #define VSTL_PRINT_SKIP_REASON true #define VSTL_SUBMODULE true -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include "vstl.hpp" diff --git a/test/x86.cpp b/test/x86.cpp index bcd9652..9057fbe 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -5,14 +5,14 @@ #define VSTL_PRINT_MODULES true #include "vstl.hpp" -#include "asm/x86/writer.hpp" -#include +#include +#include // private libs #include -#include +#include #include -#include +#include #include "test.hpp" From 8fe8232dc201ba698025fe2aa0dd30a060555862 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Thu, 9 Apr 2026 08:05:19 +0200 Subject: [PATCH 03/44] New debug dump function --- test/test.hpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/test.hpp b/test/test.hpp index 3150a97..775648f 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -54,4 +54,48 @@ namespace test { return out; } + inline void dump(asmio::SegmentedBuffer& buffer) { + + if (buffer.elf_machine == asmio::ElfMachine::NONE) { + buffer.elf_machine = asmio::ElfMachine::NATIVE; + } + + asmio::ObjectFile baked = asmio::to_elf(buffer, asmio::Label::UNSET).bake(); + asmio::util::TempFile temp {baked}; + + std::string out = call_shell("objdump --visualize-jumps -wxd -Mintel " + temp.path()); + printf("%s\n", out.c_str()); + + printf("\nAuto-generated assertions:\n\n"); + printf("\tbuffer.link(0);\n"); + + int i = 0; + + for (auto& segment : buffer.segments()) { + int count = segment.buffer.size(); + + if (count == 0) { + i ++; + continue; + } + + count --; + + printf("\tstd::vector s%d = {", i); + + for (int j = 0; j <= count; j ++) { + printf("0x%02x", segment.buffer[j]); + + if (count != j) { + printf(", "); + } + } + + printf("};\n"); + printf("\tCHECK(buffer.segments()[%d].buffer, s%d); // %s\n\n", i, i, segment.name.c_str()); + i ++; + } + + } + } \ No newline at end of file From ed4e11b0572fe627005681cb67fa82f0c5765f0e Mon Sep 17 00:00:00 2001 From: magistermaks Date: Thu, 9 Apr 2026 08:07:00 +0200 Subject: [PATCH 04/44] Add x86 SSE operations --- src/asmio/program/sizes.hpp | 11 +- src/asmio/x86/argument/condition.hpp | 18 +++ src/asmio/x86/argument/location.hpp | 12 +- src/asmio/x86/argument/registry.hpp | 26 ++- src/asmio/x86/module.cpp | 22 ++- src/asmio/x86/writer.hpp | 50 ++++++ src/asmio/x86/writer_sse.cpp | 231 +++++++++++++++++++++++++++ test/x86.cpp | 222 ++++++++++++++++++++++++- 8 files changed, 576 insertions(+), 16 deletions(-) create mode 100644 src/asmio/x86/argument/condition.hpp create mode 100644 src/asmio/x86/writer_sse.cpp diff --git a/src/asmio/program/sizes.hpp b/src/asmio/program/sizes.hpp index 7bd5513..507cec8 100644 --- a/src/asmio/program/sizes.hpp +++ b/src/asmio/program/sizes.hpp @@ -6,11 +6,12 @@ namespace asmio { enum Size : uint8_t { VOID = 0, - BYTE = 1, - WORD = 2, - DWORD = 4, - QWORD = 8, - TWORD = 10, + BYTE = 1, // 8 bits + WORD = 2, // 16 bits + DWORD = 4, // 32 bits + QWORD = 8, // 64 bits + TWORD = 10, // 80 bits + XMMWORD = 16, // 128 bits }; } \ No newline at end of file diff --git a/src/asmio/x86/argument/condition.hpp b/src/asmio/x86/argument/condition.hpp new file mode 100644 index 0000000..2e11457 --- /dev/null +++ b/src/asmio/x86/argument/condition.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace asmio::x86 { + + enum struct SimdCondition : uint8_t { + EQ = 0, ///< Equal (ordered, non-signaling) + LT = 1, ///< Less-than (ordered, signaling) + LE = 2, ///< Less-than-or-equal (ordered, signaling) + RD = 7, ///< Ordered (ordered, non-signaling) + NEQ = 4, ///< Not-equal (unordered, non-signaling) + NLT = 5, ///< Not-less-than (unordered, signaling) + NLE = 6, ///< Not-greater-than (unordered, signaling) + NRD = 3, ///< Unordered (unordered, non-signaling) + }; + +} \ No newline at end of file diff --git a/src/asmio/x86/argument/location.hpp b/src/asmio/x86/argument/location.hpp index 10189d1..ac4fbac 100644 --- a/src/asmio/x86/argument/location.hpp +++ b/src/asmio/x86/argument/location.hpp @@ -50,6 +50,14 @@ namespace asmio::x86 { public: Location ref() const { + if (base != UNSET && !base.is(Registry::GENERAL)) { + throw std::runtime_error {"Can't dereference a non-general purpose base register"}; + } + + if (index != UNSET && !index.is(Registry::GENERAL)) { + throw std::runtime_error {"Can't dereference a non-general purpose index register"}; + } + check_non_referential("Can't reference a reference!"); return Location {base, index, scale, offset, label, VOID, true}; } @@ -93,8 +101,8 @@ namespace asmio::x86 { } /// Checks if this location is a simple un-referenced register - constexpr bool is_simple() const { - return base.is(Registry::GENERAL) && !is_indexed() && offset == 0 && !reference && !is_labeled(); + constexpr bool is_simple(Registry::Flag mask = Registry::STANDARD) const { + return base.is(mask) && !is_indexed() && offset == 0 && !reference && !is_labeled(); } /// Checks if this location is a simple un-referenced accumulator, used when encoding short-forms diff --git a/src/asmio/x86/argument/registry.hpp b/src/asmio/x86/argument/registry.hpp index c4762ae..141a762 100644 --- a/src/asmio/x86/argument/registry.hpp +++ b/src/asmio/x86/argument/registry.hpp @@ -43,10 +43,11 @@ namespace asmio::x86 { NONE = 0b0000000, GENERAL = 0b0000001, - FLOATING = 0b0000010, + STANDARD = 0b0111111, ACCUMULATOR = 0b0000100, // this is the accumulator (RAX/EAX/AX) REX = 0b0001000, // this registers requires the REX prefix to be encoded HIGH_BYTE = 0b0010000, // registers that CANT be used with REX prefix present + XMM = 0b0100000, // registers used in the SSE x86 extension }; const uint8_t size; // size in bytes @@ -122,7 +123,7 @@ namespace asmio::x86 { constexpr Registry BP {WORD, 0b0101, Registry::GENERAL}; constexpr Registry ESP {DWORD, 0b0100, Registry::GENERAL}; constexpr Registry SP {WORD, 0b0100, Registry::GENERAL}; - constexpr Registry ST {TWORD, 0b0000, Registry::FLOATING}; + constexpr Registry ST {TWORD, 0b0000, Registry::NONE}; /* * Amd64 surrogates - uniform byte registers @@ -178,4 +179,25 @@ namespace asmio::x86 { constexpr Registry R15D {DWORD, 0b1111, Registry::GENERAL | Registry::REX}; constexpr Registry R15 {QWORD, 0b1111, Registry::GENERAL | Registry::REX}; + /* + * SSE + */ + + constexpr Registry XMM0 {XMMWORD, 0b0000, Registry::XMM}; + constexpr Registry XMM1 {XMMWORD, 0b0001, Registry::XMM}; + constexpr Registry XMM2 {XMMWORD, 0b0010, Registry::XMM}; + constexpr Registry XMM3 {XMMWORD, 0b0011, Registry::XMM}; + constexpr Registry XMM4 {XMMWORD, 0b0100, Registry::XMM}; + constexpr Registry XMM5 {XMMWORD, 0b0101, Registry::XMM}; + constexpr Registry XMM6 {XMMWORD, 0b0110, Registry::XMM}; + constexpr Registry XMM7 {XMMWORD, 0b0111, Registry::XMM}; + constexpr Registry XMM8 {XMMWORD, 0b1000, Registry::XMM | Registry::REX}; + constexpr Registry XMM9 {XMMWORD, 0b1001, Registry::XMM | Registry::REX}; + constexpr Registry XMM10 {XMMWORD, 0b1010, Registry::XMM | Registry::REX}; + constexpr Registry XMM11 {XMMWORD, 0b1011, Registry::XMM | Registry::REX}; + constexpr Registry XMM12 {XMMWORD, 0b1100, Registry::XMM | Registry::REX}; + constexpr Registry XMM13 {XMMWORD, 0b1101, Registry::XMM | Registry::REX}; + constexpr Registry XMM14 {XMMWORD, 0b1110, Registry::XMM | Registry::REX}; + constexpr Registry XMM15 {XMMWORD, 0b1111, Registry::XMM | Registry::REX}; + } \ No newline at end of file diff --git a/src/asmio/x86/module.cpp b/src/asmio/x86/module.cpp index 7669221..cde8fec 100644 --- a/src/asmio/x86/module.cpp +++ b/src/asmio/x86/module.cpp @@ -263,9 +263,25 @@ namespace asmio::x86 { } template - Location parse_argument(TokenStream stream) { - static_assert(std::is_same_v, "x86 can only accept Location classes as arguments"); - return parse_location(stream); + T parse_argument(TokenStream stream) { + // TODO FIXME! + + if constexpr (std::is_same_v) { + const Token& token = stream.expect(Token::NAME); + return token_to_register(&token); + } else + + if constexpr (std::is_same_v) { + return parse_location(stream); + } else + + if constexpr (std::is_same_v) { + return SimdCondition::NLT; + } + + else { + static_assert(false, "x86 can only accept Location classes as arguments"); + } } # include "generated/x86.hpp" diff --git a/src/asmio/x86/writer.hpp b/src/asmio/x86/writer.hpp index 4e8ae6c..0622b86 100644 --- a/src/asmio/x86/writer.hpp +++ b/src/asmio/x86/writer.hpp @@ -4,6 +4,7 @@ #include #include +#include "argument/condition.hpp" #include "argument/location.hpp" namespace asmio::x86 { @@ -65,6 +66,11 @@ namespace asmio::x86 { /// Used for constructing the 'set byte' family of instructions void put_inst_setx(const Location& dst, uint8_t lopcode); + /// Put SSE double XMM register operand instruction + void put_inst_sse_2xmm(uint8_t opcode, Registry dst, Registry src); + void put_inst_sse(uint8_t opcode, Registry reg, const Location& loc); + void put_inst_sse_sized(uint8_t opcode, Registry reg, const Location& loc, uint8_t size); + /// Add the REX.W prefix void put_rex_w(); @@ -354,6 +360,50 @@ namespace asmio::x86 { INST put_fdivr(Location dst, Location src); ///< Reverse Divide INST put_fdivrp(Location dst); ///< Reverse Divide And Pop + // sse + INST put_movaps(Location dst, Location src); ///< Move Aligned Packed Single Precision Floating-Point Values + INST put_movhlps(Registry dst, Registry src); ///< Move Packed Single Precision Floating-Point Values High to Low + INST put_movlhps(Registry dst, Registry src); ///< Move Packed Single Precision Floating-Point Values Low to High + INST put_movhps(Location dst, Location src); ///< Move two packed single precision floating-point values from m64 to high quadword of dst + INST put_movlps(Location dst, Location src); ///< Move two packed single precision floating-point values from m64 to low quadword of dst + INST put_movmskps(Registry dst, Registry src); ///< Extract Packed Single Precision Floating-Point Sign Mask + // TODO: movss + // TODO: movups + INST put_addps(Registry dst, Location src); ///< Add packed single precision floating-point values + INST put_addss(Registry dst, Location src); ///< + INST put_divps(Registry dst, Location src); ///< Divide packed single precision floating-point values + INST put_divss(Registry dst, Location src); ///< + INST put_maxps(Registry dst, Location src); ///< Maximum packed single precision floating-point values + INST put_maxss(Registry dst, Location src); ///< + INST put_minps(Registry dst, Location src); ///< Minimum packed single precision floating-point values + INST put_minss(Registry dst, Location src); ///< + INST put_mulps(Registry dst, Location src); ///< Minimum packed single precision floating-point values + INST put_mulss(Registry dst, Location src); ///< + INST put_rcpps(Registry dst, Location src); ///< Compute reciprocals of packed single precision floating-point values + INST put_rcpss(Registry dst, Location src); ///< + INST put_rsqrtps(Registry dst, Location src); ///< Compute reciprocals of square roots of packed single precision floating-point values + INST put_rsqrtss(Registry dst, Location src); ///< + INST put_sqrtps(Registry dst, Location src); ///< Compute square roots of packed single precision floating-point values + INST put_sqrtss(Registry dst, Location src); ///< + INST put_subps(Registry dst, Location src); ///< Subtract packed single precision floating-point values + INST put_subss(Registry dst, Location src); ///< + INST put_cmpps(Registry dst, Location src, SimdCondition cond); ///< Compare packed 32 bit floating point values + INST put_cmpss(Registry dst, Location src, SimdCondition cond); ///< + // TODO: comiss + // TODO: ucomiss + INST put_andnps(Registry dst, Location src); ///< Bitwise logical AND NOT of packed 32 bit values + INST put_andps(Registry dst, Location src); ///< Bitwise logical AND of packed 32 bit values + INST put_orps(Registry dst, Location src); ///< Bitwise logical OR of packed 32 bit values + INST put_xorps(Registry dst, Location src); ///< Bitwise logical XOR of packed 32 bit values + // TODO: shufps + // TODO: unpckhps + // TODO: unpcklps + // TODO: cvtpi2ps + // TODO: cvtps2pi + INST put_cvtsi2ss(Registry dst, Location src); ///< Convert Doubleword Integer to Scalar Single Precision Floating-Point Value + // TODO: cvtss2si + + }; } diff --git a/src/asmio/x86/writer_sse.cpp b/src/asmio/x86/writer_sse.cpp new file mode 100644 index 0000000..880159c --- /dev/null +++ b/src/asmio/x86/writer_sse.cpp @@ -0,0 +1,231 @@ +#include "writer.hpp" + +namespace asmio::x86 { + + void BufferWriter::put_inst_sse_2xmm(uint8_t opcode, Registry dst, Registry src) { + if (dst.is(Registry::XMM) && src.is(Registry::XMM)) { + put_inst_std(opcode, src, dst.pack(), XMMWORD, true); + return; + } + + throw std::runtime_error {"Invalid operands, expected two XMM registers"}; + } + + void BufferWriter::put_inst_sse(uint8_t opcode, Registry reg, const Location& loc) { + + if (!reg.is(Registry::XMM)) { + throw std::runtime_error {"Invalid operand, expected XMM register"}; + } + + if (loc.size != XMMWORD) { + throw std::runtime_error {"Invalid operand, expected xmmword register or memory"}; + } + + put_inst_std(opcode, loc, reg.pack(), XMMWORD, true); + } + + void BufferWriter::put_inst_sse_sized(uint8_t opcode, Registry reg, const Location& loc, uint8_t size) { + + if (!reg.is(Registry::XMM)) { + throw std::runtime_error {"Invalid operand, expected XMM register"}; + } + + if ((loc.is_simple() && loc.base.is(Registry::XMM)) || (loc.is_memory() && loc.size == size)) { + put_byte(0xF3); + put_inst_std(opcode, loc, reg.pack(), DWORD, true); + return; + } + + throw std::runtime_error {"Invalid operands"}; + } + + void BufferWriter::put_movaps(Location dst, Location src) { + + if (dst.is_simple()) { + put_inst_sse(0x28, dst.base, src); + return; + } + + if (src.is_simple()) { + put_inst_sse(0x29, src.base, dst); + return; + } + + throw std::runtime_error {"Invalid operands"}; + } + + void BufferWriter::put_movhlps(Registry dst, Registry src) { + put_inst_sse_2xmm(0x12, dst, src); + } + + void BufferWriter::put_movlhps(Registry dst, Registry src) { + put_inst_sse_2xmm(0x16, dst, src); + } + + void BufferWriter::put_movhps(Location dst, Location src) { + + if (dst.is_simple() && src.is_memory()) { + if (src.size != QWORD) { + throw std::runtime_error {"Invalid operand, expected QWORD memory reference"}; + } + + put_inst_std(0x16, src, dst.base.pack(), DWORD, true); + return; + } + + if (src.is_simple() && dst.is_memory()) { + if (dst.size != QWORD) { + throw std::runtime_error {"Invalid operand, expected QWORD memory reference"}; + } + + put_inst_std(0x17, dst, src.base.pack(), DWORD, true); + return; + } + + throw std::runtime_error {"Invalid operands"}; + } + + void BufferWriter::put_movlps(Location dst, Location src) { + + if (dst.is_simple() && src.is_memory()) { + if (src.size != QWORD) { + throw std::runtime_error {"Invalid operand, expected QWORD memory reference"}; + } + + put_inst_std(0x12, src, dst.base.pack(), DWORD, true); + return; + } + + if (src.is_simple() && dst.is_memory()) { + if (dst.size != QWORD) { + throw std::runtime_error {"Invalid operand, expected QWORD memory reference"}; + } + + put_inst_std(0x13, dst, src.base.pack(), DWORD, true); + return; + } + + throw std::runtime_error {"Invalid operands"}; + } + + void BufferWriter::put_movmskps(Registry dst, Registry src) { + + if (!dst.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid destination operand, expected general purpose register"}; + } + + if (dst.size != QWORD && dst.size != DWORD) { + throw std::runtime_error {"Invalid destination operand, expected dword or qword register"}; + } + + if (!src.is(Registry::XMM)) { + throw std::runtime_error {"Invalid source operand, expected XMM register"}; + } + + put_inst_std(0x50, src, dst.pack(), dst.size, true); + } + + void BufferWriter::put_addps(Registry dst, Location src) { + put_inst_sse(0x58, dst, src); + } + + void BufferWriter::put_addss(Registry dst, Location src) { + put_inst_sse_sized(0x58, dst, src, DWORD); + } + + void BufferWriter::put_divps(Registry dst, Location src) { + put_inst_sse(0x5E, dst, src); + } + + void BufferWriter::put_divss(Registry dst, Location src) { + put_inst_sse_sized(0x5E, dst, src, DWORD); + } + + void BufferWriter::put_maxps(Registry dst, Location src) { + put_inst_sse(0x5F, dst, src); + } + + void BufferWriter::put_maxss(Registry dst, Location src) { + put_inst_sse_sized(0x5F, dst, src, DWORD); + } + + void BufferWriter::put_minps(Registry dst, Location src) { + put_inst_sse(0x5D, dst, src); + } + + void BufferWriter::put_minss(Registry dst, Location src) { + put_inst_sse_sized(0x5D, dst, src, DWORD); + } + + void BufferWriter::put_mulps(Registry dst, Location src) { + put_inst_sse(0x59, dst, src); + } + + void BufferWriter::put_mulss(Registry dst, Location src) { + put_inst_sse_sized(0x59, dst, src, DWORD); + } + + void BufferWriter::put_rcpps(Registry dst, Location src) { + put_inst_sse(0x53, dst, src); + } + + void BufferWriter::put_rcpss(Registry dst, Location src) { + put_inst_sse_sized(0x53, dst, src, DWORD); + } + + void BufferWriter::put_rsqrtps(Registry dst, Location src) { + put_inst_sse(0x52, dst, src); + } + + void BufferWriter::put_rsqrtss(Registry dst, Location src) { + put_inst_sse_sized(0x52, dst, src, DWORD); + } + + void BufferWriter::put_sqrtps(Registry dst, Location src) { + put_inst_sse(0x51, dst, src); + } + + void BufferWriter::put_sqrtss(Registry dst, Location src) { + put_inst_sse_sized(0x51, dst, src, DWORD); + } + + void BufferWriter::put_subps(Registry dst, Location src) { + put_inst_sse(0x5C, dst, src); + } + + void BufferWriter::put_subss(Registry dst, Location src) { + put_inst_sse_sized(0x5C, dst, src, DWORD); + } + + void BufferWriter::put_cmpps(Registry dst, Location src, SimdCondition cond) { + put_inst_sse(0xC2, dst, src); + put_byte(static_cast(cond)); + } + + void BufferWriter::put_cmpss(Registry dst, Location src, SimdCondition cond) { + put_inst_sse_sized(0xC2, dst, src, DWORD); + put_byte(static_cast(cond)); + } + + void BufferWriter::put_andnps(Registry dst, Location src) { + put_inst_sse(0x55, dst, src); + } + + void BufferWriter::put_andps(Registry dst, Location src) { + put_inst_sse(0x54, dst, src); + } + + void BufferWriter::put_orps(Registry dst, Location src) { + put_inst_sse(0x56, dst, src); + } + + void BufferWriter::put_xorps(Registry dst, Location src) { + put_inst_sse(0x57, dst, src); + } + + void BufferWriter::put_cvtsi2ss(Registry dst, Location src) { + put_byte(0xF3); + put_inst_std(0x2A, src, dst.pack(), src.size, true); + } + +} \ No newline at end of file diff --git a/test/x86.cpp b/test/x86.cpp index 9057fbe..d687861 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -473,6 +473,205 @@ namespace test { } + TEST (check_sse_cvtsi2ss) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.put_cvtsi2ss(XMM0, EAX); + writer.put_cvtsi2ss(XMM1, ref(RAX)); + + buffer.link(0); + std::vector s1 = {0xf3, 0x0f, 0x2a, 0xc0, 0xf3, 0x48, 0x0f, 0x2a, 0x08}; + CHECK(buffer.segments()[1].buffer, s1); // .text + + }; + + TEST (check_sse_movaps) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.put_movaps(XMM1, XMM10); + writer.put_movaps(XMM15, XMM10); + writer.put_movaps(XMM14, XMM1); + writer.put_movaps(XMM1, ref(RAX)); + writer.put_movaps(XMM2, ref(R12)); + writer.put_movaps(XMM13, ref(R12)); + writer.put_movaps(ref(RAX), XMM1); + writer.put_movaps(ref(R12), XMM1); + writer.put_movaps(ref(R12), XMM14); + + EXPECT_THROW(std::runtime_error) { + writer.put_movaps(ref(R12), RAX); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_movaps(XMM1, ref(XMM0)); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_movaps(XMM1, ref(R12)); + }; + + buffer.link(0); + std::vector s1 = {0x41, 0x0f, 0x28, 0xca, 0x45, 0x0f, 0x28, 0xfa, 0x44, 0x0f, 0x28, 0xf1, 0x0f, 0x28, 0x08, 0x41, 0x0f, 0x28, 0x14, 0x24, 0x45, 0x0f, 0x28, 0x2c, 0x24, 0x0f, 0x29, 0x08, 0x41, 0x0f, 0x29, 0x0c, 0x24, 0x45, 0x0f, 0x29, 0x34, 0x24}; + CHECK(buffer.segments()[1].buffer, s1); // .text + + }; + + TEST (check_sse_movhlps_movlhps_movhps_movlps) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_movhlps(XMM14, XMM1); + writer.put_movlhps(XMM14, XMM13); + writer.put_movhps(XMM0, ref(RSP)); + writer.put_movhps(ref(RBP), XMM15); + writer.put_movlps(ref(RCX * 2 + 0x100), XMM0); + + EXPECT_THROW(std::runtime_error) { + writer.put_movhlps(XMM14, RAX); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_movhps(XMM0, ref(R11)); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_movlps(ref(RCX), XMM0); + }; + + buffer.link(0); + std::vector s0 = {0x44, 0x0f, 0x12, 0xf1, 0x45, 0x0f, 0x16, 0xf5, 0x0f, 0x16, 0x04, 0x24, 0x44, 0x0f, 0x17, 0x7d, 0x00, 0x0f, 0x13, 0x04, 0x4d, 0x00, 0x01, 0x00, 0x00}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + }; + + TEST (check_sse_movmskps) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_movmskps(RAX, XMM0); + writer.put_movmskps(RAX, XMM15); + writer.put_movmskps(ECX, XMM11); + + EXPECT_THROW(std::runtime_error) { + writer.put_movmskps(BX, XMM12); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_movmskps(DL, XMM14); + }; + + buffer.link(0); + std::vector s0 = {0x48, 0x0f, 0x50, 0xc0, 0x49, 0x0f, 0x50, 0xc7, 0x41, 0x0f, 0x50, 0xcb}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + }; + + TEST (check_sse_op_packed) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_addps(XMM1, XMM1); + writer.put_divps(XMM11, XMM11); + writer.put_maxps(XMM1, XMM11); + writer.put_minps(XMM11, ref(RAX)); + writer.put_mulps(XMM1, ref(RCX)); + writer.put_rcpps(XMM11, ref(RBP)); + writer.put_rsqrtps(XMM11, XMM1); + writer.put_sqrtps(XMM1, ref(RCX * 8)); + writer.put_subps(XMM11, ref(RBP + 0x100)); + + EXPECT_THROW(std::runtime_error) { + writer.put_addps(XMM11, ref(RAX)); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_addps(RAX, ref(RAX)); + }; + + buffer.link(0); + std::vector s0 = {0x0f, 0x58, 0xc9, 0x45, 0x0f, 0x5e, 0xdb, 0x41, 0x0f, 0x5f, 0xcb, 0x44, 0x0f, 0x5d, 0x18, 0x0f, 0x59, 0x09, 0x44, 0x0f, 0x53, 0x5d, 0x00, 0x44, 0x0f, 0x52, 0xd9, 0x0f, 0x51, 0x0c, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x44, 0x0f, 0x5c, 0x9d, 0x00, 0x01, 0x00, 0x00}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + } + + TEST (check_sse_op_bitwise) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_andnps(XMM1, XMM1); + writer.put_andps(XMM11, XMM11); + writer.put_orps(XMM1, XMM11); + writer.put_xorps(XMM11, ref(RAX)); + + buffer.link(0); + std::vector s0 = {0x0f, 0x55, 0xc9, 0x45, 0x0f, 0x54, 0xdb, 0x41, 0x0f, 0x56, 0xcb, 0x44, 0x0f, 0x57, 0x18}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + } + + TEST (check_sse_op_scalar) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_addss(XMM1, XMM1); + writer.put_divss(XMM11, XMM11); + writer.put_maxss(XMM1, XMM11); + writer.put_minss(XMM11, ref(RAX)); + writer.put_mulss(XMM1, ref(RCX)); + writer.put_rcpss(XMM11, ref(RBP)); + writer.put_rsqrtss(XMM11, XMM1); + writer.put_sqrtss(XMM1, ref(RCX * 8)); + writer.put_subss(XMM11, ref(RBP + 0x100)); + + EXPECT_THROW(std::runtime_error) { + writer.put_addss(XMM11, ref(RAX)); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_addss(RAX, ref(RAX)); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_addss(XMM11, EAX); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_addss(XMM11, RAX); + }; + + buffer.link(0); + std::vector s0 = {0xf3, 0x0f, 0x58, 0xc9, 0xf3, 0x45, 0x0f, 0x5e, 0xdb, 0xf3, 0x41, 0x0f, 0x5f, 0xcb, 0xf3, 0x44, 0x0f, 0x5d, 0x18, 0xf3, 0x0f, 0x59, 0x09, 0xf3, 0x44, 0x0f, 0x53, 0x5d, 0x00, 0xf3, 0x44, 0x0f, 0x52, 0xd9, 0xf3, 0x0f, 0x51, 0x0c, 0xcd, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x44, 0x0f, 0x5c, 0x9d, 0x00, 0x01, 0x00, 0x00}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + } + + TEST (check_sse_cmpps_cmpss) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_cmpps(XMM1, XMM1, SimdCondition::EQ); + writer.put_cmpps(XMM2, XMM3, SimdCondition::LT); + writer.put_cmpss(XMM5, XMM8, SimdCondition::RD); + writer.put_cmpss(XMM11, XMM1, SimdCondition::NLT); + + buffer.link(0); + std::vector s0 = {0x0f, 0xc2, 0xc9, 0x00, 0x0f, 0xc2, 0xd3, 0x01, 0xf3, 0x41, 0x0f, 0xc2, 0xe8, 0x07, 0xf3, 0x44, 0x0f, 0xc2, 0xd9, 0x05}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for x86 @@ -3792,10 +3991,25 @@ namespace test { exe.scall("cpuid", uint64_t(1), &result); // check for support of some expected features - ASSERT_MSG(result.edx & (1 << 23), "MMX not supprted or CPUID failed!"); - ASSERT_MSG(result.edx & (1 << 0), "FPU not supprted or CPUID failed!"); - ASSERT_MSG(result.edx & (1 << 15), "CMOV not supprted or CPUID failed!"); - ASSERT_MSG(result.ecx & (1 << 23), "POPCNT not supprted or CPUID failed!"); + ASSERT_MSG(result.edx & (1 << 23), "MMX not supported or CPUID failed!"); + ASSERT_MSG(result.edx & (1 << 0), "FPU not supported or CPUID failed!"); + ASSERT_MSG(result.edx & (1 << 15), "CMOV not supported or CPUID failed!"); + ASSERT_MSG(result.edx & (1 << 25), "SSE not supported or CPUID failed!"); + ASSERT_MSG(result.edx & (1 << 26), "SSE2 not supported or CPUID failed!"); + ASSERT_MSG(result.ecx & (1 << 23), "POPCNT not supported or CPUID failed!"); + + }; + + TEST (exec_sse) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.put_cvtsi2ss(XMM0, EAX); + writer.put_cvtsi2ss(XMM1, ref(RAX)); + + }; From 7f516c997b3a99d576a288e26ae3428c7384987d Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 9 Apr 2026 19:14:31 +0200 Subject: [PATCH 05/44] Add COMISS, UCOMISS, SHUFPS, UNPCKHPS, UNPCKLPS, CVTSS2SI, LDMXCSR, STMXCSR x86 SSE instructions --- src/asmio/x86/module.cpp | 4 ++ src/asmio/x86/writer.hpp | 23 +++++----- src/asmio/x86/writer_sse.cpp | 69 ++++++++++++++++++++++++++++- test/x86.cpp | 84 +++++++++++++++++++++++++++++++++++- 4 files changed, 166 insertions(+), 14 deletions(-) diff --git a/src/asmio/x86/module.cpp b/src/asmio/x86/module.cpp index cde8fec..634064f 100644 --- a/src/asmio/x86/module.cpp +++ b/src/asmio/x86/module.cpp @@ -277,6 +277,10 @@ namespace asmio::x86 { if constexpr (std::is_same_v) { return SimdCondition::NLT; + } else + + if constexpr (std::is_same_v) { + return 0; } else { diff --git a/src/asmio/x86/writer.hpp b/src/asmio/x86/writer.hpp index 0622b86..baff89d 100644 --- a/src/asmio/x86/writer.hpp +++ b/src/asmio/x86/writer.hpp @@ -69,7 +69,7 @@ namespace asmio::x86 { /// Put SSE double XMM register operand instruction void put_inst_sse_2xmm(uint8_t opcode, Registry dst, Registry src); void put_inst_sse(uint8_t opcode, Registry reg, const Location& loc); - void put_inst_sse_sized(uint8_t opcode, Registry reg, const Location& loc, uint8_t size); + void put_inst_sse_sized(uint8_t opcode, Registry reg, const Location& loc, uint8_t size, bool prefix = true); /// Add the REX.W prefix void put_rex_w(); @@ -389,20 +389,23 @@ namespace asmio::x86 { INST put_subss(Registry dst, Location src); ///< INST put_cmpps(Registry dst, Location src, SimdCondition cond); ///< Compare packed 32 bit floating point values INST put_cmpss(Registry dst, Location src, SimdCondition cond); ///< - // TODO: comiss - // TODO: ucomiss + INST put_comiss(Registry dst, Location src); ///< + INST put_ucomiss(Registry dst, Location src); ///< INST put_andnps(Registry dst, Location src); ///< Bitwise logical AND NOT of packed 32 bit values INST put_andps(Registry dst, Location src); ///< Bitwise logical AND of packed 32 bit values INST put_orps(Registry dst, Location src); ///< Bitwise logical OR of packed 32 bit values INST put_xorps(Registry dst, Location src); ///< Bitwise logical XOR of packed 32 bit values - // TODO: shufps - // TODO: unpckhps - // TODO: unpcklps - // TODO: cvtpi2ps - // TODO: cvtps2pi + INST put_shufps(Registry dst, Location src, uint8_t selector); ///< + INST put_unpckhps(Registry dst, Location src); ///< + INST put_unpcklps(Registry dst, Location src); ///< + // Skipped cvtpi2ps - This uses MMX registers + // Skipped cvtps2pi - This uses MMX registers INST put_cvtsi2ss(Registry dst, Location src); ///< Convert Doubleword Integer to Scalar Single Precision Floating-Point Value - // TODO: cvtss2si - + INST put_cvtss2si(Registry dst, Location src); ///< + // Skipped cvttps2pi - This uses MMX registers + // TODO: cvttss2si (same as cvtss2si) + INST put_ldmxcsr(Location src); ///< + INST put_stmxcsr(Location dst); ///< }; diff --git a/src/asmio/x86/writer_sse.cpp b/src/asmio/x86/writer_sse.cpp index 880159c..4e8906d 100644 --- a/src/asmio/x86/writer_sse.cpp +++ b/src/asmio/x86/writer_sse.cpp @@ -24,14 +24,17 @@ namespace asmio::x86 { put_inst_std(opcode, loc, reg.pack(), XMMWORD, true); } - void BufferWriter::put_inst_sse_sized(uint8_t opcode, Registry reg, const Location& loc, uint8_t size) { + void BufferWriter::put_inst_sse_sized(uint8_t opcode, Registry reg, const Location& loc, uint8_t size, bool prefix) { if (!reg.is(Registry::XMM)) { throw std::runtime_error {"Invalid operand, expected XMM register"}; } if ((loc.is_simple() && loc.base.is(Registry::XMM)) || (loc.is_memory() && loc.size == size)) { - put_byte(0xF3); + if (prefix) { + put_byte(0xF3); + } + put_inst_std(opcode, loc, reg.pack(), DWORD, true); return; } @@ -207,6 +210,14 @@ namespace asmio::x86 { put_byte(static_cast(cond)); } + void BufferWriter::put_comiss(Registry dst, Location src) { + put_inst_sse_sized(0x2F, dst, src, DWORD, false); + } + + void BufferWriter::put_ucomiss(Registry dst, Location src) { + put_inst_sse_sized(0x2E, dst, src, DWORD, false); + } + void BufferWriter::put_andnps(Registry dst, Location src) { put_inst_sse(0x55, dst, src); } @@ -223,9 +234,63 @@ namespace asmio::x86 { put_inst_sse(0x57, dst, src); } + void BufferWriter::put_shufps(Registry dst, Location src, uint8_t selector) { + put_inst_sse(0xC6, dst, src); + put_byte(selector); + } + + void BufferWriter::put_unpckhps(Registry dst, Location src) { + put_inst_sse(0x15, dst, src); + } + + void BufferWriter::put_unpcklps(Registry dst, Location src) { + put_inst_sse(0x14, dst, src); + } + void BufferWriter::put_cvtsi2ss(Registry dst, Location src) { put_byte(0xF3); put_inst_std(0x2A, src, dst.pack(), src.size, true); } + void BufferWriter::put_cvtss2si(Registry dst, Location src) { + + if (!dst.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid destination operand, expected general purpose register"}; + } + + if (dst.size != DWORD && dst.size != QWORD) { + throw std::runtime_error {"Invalid destination operand, expected dword or qword register"}; + } + + if (src.is_simple() && !src.base.is(Registry::XMM)) { + throw std::runtime_error {"Invalid source operand, expected XMM register"}; + } + + if (src.is_memory() && (src.size != VOID && src.size != DWORD)) { + throw std::runtime_error {"Invalid source operand, dword memory reference"}; + } + + put_byte(0xF3); + put_inst_std(0x2D, src, dst.pack(), dst.size, true); + } + + + void BufferWriter::put_ldmxcsr(Location src) { + if (src.is_memory() && src.size == DWORD) { + put_inst_std(0xAE, src, RegInfo::raw(2), DWORD, true); + return; + } + + throw std::runtime_error {"Invalid operand, expected memory"}; + } + + void BufferWriter::put_stmxcsr(Location dst) { + if (dst.is_memory() && dst.size == DWORD) { + put_inst_std(0xAE, dst, RegInfo::raw(3), DWORD, true); + return; + } + + throw std::runtime_error {"Invalid operand, expected memory"}; + } + } \ No newline at end of file diff --git a/test/x86.cpp b/test/x86.cpp index d687861..14adb63 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -656,7 +656,7 @@ namespace test { } - TEST (check_sse_cmpps_cmpss) { + TEST (check_sse_cmpps_cmpss_comiss_ucomiss) { SegmentedBuffer buffer; BufferWriter writer {buffer}; @@ -665,13 +665,93 @@ namespace test { writer.put_cmpps(XMM2, XMM3, SimdCondition::LT); writer.put_cmpss(XMM5, XMM8, SimdCondition::RD); writer.put_cmpss(XMM11, XMM1, SimdCondition::NLT); + writer.put_comiss(XMM11, ref(RBP)); + writer.put_comiss(XMM11, XMM1); + writer.put_ucomiss(XMM11, XMM1); + writer.put_ucomiss(XMM11, ref(RAX)); buffer.link(0); - std::vector s0 = {0x0f, 0xc2, 0xc9, 0x00, 0x0f, 0xc2, 0xd3, 0x01, 0xf3, 0x41, 0x0f, 0xc2, 0xe8, 0x07, 0xf3, 0x44, 0x0f, 0xc2, 0xd9, 0x05}; + std::vector s0 = {0x0f, 0xc2, 0xc9, 0x00, 0x0f, 0xc2, 0xd3, 0x01, 0xf3, 0x41, 0x0f, 0xc2, 0xe8, 0x07, 0xf3, 0x44, 0x0f, 0xc2, 0xd9, 0x05, 0x44, 0x0f, 0x2f, 0x5d, 0x00, 0x44, 0x0f, 0x2f, 0xd9, 0x44, 0x0f, 0x2e, 0xd9, 0x44, 0x0f, 0x2e, 0x18}; CHECK(buffer.segments()[0].buffer, s0); // .rwx }; + TEST (check_sse_shufps_unpckhps_unpcklps) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_shufps(XMM11, ref(RAX + RCX), 0b1011); + writer.put_unpckhps(XMM11, ref(RAX)); + writer.put_unpcklps(XMM11, XMM1); + + buffer.link(0); + std::vector s0 = {0x44, 0x0f, 0xc6, 0x1c, 0x08, 0x0b, 0x44, 0x0f, 0x15, 0x18, 0x44, 0x0f, 0x14, 0xd9}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + }; + + TEST (check_sse_cvtss2si) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_cvtss2si(EAX, XMM1); + writer.put_cvtss2si(EAX, ref(RAX)); + writer.put_cvtss2si(EAX, ref(RAX)); + writer.put_cvtss2si(RAX, XMM1); + writer.put_cvtss2si(RAX, ref(RAX)); + writer.put_cvtss2si(RAX, ref(RAX)); + writer.put_cvtss2si(R11, XMM11); + writer.put_cvtss2si(R11, ref(R11)); + writer.put_cvtss2si(R11, ref(R11)); + + EXPECT_THROW(std::runtime_error) { + writer.put_cvtss2si(XMM11, XMM11); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_cvtss2si(R11, R11); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_cvtss2si(R11, ref(R11)); + }; + + buffer.link(0); + std::vector s0 = {0xf3, 0x0f, 0x2d, 0xc1, 0xf3, 0x0f, 0x2d, 0x00, 0xf3, 0x0f, 0x2d, 0x00, 0xf3, 0x48, 0x0f, 0x2d, 0xc1, 0xf3, 0x48, 0x0f, 0x2d, 0x00, 0xf3, 0x48, 0x0f, 0x2d, 0x00, 0xf3, 0x4d, 0x0f, 0x2d, 0xdb, 0xf3, 0x4d, 0x0f, 0x2d, 0x1b, 0xf3, 0x4d, 0x0f, 0x2d, 0x1b}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + }; + + TEST (check_sse_ldmxcsr_stmxcsr) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_ldmxcsr(ref(EAX)); + writer.put_ldmxcsr(ref(ECX)); + writer.put_stmxcsr(ref(ESP)); + writer.put_stmxcsr(ref(ESP)); + + EXPECT_THROW(std::runtime_error) { + writer.put_ldmxcsr(RAX); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_stmxcsr(EAX); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_stmxcsr(XMM1); + }; + + buffer.link(0); + std::vector s0 = {0x67, 0x0f, 0xae, 0x10, 0x67, 0x0f, 0xae, 0x11, 0x67, 0x0f, 0xae, 0x1c, 0x24, 0x67, 0x0f, 0xae, 0x1c, 0x24}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + } + /* * region Executable * Begin architecture depended tests for x86 From 85908323726f4080093309c7d9f9c34c77665c07 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Thu, 9 Apr 2026 22:05:34 +0200 Subject: [PATCH 06/44] Add MOVSS, MOVUPS, CVTTSS2CSI, MOVNTPS, SFENCE --- src/asmio/x86/writer.hpp | 13 ++-- src/asmio/x86/writer_sse.cpp | 136 ++++++++++++++++++++++++----------- test/x86.cpp | 55 +++++++++++++- 3 files changed, 152 insertions(+), 52 deletions(-) diff --git a/src/asmio/x86/writer.hpp b/src/asmio/x86/writer.hpp index baff89d..03cc643 100644 --- a/src/asmio/x86/writer.hpp +++ b/src/asmio/x86/writer.hpp @@ -70,6 +70,8 @@ namespace asmio::x86 { void put_inst_sse_2xmm(uint8_t opcode, Registry dst, Registry src); void put_inst_sse(uint8_t opcode, Registry reg, const Location& loc); void put_inst_sse_sized(uint8_t opcode, Registry reg, const Location& loc, uint8_t size, bool prefix = true); + void put_inst_mxcsr(const Location& loc, uint8_t opcode); + void put_inst_movxps(Location dst, Location src, uint8_t opcode); /// Add the REX.W prefix void put_rex_w(); @@ -367,8 +369,8 @@ namespace asmio::x86 { INST put_movhps(Location dst, Location src); ///< Move two packed single precision floating-point values from m64 to high quadword of dst INST put_movlps(Location dst, Location src); ///< Move two packed single precision floating-point values from m64 to low quadword of dst INST put_movmskps(Registry dst, Registry src); ///< Extract Packed Single Precision Floating-Point Sign Mask - // TODO: movss - // TODO: movups + INST put_movss(Location dst, Location src); ///< + INST put_movups(Location dst, Location src); ///< INST put_addps(Registry dst, Location src); ///< Add packed single precision floating-point values INST put_addss(Registry dst, Location src); ///< INST put_divps(Registry dst, Location src); ///< Divide packed single precision floating-point values @@ -398,14 +400,13 @@ namespace asmio::x86 { INST put_shufps(Registry dst, Location src, uint8_t selector); ///< INST put_unpckhps(Registry dst, Location src); ///< INST put_unpcklps(Registry dst, Location src); ///< - // Skipped cvtpi2ps - This uses MMX registers - // Skipped cvtps2pi - This uses MMX registers INST put_cvtsi2ss(Registry dst, Location src); ///< Convert Doubleword Integer to Scalar Single Precision Floating-Point Value INST put_cvtss2si(Registry dst, Location src); ///< - // Skipped cvttps2pi - This uses MMX registers - // TODO: cvttss2si (same as cvtss2si) + INST put_cvttss2si(Registry dst, Location src); ///< INST put_ldmxcsr(Location src); ///< INST put_stmxcsr(Location dst); ///< + INST put_movntps(Location dst, Registry src); ///< + INST put_sfence(); ///< }; diff --git a/src/asmio/x86/writer_sse.cpp b/src/asmio/x86/writer_sse.cpp index 4e8906d..102204b 100644 --- a/src/asmio/x86/writer_sse.cpp +++ b/src/asmio/x86/writer_sse.cpp @@ -42,37 +42,22 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operands"}; } - void BufferWriter::put_movaps(Location dst, Location src) { - - if (dst.is_simple()) { - put_inst_sse(0x28, dst.base, src); + void BufferWriter::put_inst_mxcsr(const Location& loc, uint8_t opcode) { + if (loc.is_memory() && loc.size == DWORD) { + put_inst_std(0xAE, loc, RegInfo::raw(opcode), DWORD, true); return; } - if (src.is_simple()) { - put_inst_sse(0x29, src.base, dst); - return; - } - - throw std::runtime_error {"Invalid operands"}; + throw std::runtime_error {"Invalid operand, expected memory reference"}; } - void BufferWriter::put_movhlps(Registry dst, Registry src) { - put_inst_sse_2xmm(0x12, dst, src); - } - - void BufferWriter::put_movlhps(Registry dst, Registry src) { - put_inst_sse_2xmm(0x16, dst, src); - } - - void BufferWriter::put_movhps(Location dst, Location src) { - + void BufferWriter::put_inst_movxps(Location dst, Location src, uint8_t opcode) { if (dst.is_simple() && src.is_memory()) { if (src.size != QWORD) { throw std::runtime_error {"Invalid operand, expected QWORD memory reference"}; } - put_inst_std(0x16, src, dst.base.pack(), DWORD, true); + put_inst_std(opcode, src, dst.base.pack(), DWORD, true); return; } @@ -81,36 +66,45 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operand, expected QWORD memory reference"}; } - put_inst_std(0x17, dst, src.base.pack(), DWORD, true); + // set the direction flag in opcode + put_inst_std(opcode | 1, dst, src.base.pack(), DWORD, true); return; } throw std::runtime_error {"Invalid operands"}; } - void BufferWriter::put_movlps(Location dst, Location src) { - - if (dst.is_simple() && src.is_memory()) { - if (src.size != QWORD) { - throw std::runtime_error {"Invalid operand, expected QWORD memory reference"}; - } + void BufferWriter::put_movaps(Location dst, Location src) { - put_inst_std(0x12, src, dst.base.pack(), DWORD, true); + if (dst.is_simple()) { + put_inst_sse(0x28, dst.base, src); return; } - if (src.is_simple() && dst.is_memory()) { - if (dst.size != QWORD) { - throw std::runtime_error {"Invalid operand, expected QWORD memory reference"}; - } - - put_inst_std(0x13, dst, src.base.pack(), DWORD, true); + if (src.is_simple()) { + put_inst_sse(0x29, src.base, dst); return; } throw std::runtime_error {"Invalid operands"}; } + void BufferWriter::put_movhlps(Registry dst, Registry src) { + put_inst_sse_2xmm(0x12, dst, src); + } + + void BufferWriter::put_movlhps(Registry dst, Registry src) { + put_inst_sse_2xmm(0x16, dst, src); + } + + void BufferWriter::put_movhps(Location dst, Location src) { + put_inst_movxps(dst, src, 0x16); + } + + void BufferWriter::put_movlps(Location dst, Location src) { + put_inst_movxps(dst, src, 0x12); + } + void BufferWriter::put_movmskps(Registry dst, Registry src) { if (!dst.is(Registry::GENERAL)) { @@ -128,6 +122,36 @@ namespace asmio::x86 { put_inst_std(0x50, src, dst.pack(), dst.size, true); } + void BufferWriter::put_movss(Location dst, Location src) { + + if (dst.is_simple()) { + put_inst_sse_sized(0x10, dst.base, src, DWORD); + return; + } + + if (src.is_simple()) { + put_inst_sse_sized(0x11, src.base, dst, DWORD); + return; + } + + throw std::runtime_error {"Invalid operands"}; + } + + void BufferWriter::put_movups(Location dst, Location src) { + + if (dst.is_simple()) { + put_inst_sse(0x10, dst.base, src); + return; + } + + if (src.is_simple()) { + put_inst_sse(0x11, src.base, dst); + return; + } + + throw std::runtime_error {"Invalid operands"}; + } + void BufferWriter::put_addps(Registry dst, Location src) { put_inst_sse(0x58, dst, src); } @@ -274,23 +298,49 @@ namespace asmio::x86 { put_inst_std(0x2D, src, dst.pack(), dst.size, true); } + void BufferWriter::put_cvttss2si(Registry dst, Location src) { - void BufferWriter::put_ldmxcsr(Location src) { - if (src.is_memory() && src.size == DWORD) { - put_inst_std(0xAE, src, RegInfo::raw(2), DWORD, true); - return; + if (!dst.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid destination operand, expected general purpose register"}; + } + + if (dst.size != DWORD && dst.size != QWORD) { + throw std::runtime_error {"Invalid destination operand, expected dword or qword register"}; + } + + if (src.is_simple() && !src.base.is(Registry::XMM)) { + throw std::runtime_error {"Invalid source operand, expected XMM register"}; } - throw std::runtime_error {"Invalid operand, expected memory"}; + if (src.is_memory() && (src.size != VOID && src.size != DWORD)) { + throw std::runtime_error {"Invalid source operand, dword memory reference"}; + } + + put_byte(0xF3); + put_inst_std(0x2C, src, dst.pack(), dst.size, true); + } + + void BufferWriter::put_ldmxcsr(Location src) { + put_inst_mxcsr(src, 2); } void BufferWriter::put_stmxcsr(Location dst) { - if (dst.is_memory() && dst.size == DWORD) { - put_inst_std(0xAE, dst, RegInfo::raw(3), DWORD, true); + put_inst_mxcsr(dst, 3); + } + + void BufferWriter::put_movntps(Location dst, Registry src) { + if (dst.is_memory()) { + put_inst_sse(0x2B, src, dst); return; } - throw std::runtime_error {"Invalid operand, expected memory"}; + throw std::runtime_error {"Invalid destination operand, expected memory reference"}; + } + + void BufferWriter::put_sfence() { + put_byte(0x0F); + put_byte(0xAE); + put_byte(0xF8); } } \ No newline at end of file diff --git a/test/x86.cpp b/test/x86.cpp index 14adb63..5a92d68 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -691,7 +691,7 @@ namespace test { }; - TEST (check_sse_cvtss2si) { + TEST (check_sse_cvtss2si_cvttss2si) { SegmentedBuffer buffer; BufferWriter writer {buffer}; @@ -705,6 +705,9 @@ namespace test { writer.put_cvtss2si(R11, XMM11); writer.put_cvtss2si(R11, ref(R11)); writer.put_cvtss2si(R11, ref(R11)); + writer.put_cvttss2si(R11, XMM11); + writer.put_cvttss2si(EAX, ref(R11)); + writer.put_cvttss2si(R11, ref(R11)); EXPECT_THROW(std::runtime_error) { writer.put_cvtss2si(XMM11, XMM11); @@ -719,7 +722,7 @@ namespace test { }; buffer.link(0); - std::vector s0 = {0xf3, 0x0f, 0x2d, 0xc1, 0xf3, 0x0f, 0x2d, 0x00, 0xf3, 0x0f, 0x2d, 0x00, 0xf3, 0x48, 0x0f, 0x2d, 0xc1, 0xf3, 0x48, 0x0f, 0x2d, 0x00, 0xf3, 0x48, 0x0f, 0x2d, 0x00, 0xf3, 0x4d, 0x0f, 0x2d, 0xdb, 0xf3, 0x4d, 0x0f, 0x2d, 0x1b, 0xf3, 0x4d, 0x0f, 0x2d, 0x1b}; + std::vector s0 = {0xf3, 0x0f, 0x2d, 0xc1, 0xf3, 0x0f, 0x2d, 0x00, 0xf3, 0x0f, 0x2d, 0x00, 0xf3, 0x48, 0x0f, 0x2d, 0xc1, 0xf3, 0x48, 0x0f, 0x2d, 0x00, 0xf3, 0x48, 0x0f, 0x2d, 0x00, 0xf3, 0x4d, 0x0f, 0x2d, 0xdb, 0xf3, 0x4d, 0x0f, 0x2d, 0x1b, 0xf3, 0x4d, 0x0f, 0x2d, 0x1b, 0xf3, 0x4d, 0x0f, 0x2c, 0xdb, 0xf3, 0x41, 0x0f, 0x2c, 0x03, 0xf3, 0x4d, 0x0f, 0x2c, 0x1b}; CHECK(buffer.segments()[0].buffer, s0); // .rwx }; @@ -750,7 +753,53 @@ namespace test { std::vector s0 = {0x67, 0x0f, 0xae, 0x10, 0x67, 0x0f, 0xae, 0x11, 0x67, 0x0f, 0xae, 0x1c, 0x24, 0x67, 0x0f, 0xae, 0x1c, 0x24}; CHECK(buffer.segments()[0].buffer, s0); // .rwx - } + }; + + TEST (check_sse_sfence_movntps) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_sfence(); + writer.put_movntps(ref(RAX), XMM1); + writer.put_movntps(ref(RAX), XMM11); + + EXPECT_THROW(std::runtime_error) { + writer.put_movntps(XMM1, XMM11); + }; + + buffer.link(0); + std::vector s0 = {0x0f, 0xae, 0xf8, 0x0f, 0x2b, 0x08, 0x44, 0x0f, 0x2b, 0x18}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + }; + + TEST (check_sse_movss_movups) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + writer.put_movups(XMM1, XMM2); + writer.put_movups(XMM1, ref(RAX)); + writer.put_movups(ref(RAX), XMM1); + + writer.put_movss(XMM1, XMM2); + writer.put_movss(XMM1, ref(RAX)); + writer.put_movss(ref(RAX), XMM1); + + EXPECT_THROW(std::runtime_error) { + writer.put_movups(XMM1, ref(RAX)); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_movss(XMM1, ref(RAX)); + }; + + buffer.link(0); + std::vector s0 = {0x0f, 0x10, 0xca, 0x0f, 0x10, 0x08, 0x0f, 0x11, 0x08, 0xf3, 0x0f, 0x10, 0xca, 0xf3, 0x0f, 0x10, 0x08, 0xf3, 0x0f, 0x11, 0x08}; + CHECK(buffer.segments()[0].buffer, s0); // .rwx + + }; /* * region Executable From ab33c383584b194d0434cdd03f1593e751316c7f Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:32:07 +0200 Subject: [PATCH 07/44] Runtime SSE test --- src/asmio/program/executable.hpp | 18 ++++++--- test/x86.cpp | 67 +++++++++++++++++++++----------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/src/asmio/program/executable.hpp b/src/asmio/program/executable.hpp index b64d99b..4a1ee01 100644 --- a/src/asmio/program/executable.hpp +++ b/src/asmio/program/executable.hpp @@ -19,7 +19,7 @@ namespace asmio { concept trivially_copyable = std::is_trivially_copyable_v; template - concept integral_or_void = std::is_integral_v || std::is_void_v; + concept trivially_returnable = std::is_integral_v || std::is_void_v || std::is_floating_point_v; class ExecutableBuffer { @@ -54,7 +54,7 @@ namespace asmio { public: - template + template R scall(size_t offset, Args... args) { // unpack parameter pack @@ -76,7 +76,7 @@ namespace asmio { return CALL_POINTER(offset, R, params); } - template + template R scall(const Label& label, Args... args) { return scall(labels.at(label), args...); } @@ -105,7 +105,11 @@ namespace asmio { return CALL_POINTER(offset, int32_t, nullptr); } - float call_f32(uint64_t offset = 0) { + float call_f32(uint32_t offset = 0) { + return CALL_POINTER(offset, float, nullptr); + } + + float call_f80(uint64_t offset = 0) { #if ARCH_X86 auto function = buffer + offset; volatile float tmp; @@ -113,7 +117,7 @@ namespace asmio { return tmp; #endif - throw std::runtime_error {"Float calls are unimplemented!"}; + throw std::runtime_error {"80 bit float calls are not supported!"}; } /* @@ -140,6 +144,10 @@ namespace asmio { return call_i32(labels.at(label)); } + float call_f80(const Label& label) { + return call_f80(labels.at(label)); + } + float call_f32(const Label& label) { return call_f32(labels.at(label)); } diff --git a/test/x86.cpp b/test/x86.cpp index 5a92d68..d8ab5aa 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -2043,7 +2043,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32(), 1.0f); + CHECK(buffer.call_f80(), 1.0f); } @@ -2075,7 +2075,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 3.0f); + CHECK(buffer.call_f80("main"), 3.0f); } @@ -2100,7 +2100,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 2.5f); + CHECK(buffer.call_f80("main"), 2.5f); } @@ -2132,7 +2132,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 10.75f); + CHECK(buffer.call_f80("main"), 10.75f); } @@ -2169,7 +2169,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 12.5f); + CHECK(buffer.call_f80("main"), 12.5f); } @@ -2204,7 +2204,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 6.0f); + CHECK(buffer.call_f80("main"), 6.0f); } @@ -2249,7 +2249,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 4.5f); + CHECK(buffer.call_f80("main"), 4.5f); } @@ -2265,7 +2265,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), -1); + CHECK(buffer.call_f80("main"), -1); } @@ -2294,7 +2294,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 20); + CHECK(buffer.call_f80("main"), 20); } @@ -2320,7 +2320,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 7); + CHECK(buffer.call_f80("main"), 7); } @@ -2365,7 +2365,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 1); + CHECK(buffer.call_f80("main"), 1); } @@ -2381,7 +2381,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32(), 1); + CHECK(buffer.call_f80(), 1); } @@ -2409,7 +2409,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 6); + CHECK(buffer.call_f80("main"), 6); } @@ -2444,7 +2444,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("init"), 1.0f); + CHECK(buffer.call_f80("init"), 1.0f); CHECK(buffer.call_i32("main"), 6); } @@ -2472,7 +2472,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("init"), 1.0f); + CHECK(buffer.call_f80("init"), 1.0f); CHECK(buffer.call_i32("main"), 3); } @@ -2489,7 +2489,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 3.0f); + CHECK(buffer.call_f80("main"), 3.0f); } @@ -2508,7 +2508,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("main"), 4.0f); + CHECK(buffer.call_f80("main"), 4.0f); } @@ -2563,10 +2563,10 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.call_f32("set_a"), 2.0f); - CHECK(buffer.call_f32("set_b"), 1.0f); - CHECK(buffer.call_f32("set_c"), 0.0f); - CHECK(buffer.call_f32("ctrl_sum"), 11.0f); + CHECK(buffer.call_f80("set_a"), 2.0f); + CHECK(buffer.call_f80("set_b"), 1.0f); + CHECK(buffer.call_f80("set_c"), 0.0f); + CHECK(buffer.call_f80("ctrl_sum"), 11.0f); } @@ -4129,16 +4129,37 @@ namespace test { }; - TEST (exec_sse) { + TEST (exec_sse_generic_floats) { SegmentedBuffer buffer; BufferWriter writer {buffer}; + writer.section(MemoryFlag::R | MemoryFlag::W); + writer.label("three"); + writer.put_dword(3); + + writer.label("fraction"); + writer.put_dword_f(3.1415); + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.label("mult"); + writer.put_mov(EAX, 13); writer.put_cvtsi2ss(XMM0, EAX); - writer.put_cvtsi2ss(XMM1, ref(RAX)); + writer.put_cvtsi2ss(XMM1, ref("three")); + writer.put_mulss(XMM0, XMM1); + writer.put_ret(); + + writer.label("pi"); + writer.put_movss(XMM0, ref("fraction")); + writer.put_ret(); + + ExecutableBuffer exe = to_executable(buffer); + int res = exe.call_f32("mult"); + CHECK(res, 39); + float pi = exe.call_f32("pi"); + CHECK(pi, 3.1415); }; From cdb2486d7a7d07d7929819a0553de87058e9cd12 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Fri, 10 Apr 2026 19:43:43 +0200 Subject: [PATCH 08/44] Add SSE support to TASML --- src/asmio/x86/module.cpp | 75 ++++++++++++++++++++++++------------ src/asmio/x86/writer_sse.cpp | 2 +- test/x86.cpp | 34 +++++++++++++++- 3 files changed, 84 insertions(+), 27 deletions(-) diff --git a/src/asmio/x86/module.cpp b/src/asmio/x86/module.cpp index 634064f..475c4ef 100644 --- a/src/asmio/x86/module.cpp +++ b/src/asmio/x86/module.cpp @@ -17,6 +17,7 @@ namespace asmio::x86 { {"dword", DWORD}, {"qword", QWORD}, {"tword", TWORD}, + {"xmmword", XMMWORD}, {"float", DWORD}, {"double", QWORD}, @@ -106,6 +107,24 @@ namespace asmio::x86 { if (raw == "r15d") return R15D; if (raw == "r15") return R15; + // sse + if (raw == "xmm0") return XMM0; + if (raw == "xmm1") return XMM1; + if (raw == "xmm2") return XMM2; + if (raw == "xmm3") return XMM3; + if (raw == "xmm4") return XMM4; + if (raw == "xmm5") return XMM5; + if (raw == "xmm6") return XMM6; + if (raw == "xmm7") return XMM7; + if (raw == "xmm8") return XMM8; + if (raw == "xmm9") return XMM9; + if (raw == "xmm10") return XMM10; + if (raw == "xmm11") return XMM11; + if (raw == "xmm12") return XMM12; + if (raw == "xmm13") return XMM13; + if (raw == "xmm14") return XMM14; + if (raw == "xmm15") return XMM15; + throw std::runtime_error {"Unknown registry " + token->quoted()}; } @@ -250,7 +269,22 @@ namespace asmio::x86 { return parse_expression(stream); } - static Location parse_location(TokenStream stream) { + template + T parse_argument(TokenStream stream); + + template + T parse_argument(TokenStream stream) { + return stream.expect(Token::INT).as_int(); + } + + template <> + Registry parse_argument(TokenStream stream) { + const Token& name = stream.expect(Token::NAME); + return token_to_register(&name); + } + + template <> + Location parse_argument(TokenStream stream) { const Token* name = &stream.peek(); const int size = token_to_sizing(name); @@ -262,30 +296,21 @@ namespace asmio::x86 { return parse_inner(stream); } - template - T parse_argument(TokenStream stream) { - // TODO FIXME! - - if constexpr (std::is_same_v) { - const Token& token = stream.expect(Token::NAME); - return token_to_register(&token); - } else - - if constexpr (std::is_same_v) { - return parse_location(stream); - } else - - if constexpr (std::is_same_v) { - return SimdCondition::NLT; - } else - - if constexpr (std::is_same_v) { - return 0; - } - - else { - static_assert(false, "x86 can only accept Location classes as arguments"); - } + template <> + SimdCondition parse_argument(TokenStream stream) { + const Token& token = stream.expect(Token::NAME); + std::string raw = util::to_lower(token.raw); + + if (raw == "eq") return SimdCondition::EQ; + if (raw == "lt") return SimdCondition::LT; + if (raw == "le") return SimdCondition::LE; + if (raw == "rd") return SimdCondition::RD; + if (raw == "neq") return SimdCondition::NEQ; + if (raw == "nlt") return SimdCondition::NLT; + if (raw == "nle") return SimdCondition::NLE; + if (raw == "nrd") return SimdCondition::NRD; + + throw std::runtime_error {"Unknown SIMD condition '" + raw + "'"}; } # include "generated/x86.hpp" diff --git a/src/asmio/x86/writer_sse.cpp b/src/asmio/x86/writer_sse.cpp index 102204b..c243f86 100644 --- a/src/asmio/x86/writer_sse.cpp +++ b/src/asmio/x86/writer_sse.cpp @@ -17,7 +17,7 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operand, expected XMM register"}; } - if (loc.size != XMMWORD) { + if (loc.size != XMMWORD && loc.size != VOID) { throw std::runtime_error {"Invalid operand, expected xmmword register or memory"}; } diff --git a/test/x86.cpp b/test/x86.cpp index d8ab5aa..e01731a 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -4155,7 +4155,7 @@ namespace test { ExecutableBuffer exe = to_executable(buffer); - int res = exe.call_f32("mult"); + int res = (int) exe.call_f32("mult"); CHECK(res, 39); float pi = exe.call_f32("pi"); @@ -4163,5 +4163,37 @@ namespace test { }; + TEST (tasml_exec_string_prefix) { + + std::string code = R"( + lang x86 + + section r + value: + dword 3.5 + + section rx + syntax: + cmpps xmm11, xmmword [rax], nrd + cmpps xmm11, [rax], nrd + shufps xmm1, xmm15, 3 + + floats: + movss xmm1, dword [@value] + mulss xmm1, xmm1 + movss xmm0, xmm1 + ret + )"; + + SegmentedBuffer segmented = tasml::assemble(vstl_self.name(), code); + ExecutableBuffer buffer = to_executable(segmented); + + float f = buffer.call_f32("floats"); + + ASSERT(f > 12); + ASSERT(f < 13); + + }; + #endif ;} From 834cf28af32a06b5c9d89dbf7c7fc1f2bc53ffc3 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Fri, 10 Apr 2026 22:22:37 +0200 Subject: [PATCH 09/44] Cleanup --- src/asmio/program/segmented.cpp | 22 ---- src/asmio/program/segmented.hpp | 3 - src/asmio/x86/argument/condition.hpp | 19 ++++ src/asmio/x86/module.cpp | 39 +++++++ src/asmio/x86/writer.cpp | 9 +- src/asmio/x86/writer.hpp | 139 ++++++++++++----------- src/asmio/x86/writer_cpu.cpp | 163 ++++++++++++--------------- src/asmio/x86/writer_sse.cpp | 44 ++++++++ test/aarch64.cpp | 14 +++ util/felix | 52 +++++++++ 10 files changed, 309 insertions(+), 195 deletions(-) create mode 100755 util/felix diff --git a/src/asmio/program/segmented.cpp b/src/asmio/program/segmented.cpp index 5837178..b77558e 100644 --- a/src/asmio/program/segmented.cpp +++ b/src/asmio/program/segmented.cpp @@ -187,28 +187,6 @@ namespace asmio { return last.start + last.buffer.size() + last.tail; } - void SegmentedBuffer::dump() const { - std::cout << "./unasm.sh " << base_address << " \""; - - for (const BufferSegment& segment : sections) { - std::cout << "SECTION " << (segment.flags.x ? ".text" : ".data") << " \\ndb "; - bool first = true; - - for (uint8_t byte : segment.buffer) { - if (!first) { - std::cout << ", "; - } - - first = false; - std::cout << '0' << std::setfill('0') << std::setw(2) << std::hex << ((int) byte) << "h"; - } - - std::cout << "\\n"; - } - - std::cout << "\\n\"" << std::endl; - } - const std::vector& SegmentedBuffer::segments() const { return sections; } diff --git a/src/asmio/program/segmented.hpp b/src/asmio/program/segmented.hpp index 02357f1..efeab4b 100644 --- a/src/asmio/program/segmented.hpp +++ b/src/asmio/program/segmented.hpp @@ -169,9 +169,6 @@ namespace asmio { /// Get the total size in bytes of the whole segmented buffer, can be used only after linking size_t total() const; - /// Print the contests of this buffer for debugging - void dump() const; - /// Get segment list const std::vector& segments() const; diff --git a/src/asmio/x86/argument/condition.hpp b/src/asmio/x86/argument/condition.hpp index 2e11457..87cc2bc 100644 --- a/src/asmio/x86/argument/condition.hpp +++ b/src/asmio/x86/argument/condition.hpp @@ -15,4 +15,23 @@ namespace asmio::x86 { NRD = 3, ///< Unordered (unordered, non-signaling) }; + enum struct Condition : uint8_t { + O = 0x0, ///< Overflow + NO = 0x1, ///< Not Overflow + B = 0x2, C = 0x2, NAE = 0x2, ///< Below, Carry, Not Above or Equal + NB = 0x3, NC = 0x3, AE = 0x3, ///< Not Below, Not Carry, Above or Equal + E = 0x4, Z = 0x4, ///< Equal, Zero + NE = 0x5, NZ = 0x5, ///< Not Equal, Not Zero + BE = 0x6, NA = 0x6, ///< Below or Equal, Not Above + NBE = 0x7, A = 0x7, ///< Not Below or Equal, Above + S = 0x8, ///< Sign + NS = 0x9, ///< Not Sign + P = 0xA, PE = 0xA, ///< Parity, Parity Even + NP = 0xB, PO = 0xB, ///< Not Parity, Parity Odd + L = 0xC, NGE = 0xC, ///< Less, Not Greater or Equal + NL = 0xD, GE = 0xD, ///< Not Less, Greater or Equal + LE = 0xE, NG = 0xE, ///< Less or Equal, Not Greater + NLE = 0xf, G = 0xF, ///< Not Less or Equal, Greater + }; + } \ No newline at end of file diff --git a/src/asmio/x86/module.cpp b/src/asmio/x86/module.cpp index 475c4ef..9f197fd 100644 --- a/src/asmio/x86/module.cpp +++ b/src/asmio/x86/module.cpp @@ -313,6 +313,45 @@ namespace asmio::x86 { throw std::runtime_error {"Unknown SIMD condition '" + raw + "'"}; } + template <> + Condition parse_argument(TokenStream stream) { + const Token& token = stream.expect(Token::NAME); + std::string raw = util::to_lower(token.raw); + + if (raw == "o") return Condition::O; + if (raw == "no") return Condition::NO; + if (raw == "b") return Condition::B; + if (raw == "c") return Condition::C; + if (raw == "nae") return Condition::NAE; + if (raw == "nb") return Condition::NB; + if (raw == "nc") return Condition::NC; + if (raw == "ae") return Condition::AE; + if (raw == "e") return Condition::E; + if (raw == "z") return Condition::Z; + if (raw == "ne") return Condition::NE; + if (raw == "nz") return Condition::NZ; + if (raw == "be") return Condition::BE; + if (raw == "na") return Condition::NA; + if (raw == "nbe") return Condition::NBE; + if (raw == "a") return Condition::A; + if (raw == "s") return Condition::S; + if (raw == "ns") return Condition::NS; + if (raw == "p") return Condition::P; + if (raw == "pe") return Condition::PE; + if (raw == "np") return Condition::NP; + if (raw == "po") return Condition::PO; + if (raw == "l") return Condition::L; + if (raw == "nge") return Condition::NGE; + if (raw == "nl") return Condition::NL; + if (raw == "ge") return Condition::GE; + if (raw == "le") return Condition::LE; + if (raw == "ng") return Condition::NG; + if (raw == "nle") return Condition::NLE; + if (raw == "g") return Condition::G; + + throw std::runtime_error {"Unknown condition '" + raw + "'"}; + } + # include "generated/x86.hpp" /* diff --git a/src/asmio/x86/writer.cpp b/src/asmio/x86/writer.cpp index dcd40c9..6f3f834 100644 --- a/src/asmio/x86/writer.cpp +++ b/src/asmio/x86/writer.cpp @@ -521,7 +521,7 @@ namespace asmio::x86 { long addend = dst.offset; if (!dst.is_jump_label()) { - throw std::runtime_error {"Invalid operand"}; + throw std::runtime_error {"Invalid operand, expected label"}; } if (buffer.has_label(label)) { @@ -549,13 +549,6 @@ namespace asmio::x86 { } - /** - * Used for constructing the 'set byte' family of instructions - */ - void BufferWriter::put_inst_setx(const Location& dst, uint8_t lopcode) { - put_inst_std_as(0b1001'0000 | lopcode, dst, RegInfo::raw(0), true); - } - void BufferWriter::put_rex_w() { put_byte(REX_PREFIX | REX_BIT_W); } diff --git a/src/asmio/x86/writer.hpp b/src/asmio/x86/writer.hpp index 03cc643..d4f81bf 100644 --- a/src/asmio/x86/writer.hpp +++ b/src/asmio/x86/writer.hpp @@ -63,9 +63,6 @@ namespace asmio::x86 { /// Used for constructing the conditional jump family of instructions void put_inst_jx(const Location& label, uint8_t sopcode, uint8_t lopcode); - /// Used for constructing the 'set byte' family of instructions - void put_inst_setx(const Location& dst, uint8_t lopcode); - /// Put SSE double XMM register operand instruction void put_inst_sse_2xmm(uint8_t opcode, Registry dst, Registry src); void put_inst_sse(uint8_t opcode, Registry reg, const Location& loc); @@ -162,6 +159,7 @@ namespace asmio::x86 { INST put_shrd(Location dst, Location src, Location cnt); ///< Double Right Shift INST put_jmp(Location dst); ///< Unconditional Jump INST put_call(Location dst); ///< Procedure Call + INST put_j(Location label, Condition cond); ///< Jump on Condition INST put_jo(Location label); ///< Jump on Overflow INST put_jno(Location label); ///< Jump on Not Overflow INST put_jb(Location label); ///< Jump on Below @@ -199,6 +197,7 @@ namespace asmio::x86 { INST put_loopz(Location label); ///< Loop RCX Times, if zero INST put_loopne(Location label); ///< Loop RCX Times, if not equal INST put_loopnz(Location label); ///< Loop RCX Times, if not zero + INST put_set(Location dst, Condition cond); /// Set Byte on Condition INST put_seto(Location dst); ///< Set Byte on Overflow INST put_setno(Location dst); ///< Set Byte on Not Overflow INST put_setb(Location dst); ///< Set Byte on Below @@ -215,28 +214,28 @@ namespace asmio::x86 { INST put_setnl(Location dst); ///< Set Byte on Not Less INST put_setle(Location dst); ///< Set Byte on Less or Equal INST put_setnle(Location dst); ///< Set Byte on Not Less or Equal - INST put_setc(Location dst); ///< Alias to JB, Jump on Carry - INST put_setnc(Location dst); ///< Alias to JNB, Jump on Not Carry - INST put_setnae(Location dst); ///< Alias to JB, Jump on Not Above or Equal - INST put_setae(Location dst); ///< Alias to JNB, Jump on Above or Equal - INST put_setz(Location dst); ///< Alias to JE, Jump on Zero - INST put_setnz(Location dst); ///< Alias to JNE, Jump on Not Zero - INST put_setna(Location dst); ///< Alias to JBE, Jump on Not Above - INST put_seta(Location dst); ///< Alias to JNBE, Jump on Above - INST put_setpe(Location dst); ///< Alias to JP, Jump on Parity Even - INST put_setpo(Location dst); ///< Alias to JNP, Jump on Parity Odd - INST put_setnge(Location dst); ///< Alias to JL, Jump on Not Greater or Equal - INST put_setge(Location dst); ///< Alias to JNL, Jump on Greater or Equal - INST put_setng(Location dst); ///< Alias to JLE, Jump on Not Greater - INST put_setg(Location dst); ///< Alias to JNLE, Jump on Greater - INST put_int(Location type); ///< Interrupt + INST put_setc(Location dst); ///< Alias to SETB, Set Byte on Carry + INST put_setnc(Location dst); ///< Alias to SETNB, Set Byte on Not Carry + INST put_setnae(Location dst); ///< Alias to SETB, Set Byte on Not Above or Equal + INST put_setae(Location dst); ///< Alias to SETNB, Set Byte on Above or Equal + INST put_setz(Location dst); ///< Alias to SETE, Set Byte on Zero + INST put_setnz(Location dst); ///< Alias to SETNE, Set Byte on Not Zero + INST put_setna(Location dst); ///< Alias to SETBE, Set Byte on Not Above + INST put_seta(Location dst); ///< Alias to SETNBE, Set Byte on Above + INST put_setpe(Location dst); ///< Alias to SETP, Set Byte on Parity Even + INST put_setpo(Location dst); ///< Alias to SETNP, Set Byte on Parity Odd + INST put_setnge(Location dst); ///< Alias to SETL, Set Byte on Not Greater or Equal + INST put_setge(Location dst); ///< Alias to SETNL, Set Byte on Greater or Equal + INST put_setng(Location dst); ///< Alias to SETLE, Set Byte on Not Greater + INST put_setg(Location dst); ///< Alias to SETNLE, Set Byte on Greater + INST put_int(uint8_t offset); ///< Interrupt INST put_into(); ///< Interrupt if Overflow INST put_iret(); ///< Return from Interrupt INST put_nop(); ///< No Operation INST put_hlt(); ///< Halt INST put_wait(); ///< Wait INST put_ud2(); ///< Undefined Instruction - INST put_enter(Location alc, Location nst); ///< Enter Procedure + INST put_enter(uint16_t alc, uint8_t nst); ///< Enter Procedure INST put_leave(); ///< Leave Procedure INST put_pusha(); ///< Push RBX, RBP, R12-R15 INST put_popa(); ///< Pop RBX, RBP, R12-R15 @@ -251,9 +250,9 @@ namespace asmio::x86 { INST put_std(); ///< Set Direction Flag INST put_cli(); ///< Clear Interrupt Flag INST put_sti(); ///< Set Interrupt Flag - INST put_scf(Location src); ///< Set Carry Flag to Immediate, ASMIOV extension - INST put_sdf(Location src); ///< Set Direction Flag to Immediate, ASMIOV extension - INST put_sif(Location src); ///< Set Interrupt Flag to Immediate, ASMIOV extension + INST put_scf(bool src); ///< Set Carry Flag to Immediate, ASMIOV extension + INST put_sdf(bool src); ///< Set Direction Flag to Immediate, ASMIOV extension + INST put_sif(bool src); ///< Set Interrupt Flag to Immediate, ASMIOV extension INST put_sahf(); ///< Store AH into flags INST put_lahf(); ///< Load status flags into AH register INST put_aaa(); ///< ASCII adjust for add @@ -266,17 +265,17 @@ namespace asmio::x86 { INST put_in(Location dst, Location src); ///< Input from Port INST put_out(Location dst, Location src); ///< Output to Port INST put_test(Location dst, Location src); ///< Test For Bit Pattern - INST put_test(Location src); ///< Sets flags accordingly to the value of register given, ASMIOV extension + INST put_test(Registry src); ///< Sets flags accordingly to the value of register given, ASMIOV extension INST put_ret(); ///< Return from procedure - INST put_ret(Location bytes); ///< Return from procedure and pop X bytes + INST put_ret(uint16_t offset); ///< Return from procedure and pop X bytes INST put_cpuid(); ///< Return CPU information in EAX, EBX, ECX, and EDX registers // i486 - INST put_xadd(Location dst, Location src); ///< Exchange and Add + INST put_xadd(Location dst, Registry src); ///< Exchange and Add INST put_bswap(Location dst); ///< Byte Swap INST put_invd(); ///< Invalidate Internal Caches INST put_wbinvd(); ///< Write Back and Invalidate Cache - INST put_cmpxchg(Location dst, Location src); ///< Compare and Exchange + INST put_cmpxchg(Location dst, Registry src); ///< Compare and Exchange // x86-64 INST put_cqo(); ///< Convert Doubleword to Quadword @@ -363,50 +362,50 @@ namespace asmio::x86 { INST put_fdivrp(Location dst); ///< Reverse Divide And Pop // sse - INST put_movaps(Location dst, Location src); ///< Move Aligned Packed Single Precision Floating-Point Values - INST put_movhlps(Registry dst, Registry src); ///< Move Packed Single Precision Floating-Point Values High to Low - INST put_movlhps(Registry dst, Registry src); ///< Move Packed Single Precision Floating-Point Values Low to High - INST put_movhps(Location dst, Location src); ///< Move two packed single precision floating-point values from m64 to high quadword of dst - INST put_movlps(Location dst, Location src); ///< Move two packed single precision floating-point values from m64 to low quadword of dst - INST put_movmskps(Registry dst, Registry src); ///< Extract Packed Single Precision Floating-Point Sign Mask - INST put_movss(Location dst, Location src); ///< - INST put_movups(Location dst, Location src); ///< - INST put_addps(Registry dst, Location src); ///< Add packed single precision floating-point values - INST put_addss(Registry dst, Location src); ///< - INST put_divps(Registry dst, Location src); ///< Divide packed single precision floating-point values - INST put_divss(Registry dst, Location src); ///< - INST put_maxps(Registry dst, Location src); ///< Maximum packed single precision floating-point values - INST put_maxss(Registry dst, Location src); ///< - INST put_minps(Registry dst, Location src); ///< Minimum packed single precision floating-point values - INST put_minss(Registry dst, Location src); ///< - INST put_mulps(Registry dst, Location src); ///< Minimum packed single precision floating-point values - INST put_mulss(Registry dst, Location src); ///< - INST put_rcpps(Registry dst, Location src); ///< Compute reciprocals of packed single precision floating-point values - INST put_rcpss(Registry dst, Location src); ///< - INST put_rsqrtps(Registry dst, Location src); ///< Compute reciprocals of square roots of packed single precision floating-point values - INST put_rsqrtss(Registry dst, Location src); ///< - INST put_sqrtps(Registry dst, Location src); ///< Compute square roots of packed single precision floating-point values - INST put_sqrtss(Registry dst, Location src); ///< - INST put_subps(Registry dst, Location src); ///< Subtract packed single precision floating-point values - INST put_subss(Registry dst, Location src); ///< - INST put_cmpps(Registry dst, Location src, SimdCondition cond); ///< Compare packed 32 bit floating point values - INST put_cmpss(Registry dst, Location src, SimdCondition cond); ///< - INST put_comiss(Registry dst, Location src); ///< - INST put_ucomiss(Registry dst, Location src); ///< - INST put_andnps(Registry dst, Location src); ///< Bitwise logical AND NOT of packed 32 bit values - INST put_andps(Registry dst, Location src); ///< Bitwise logical AND of packed 32 bit values - INST put_orps(Registry dst, Location src); ///< Bitwise logical OR of packed 32 bit values - INST put_xorps(Registry dst, Location src); ///< Bitwise logical XOR of packed 32 bit values - INST put_shufps(Registry dst, Location src, uint8_t selector); ///< - INST put_unpckhps(Registry dst, Location src); ///< - INST put_unpcklps(Registry dst, Location src); ///< - INST put_cvtsi2ss(Registry dst, Location src); ///< Convert Doubleword Integer to Scalar Single Precision Floating-Point Value - INST put_cvtss2si(Registry dst, Location src); ///< - INST put_cvttss2si(Registry dst, Location src); ///< - INST put_ldmxcsr(Location src); ///< - INST put_stmxcsr(Location dst); ///< - INST put_movntps(Location dst, Registry src); ///< - INST put_sfence(); ///< + INST put_movaps(Location dst, Location src); ///< Move Aligned Packed f32 Values + INST put_movhlps(Registry dst, Registry src); ///< Move Packed f32 Values High to Low + INST put_movlhps(Registry dst, Registry src); ///< Move Packed f32 Values Low to High + INST put_movhps(Location dst, Location src); ///< Move two packed f32 values from m64 to high quadword of dst + INST put_movlps(Location dst, Location src); ///< Move two packed f32 values from m64 to low quadword of dst + INST put_movmskps(Registry dst, Registry src); ///< Extract Packed f32 Sign Mask + INST put_movss(Location dst, Location src); ///< Move or Merge Scalar f32 Value + INST put_movups(Location dst, Location src); ///< Move Unaligned Packed f32 Values + INST put_addps(Registry dst, Location src); ///< Add Packed f32 values + INST put_addss(Registry dst, Location src); ///< Add Scalar f32 Values + INST put_divps(Registry dst, Location src); ///< Divide Packed f32 values + INST put_divss(Registry dst, Location src); ///< Divide Scalar f32 values + INST put_maxps(Registry dst, Location src); ///< Compute Maximum of packed f32 values + INST put_maxss(Registry dst, Location src); ///< Compute Maximum of Scalar f32 values + INST put_minps(Registry dst, Location src); ///< Compute Minimum of packed f32 values + INST put_minss(Registry dst, Location src); ///< Compute Minimum of Scalar f32 values + INST put_mulps(Registry dst, Location src); ///< Multiply Packed f32 Values + INST put_mulss(Registry dst, Location src); ///< Multiply Scalar f32 Values + INST put_rcpps(Registry dst, Location src); ///< Compute Reciprocals of Packed f32 values + INST put_rcpss(Registry dst, Location src); ///< Compute Reciprocals of Scalar f32 values + INST put_rsqrtps(Registry dst, Location src); ///< Compute Square Root Reciprocals of Packed f32 values + INST put_rsqrtss(Registry dst, Location src); ///< Compute Square Root Reciprocals of Scalar f32 values + INST put_sqrtps(Registry dst, Location src); ///< Compute Square Roots of Packed f32 values + INST put_sqrtss(Registry dst, Location src); ///< Compute Square Roots of Scalar f32 values + INST put_subps(Registry dst, Location src); ///< Subtract Packed f32 values + INST put_subss(Registry dst, Location src); ///< Subtract Scalar f32 values + INST put_cmpps(Registry dst, Location src, SimdCondition cond); ///< Compare Packed f32 values + INST put_cmpss(Registry dst, Location src, SimdCondition cond); ///< Compare Scalar f32 values + INST put_comiss(Registry dst, Location src); ///< Compare Scalar Ordered f32 Values and Set EFLAGS + INST put_ucomiss(Registry dst, Location src); ///< Compare Scalar Unordered f32 Values and Set EFLAGS + INST put_andnps(Registry dst, Location src); ///< Bitwise logical AND NOT of packed dword values + INST put_andps(Registry dst, Location src); ///< Bitwise logical AND of packed dword values + INST put_orps(Registry dst, Location src); ///< Bitwise logical OR of packed dword values + INST put_xorps(Registry dst, Location src); ///< Bitwise logical XOR of packed dword values + INST put_shufps(Registry dst, Location src, uint8_t selector); ///< Packed Interleave Shuffle of Quadruplets of f32 Values + INST put_unpckhps(Registry dst, Location src); ///< Unpack and Interleave High Packed f32 Values + INST put_unpcklps(Registry dst, Location src); ///< Unpack and Interleave Low Packed f32 Values + INST put_cvtsi2ss(Registry dst, Location src); ///< Convert Doubleword Integer to Scalar f32 Value + INST put_cvtss2si(Registry dst, Location src); ///< Convert Scalar f32 Value to Doubleword Integer + INST put_cvttss2si(Registry dst, Location src); ///< Convert With Truncation Scalar f32 Value to Integer + INST put_ldmxcsr(Location src); ///< Load MXCSR Register from src + INST put_stmxcsr(Location dst); ///< Store MXCSR Register into dst + INST put_movntps(Location dst, Registry src); ///< Store Packed f32 Values Using Non-Temporal Hint + INST put_sfence(); ///< Store Fence }; diff --git a/src/asmio/x86/writer_cpu.cpp b/src/asmio/x86/writer_cpu.cpp index b2ff023..6fef351 100644 --- a/src/asmio/x86/writer_cpu.cpp +++ b/src/asmio/x86/writer_cpu.cpp @@ -505,6 +505,7 @@ namespace asmio::x86 { put_inst_std_dw(0b011010, src, dst.base.pack(), pair_size(src, dst), true /* TODO: sign flag */, true); // not sure why but it looks like IMUL uses 8bit immediate values + // TODO: There is a word/dword/qword variant too under a different opcode put_byte(val.offset); return; } @@ -729,182 +730,189 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operand"}; } + ///< Jump on Condition + void BufferWriter::put_j(Location label, Condition cond) { + const auto code = static_cast(cond); + put_inst_jx(label, 0b01110000 | code, 0b10000000 | code); + } + /// Jump on Overflow void BufferWriter::put_jo(Location label) { - put_inst_jx(label, 0b01110000, 0b10000000); + put_j(label, Condition::O); } /// Jump on Not Overflow void BufferWriter::put_jno(Location label) { - put_inst_jx(label, 0b01110001, 0b10000001); + put_j(label, Condition::NO); } /// Jump on Below void BufferWriter::put_jb(Location label) { - put_inst_jx(label, 0b01110010, 0b10000010); + put_j(label, Condition::B); } /// Jump on Not Below void BufferWriter::put_jnb(Location label) { - put_inst_jx(label, 0b01110011, 0b10000011); + put_j(label, Condition::NB); } /// Jump on Equal void BufferWriter::put_je(Location label) { - put_inst_jx(label, 0b01110100, 0b10000100); + put_j(label, Condition::E); } /// Jump on Not Equal void BufferWriter::put_jne(Location label) { - put_inst_jx(label, 0b01110101, 0b10000101); + put_j(label, Condition::NE); } /// Jump on Below or Equal void BufferWriter::put_jbe(Location label) { - put_inst_jx(label, 0b01110110, 0b10000110); + put_j(label, Condition::BE); } /// Jump on Not Below or Equal void BufferWriter::put_jnbe(Location label) { - put_inst_jx(label, 0b01110111, 0b10000111); + put_j(label, Condition::NBE); } /// Jump on Sign void BufferWriter::put_js(Location label) { - put_inst_jx(label, 0b01111000, 0b10001000); + put_j(label, Condition::S); } /// Jump on Not Sign void BufferWriter::put_jns(Location label) { - put_inst_jx(label, 0b01111001, 0b10001001); + put_j(label, Condition::NS); } /// Jump on Parity void BufferWriter::put_jp(Location label) { - put_inst_jx(label, 0b01111010, 0b10001010); + put_j(label, Condition::P); } /// Jump on Not Parity void BufferWriter::put_jnp(Location label) { - put_inst_jx(label, 0b01111011, 0b10001011); + put_j(label, Condition::NP); } /// Jump on Less void BufferWriter::put_jl(Location label) { - put_inst_jx(label, 0b01111100, 0b10001100); + put_j(label, Condition::L); } /// Jump on Not Less void BufferWriter::put_jnl(Location label) { - put_inst_jx(label, 0b01111101, 0b10001101); + put_j(label, Condition::NL); } /// Jump on Less or Equal void BufferWriter::put_jle(Location label) { - put_inst_jx(label, 0b01111110, 0b10001110); + put_j(label, Condition::LE); } /// Jump on Not Less or Equal void BufferWriter::put_jnle(Location label) { - put_inst_jx(label, 0b01111111, 0b10001111); + put_j(label, Condition::NLE); + } + + /// Set Byte on Condition + void BufferWriter::put_set(Location dst, Condition cond) { + put_inst_std_as(0b1001'0000 | static_cast(cond), dst, RegInfo::raw(0), true); } /// Set Byte on Overflow void BufferWriter::put_seto(Location dst) { - put_inst_setx(dst, 0); + put_set(dst, Condition::O); } /// Set Byte on Not Overflow void BufferWriter::put_setno(Location dst) { - put_inst_setx(dst, 1); + put_set(dst, Condition::NO); } /// Set Byte on Below void BufferWriter::put_setb(Location dst) { - put_inst_setx(dst, 2); + put_set(dst, Condition::B); } /// Set Byte on Not Below void BufferWriter::put_setnb(Location dst) { - put_inst_setx(dst, 3); + put_set(dst, Condition::NB); } /// Set Byte on Equal void BufferWriter::put_sete(Location dst) { - put_inst_setx(dst, 4); + put_set(dst, Condition::E); } /// Set Byte on Not Equal void BufferWriter::put_setne(Location dst) { - put_inst_setx(dst, 5); + put_set(dst, Condition::NE); } /// Set Byte on Below or Equal void BufferWriter::put_setbe(Location dst) { - put_inst_setx(dst, 6); + put_set(dst, Condition::BE); } /// Set Byte on Not Below or Equal void BufferWriter::put_setnbe(Location dst) { - put_inst_setx(dst, 7); + put_set(dst, Condition::NBE); } /// Set Byte on Sign void BufferWriter::put_sets(Location dst) { - put_inst_setx(dst, 8); + put_set(dst, Condition::S); } /// Set Byte on Not Sign void BufferWriter::put_setns(Location dst) { - put_inst_setx(dst, 9); + put_set(dst, Condition::NS); } /// Set Byte on Parity void BufferWriter::put_setp(Location dst) { - put_inst_setx(dst, 10); + put_set(dst, Condition::P); } /// Set Byte on Not Parity void BufferWriter::put_setnp(Location dst) { - put_inst_setx(dst, 11); + put_set(dst, Condition::NP); } /// Set Byte on Less void BufferWriter::put_setl(Location dst) { - put_inst_setx(dst, 12); + put_set(dst, Condition::L); } /// Set Byte on Not Less void BufferWriter::put_setnl(Location dst) { - put_inst_setx(dst, 13); + put_set(dst, Condition::NL); } /// Set Byte on Less or Equal void BufferWriter::put_setle(Location dst) { - put_inst_setx(dst, 14); + put_set(dst, Condition::LE); } /// Set Byte on Not Less or Equal void BufferWriter::put_setnle(Location dst) { - put_inst_setx(dst, 15); + put_set(dst, Condition::NLE); } /// Interrupt - void BufferWriter::put_int(Location type) { - - if (!type.is_immediate()) { - throw std::runtime_error {"Invalid operand"}; - } + void BufferWriter::put_int(uint8_t offset) { // short form - if (type.offset == 3) { + if (offset == 3) { put_byte(0xCC); return; } // standard form put_byte(0b11001101); - put_byte(type.offset); + put_byte(offset); } @@ -940,15 +948,10 @@ namespace asmio::x86 { } /// Enter Procedure - void BufferWriter::put_enter(Location alc, Location nst) { - if (alc.is_immediate() && nst.is_immediate()) { - put_byte(0b11001000); - put_word(alc.offset); - put_byte(nst.offset); - return; - } - - throw std::runtime_error {"Invalid operands, immediate value expected"}; + void BufferWriter::put_enter(uint16_t alc, uint8_t nst) { + put_byte(0b11001000); + put_word(alc); + put_byte(nst); } /// Leave Procedure @@ -1034,30 +1037,18 @@ namespace asmio::x86 { } /// Set Interrupt Flag to Immediate, ASMIOV extension - void BufferWriter::put_sif(Location src) { - if (!src.is_immediate()) { - throw std::runtime_error {"Invalid operand"}; - } - - if (src.offset == 0) put_cli(); else put_sti(); + void BufferWriter::put_sif(bool src) { + if (src == false) put_cli(); else put_sti(); } /// Set Carry Flag to Immediate, ASMIOV extension - void BufferWriter::put_scf(Location src) { - if (!src.is_immediate()) { - throw std::runtime_error {"Invalid operand"}; - } - - if (src.offset == 0) put_clc(); else put_stc(); + void BufferWriter::put_scf(bool src) { + if (src == false) put_clc(); else put_stc(); } /// Set Direction Flag to Immediate, ASMIOV extension - void BufferWriter::put_sdf(Location src) { - if (!src.is_immediate()) { - throw std::runtime_error {"Invalid operand"}; - } - - if (src.offset == 0) put_cld(); else put_std(); + void BufferWriter::put_sdf(bool src) { + if (src == false) put_cld(); else put_std(); } /// Store AH into flags @@ -1414,14 +1405,8 @@ namespace asmio::x86 { } /// Sets flags accordingly to the value of register given, ASMIOV extension - void BufferWriter::put_test(Location src) { - - if (src.is_simple()) { - put_test(src, src); - return; - } - - throw std::runtime_error {"Invalid operand, register expected"}; + void BufferWriter::put_test(Registry src) { + put_test(src, src); } /// Return from procedure @@ -1430,20 +1415,14 @@ namespace asmio::x86 { } /// Return from procedure and pop X bytes - void BufferWriter::put_ret(Location loc) { - if (loc.is_immediate()) { - uint32_t loc_val = loc.offset; - - if (loc_val != 0) { - put_byte(0b11000010); - put_word(loc_val); - return; - } - - return put_ret(); + void BufferWriter::put_ret(uint16_t offset) { + if (offset != 0) { + put_byte(0b11000010); + put_word(offset); + return; } - throw std::runtime_error {"Invalid operand"}; + return put_ret(); } void BufferWriter::put_cpuid() { @@ -1451,10 +1430,10 @@ namespace asmio::x86 { put_byte(0xA2); } - void BufferWriter::put_xadd(Location dst, Location src) { + void BufferWriter::put_xadd(Location dst, Registry src) { - if (dst.is_memreg() && src.is_simple()) { - put_inst_std_ds(0xC0 >> 2, dst, src.base.pack(), pair_size(dst, src), false, true); + if (dst.is_memreg()) { + put_inst_std_ds(0xC0 >> 2, dst, src.pack(), pair_size(dst, src), false, true); return; } @@ -1493,10 +1472,10 @@ namespace asmio::x86 { put_byte(0x09); } - void BufferWriter::put_cmpxchg(Location dst, Location src) { + void BufferWriter::put_cmpxchg(Location dst, Registry src) { - if (dst.is_memreg() && src.is_simple()) { - put_inst_std_ds(0xB0 >> 2, dst, src.base.pack(), pair_size(dst, src), false, true); + if (dst.is_memreg()) { + put_inst_std_ds(0xB0 >> 2, dst, src.pack(), pair_size(dst, src), false, true); return; } diff --git a/src/asmio/x86/writer_sse.cpp b/src/asmio/x86/writer_sse.cpp index c243f86..b19adfd 100644 --- a/src/asmio/x86/writer_sse.cpp +++ b/src/asmio/x86/writer_sse.cpp @@ -74,6 +74,7 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operands"}; } + /// Move Aligned Packed f32 Values void BufferWriter::put_movaps(Location dst, Location src) { if (dst.is_simple()) { @@ -89,22 +90,27 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operands"}; } + /// Move Packed f32 Values High to Low void BufferWriter::put_movhlps(Registry dst, Registry src) { put_inst_sse_2xmm(0x12, dst, src); } + /// Move Packed f32 Values Low to High void BufferWriter::put_movlhps(Registry dst, Registry src) { put_inst_sse_2xmm(0x16, dst, src); } + /// Move two packed f32 values from m64 to high quadword of dst void BufferWriter::put_movhps(Location dst, Location src) { put_inst_movxps(dst, src, 0x16); } + /// Move two packed f32 values from m64 to low quadword of dst void BufferWriter::put_movlps(Location dst, Location src) { put_inst_movxps(dst, src, 0x12); } + /// Extract Packed f32 Sign Mask void BufferWriter::put_movmskps(Registry dst, Registry src) { if (!dst.is(Registry::GENERAL)) { @@ -122,6 +128,7 @@ namespace asmio::x86 { put_inst_std(0x50, src, dst.pack(), dst.size, true); } + /// Move or Merge Scalar f32 Value void BufferWriter::put_movss(Location dst, Location src) { if (dst.is_simple()) { @@ -137,6 +144,7 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operands"}; } + /// Move Unaligned Packed f32 Values void BufferWriter::put_movups(Location dst, Location src) { if (dst.is_simple()) { @@ -152,130 +160,161 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid operands"}; } + /// Add Packed f32 values void BufferWriter::put_addps(Registry dst, Location src) { put_inst_sse(0x58, dst, src); } + /// Add Scalar f32 Values void BufferWriter::put_addss(Registry dst, Location src) { put_inst_sse_sized(0x58, dst, src, DWORD); } + /// Divide Packed f32 values void BufferWriter::put_divps(Registry dst, Location src) { put_inst_sse(0x5E, dst, src); } + /// Divide Scalar f32 values void BufferWriter::put_divss(Registry dst, Location src) { put_inst_sse_sized(0x5E, dst, src, DWORD); } + /// Compute Maximum of packed f32 values void BufferWriter::put_maxps(Registry dst, Location src) { put_inst_sse(0x5F, dst, src); } + /// Compute Maximum of Scalar f32 values void BufferWriter::put_maxss(Registry dst, Location src) { put_inst_sse_sized(0x5F, dst, src, DWORD); } + /// Compute Minimum of packed f32 values void BufferWriter::put_minps(Registry dst, Location src) { put_inst_sse(0x5D, dst, src); } + /// Compute Minimum of Scalar f32 values void BufferWriter::put_minss(Registry dst, Location src) { put_inst_sse_sized(0x5D, dst, src, DWORD); } + /// Multiply Packed f32 Values void BufferWriter::put_mulps(Registry dst, Location src) { put_inst_sse(0x59, dst, src); } + /// Multiply Scalar f32 Values void BufferWriter::put_mulss(Registry dst, Location src) { put_inst_sse_sized(0x59, dst, src, DWORD); } + /// Compute Reciprocals of Packed f32 values void BufferWriter::put_rcpps(Registry dst, Location src) { put_inst_sse(0x53, dst, src); } + /// Compute Reciprocals of Scalar f32 values void BufferWriter::put_rcpss(Registry dst, Location src) { put_inst_sse_sized(0x53, dst, src, DWORD); } + /// Compute Square Root Reciprocals of Packed f32 values void BufferWriter::put_rsqrtps(Registry dst, Location src) { put_inst_sse(0x52, dst, src); } + /// Compute Square Root Reciprocals of Scalar f32 values void BufferWriter::put_rsqrtss(Registry dst, Location src) { put_inst_sse_sized(0x52, dst, src, DWORD); } + /// Compute Square Roots of Packed f32 values void BufferWriter::put_sqrtps(Registry dst, Location src) { put_inst_sse(0x51, dst, src); } + /// Compute Square Roots of Scalar f32 values void BufferWriter::put_sqrtss(Registry dst, Location src) { put_inst_sse_sized(0x51, dst, src, DWORD); } + /// Subtract Packed f32 values void BufferWriter::put_subps(Registry dst, Location src) { put_inst_sse(0x5C, dst, src); } + /// Subtract Scalar f32 values void BufferWriter::put_subss(Registry dst, Location src) { put_inst_sse_sized(0x5C, dst, src, DWORD); } + /// Compare Packed f32 values void BufferWriter::put_cmpps(Registry dst, Location src, SimdCondition cond) { put_inst_sse(0xC2, dst, src); put_byte(static_cast(cond)); } + /// Compare Scalar f32 values void BufferWriter::put_cmpss(Registry dst, Location src, SimdCondition cond) { put_inst_sse_sized(0xC2, dst, src, DWORD); put_byte(static_cast(cond)); } + /// Compare Scalar Ordered f32 Values and Set EFLAGS void BufferWriter::put_comiss(Registry dst, Location src) { put_inst_sse_sized(0x2F, dst, src, DWORD, false); } + /// Compare Scalar Unordered f32 Values and Set EFLAGS void BufferWriter::put_ucomiss(Registry dst, Location src) { put_inst_sse_sized(0x2E, dst, src, DWORD, false); } + /// Bitwise logical AND NOT of packed dword values void BufferWriter::put_andnps(Registry dst, Location src) { put_inst_sse(0x55, dst, src); } + /// Bitwise logical AND of packed dword values void BufferWriter::put_andps(Registry dst, Location src) { put_inst_sse(0x54, dst, src); } + /// Bitwise logical OR of packed dword values void BufferWriter::put_orps(Registry dst, Location src) { put_inst_sse(0x56, dst, src); } + /// Bitwise logical XOR of packed dword values void BufferWriter::put_xorps(Registry dst, Location src) { put_inst_sse(0x57, dst, src); } + /// Packed Interleave Shuffle of Quadruplets of f32 Values void BufferWriter::put_shufps(Registry dst, Location src, uint8_t selector) { put_inst_sse(0xC6, dst, src); put_byte(selector); } + /// Unpack and Interleave High Packed f32 Values void BufferWriter::put_unpckhps(Registry dst, Location src) { put_inst_sse(0x15, dst, src); } + /// Unpack and Interleave Low Packed f32 Values void BufferWriter::put_unpcklps(Registry dst, Location src) { put_inst_sse(0x14, dst, src); } + /// Convert Doubleword Integer to Scalar f32 Value void BufferWriter::put_cvtsi2ss(Registry dst, Location src) { put_byte(0xF3); put_inst_std(0x2A, src, dst.pack(), src.size, true); } + /// Convert Scalar f32 Value to Doubleword Integer void BufferWriter::put_cvtss2si(Registry dst, Location src) { if (!dst.is(Registry::GENERAL)) { @@ -298,6 +337,7 @@ namespace asmio::x86 { put_inst_std(0x2D, src, dst.pack(), dst.size, true); } + /// Convert With Truncation Scalar f32 Value to Integer void BufferWriter::put_cvttss2si(Registry dst, Location src) { if (!dst.is(Registry::GENERAL)) { @@ -320,14 +360,17 @@ namespace asmio::x86 { put_inst_std(0x2C, src, dst.pack(), dst.size, true); } + /// Load MXCSR Register from src void BufferWriter::put_ldmxcsr(Location src) { put_inst_mxcsr(src, 2); } + /// Store MXCSR Register into dst void BufferWriter::put_stmxcsr(Location dst) { put_inst_mxcsr(dst, 3); } + /// Store Packed f32 Values Using Non-Temporal Hint void BufferWriter::put_movntps(Location dst, Registry src) { if (dst.is_memory()) { put_inst_sse(0x2B, src, dst); @@ -337,6 +380,7 @@ namespace asmio::x86 { throw std::runtime_error {"Invalid destination operand, expected memory reference"}; } + /// Store Fence void BufferWriter::put_sfence() { put_byte(0x0F); put_byte(0xAE); diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 26bbd87..bb29150 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -155,6 +155,20 @@ namespace test { #if ARCH_AARCH64 + TEST (check_no_f80) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + writer.put_ret(); + + auto exe = to_executable(segmented); + + EXPECT_THROW(std::runtime_error) { + exe.call_f80(); + }; + + } + TEST (writer_exec_nop_ret) { SegmentedBuffer segmented; diff --git a/util/felix b/util/felix new file mode 100755 index 0000000..af45fd4 --- /dev/null +++ b/util/felix @@ -0,0 +1,52 @@ +#!/bin/env python3 +import requests +import sys +from bs4 import BeautifulSoup + +def to_markdown_table(data): + header = data[0] + rows = data[1:] + + md = "| " + " | ".join(map(str, header)) + " |\n" + md += "| " + " | ".join(["---"] * len(header)) + " |\n" + + for row in rows: + md += "| " + " | ".join(map(str, row)) + " |\n" + + return md + +if len(sys.argv) < 2: + print("Usage: felix-fetch ...") + exit(1) + +def print_x86(mnm): + url = 'https://www.felixcloutier.com/x86/' + mnm + res = requests.get(url) + + print("## " + mnm.upper()) + + if res.status_code != 200: + print("Mnemonic not found!\n") + return + + soup = BeautifulSoup(res.content, 'html.parser') + title = soup.title.string.split('—', 1)[1].strip(' \t\n\r') + + print(title) + + table = soup.find("table") # first table + rows = [] + + for row in table.find_all("tr"): + cells = row.find_all(["td", "th"]) + data = [cell.get_text(strip=True) for cell in cells] + + if data[0].startswith("VEX") or data[0].startswith("EVEX"): + continue + + rows.append(data) + + print(to_markdown_table(rows)) + +for arg in sys.argv[1:]: + print_x86(arg.lower()) From 2d9d125e59468aa10b2e07eaa217922bd3958c3b Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sat, 11 Apr 2026 09:33:20 +0200 Subject: [PATCH 10/44] Resolved 2 TODOs, cleanup --- src/asmio/elf/export.cpp | 104 +++++++++++++++++--------------- src/asmio/elf/header.hpp | 21 ++++++- src/asmio/elf/model.cpp | 2 +- src/asmio/program/segmented.cpp | 2 +- src/asmio/program/segmented.hpp | 2 +- src/asmio/program/writer.cpp | 4 +- src/asmio/program/writer.hpp | 2 +- src/asmio/util.hpp | 8 ++- src/asmio/x86/writer_cpu.cpp | 23 +++++-- test/unit.cpp | 1 + test/x86.cpp | 63 +++++++++++++++++++ 11 files changed, 168 insertions(+), 64 deletions(-) diff --git a/src/asmio/elf/export.cpp b/src/asmio/elf/export.cpp index af6a233..3ec74a8 100644 --- a/src/asmio/elf/export.cpp +++ b/src/asmio/elf/export.cpp @@ -5,6 +5,11 @@ namespace asmio { + struct MappingInfo { + ElfModel::Section* section; + ElfSymbolType content; + }; + static void export_line_data(ElfModel& model, SegmentedBuffer& segmented) { // don't create the line emitter if we have no lines @@ -28,15 +33,54 @@ namespace asmio { } emitter.end_sequence(); + } + + static void export_defined_symbols(ElfModel& model, SegmentedBuffer& segmented, std::unordered_map& section_map) { + for (const ExportSymbol& symbol : segmented.exports()) { + const Label& label = symbol.label; + + if (!label.is_text()) { + continue; + } + + BufferMarker marker = segmented.get_label(label); + MappingInfo info = section_map[marker.section]; + + ElfSymbolBinding binding = ElfSymbolBinding::GLOBAL; + ElfSymbolVisibility visibility = ElfSymbolVisibility::DEFAULT; + + switch (symbol.type) { + + case ExportSymbol::PRIVATE: + binding = ElfSymbolBinding::LOCAL; + visibility = ElfSymbolVisibility::HIDDEN; + break; + + case ExportSymbol::PUBLIC: + binding = ElfSymbolBinding::GLOBAL; + visibility = ElfSymbolVisibility::PROTECTED; + break; + + case ExportSymbol::WEAK: + binding = ElfSymbolBinding::WEAK; + visibility = ElfSymbolVisibility::PROTECTED; + break; + } + + model.symbol(info.content, label.view(), binding, visibility, info.section, marker.offset, symbol.size); + } } - ElfModel to_elf(SegmentedBuffer& segmented, const Label& entry, uint64_t address, const LinkReporter& handler) { + static void export_undefined_symbols(ElfModel& model, std::vector& symbols, std::unordered_map& section_map) { + for (auto& linkage : symbols) { + auto mapping = section_map[linkage.target.section]; + ElfModel::Symbol* symbol = model.symbol(mapping.content, linkage.label.view(), ElfSymbolBinding::GLOBAL, ElfSymbolVisibility::DEFAULT, nullptr, 0, 0); + model.relocation(linkage.type.relocation, symbol, mapping.section, linkage.target.offset, linkage.addend); + } + } - struct MappingInfo { - ElfModel::Section* section; - ElfSymbolType content; - }; + ElfModel to_elf(SegmentedBuffer& segmented, const Label& entry, uint64_t address, const LinkReporter& handler) { // after alignment, we will know how big the buffer needs to be const size_t page = getpagesize(); @@ -77,58 +121,20 @@ namespace asmio { auto* elf_section = model.section(ElfSectionType::PROGBITS, segment.name, elf_segment, address, 1, 0, segment.flags.to_elf_section()); elf_section_buffer = elf_section->buffer; - const ElfSymbolType content = segment.flags.to_elf_symbol(); - section_map[segment.index] = {elf_section, content}; + const ElfSymbolType resident = segment.flags.to_elf_symbol(); + section_map[segment.index] = {elf_section, resident}; } elf_section_buffer->write(segment.buffer); elf_segment->buffer->push(segment.tail); } - for (const ExportSymbol& symbol : segmented.exports()) { - const Label& label = symbol.label; - - if (!label.is_text()) { - continue; - } - - BufferMarker marker = segmented.get_label(label); - MappingInfo info = section_map[marker.section]; - - ElfSymbolBinding binding = ElfSymbolBinding::GLOBAL; - ElfSymbolVisibility visibility = ElfSymbolVisibility::DEFAULT; - - switch (symbol.type) { - - case ExportSymbol::PRIVATE: - binding = ElfSymbolBinding::LOCAL; - visibility = ElfSymbolVisibility::HIDDEN; - break; - - case ExportSymbol::PUBLIC: - binding = ElfSymbolBinding::GLOBAL; - visibility = ElfSymbolVisibility::PROTECTED; - break; - - case ExportSymbol::WEAK: - binding = ElfSymbolBinding::WEAK; - visibility = ElfSymbolVisibility::PROTECTED; - break; - - } - - model.symbol(info.content, label.view(), binding, visibility, info.section, marker.offset, symbol.size); - } - - for (auto& linkage : unresolved) { - auto mapping = section_map[linkage.target.section]; - ElfModel::Symbol* symbol = model.symbol(mapping.content, linkage.label.view(), ElfSymbolBinding::GLOBAL, ElfSymbolVisibility::DEFAULT, nullptr, 0, 0); - model.relocation(linkage.type.relocation, symbol, mapping.section, linkage.target.offset, linkage.addend); - + if (create_sections) { + export_defined_symbols(model, segmented, section_map); + export_undefined_symbols(model, unresolved, section_map); + export_line_data(model, segmented); } - export_line_data(model, segmented); - return model; } diff --git a/src/asmio/elf/header.hpp b/src/asmio/elf/header.hpp index 0c2c46d..1000c81 100644 --- a/src/asmio/elf/header.hpp +++ b/src/asmio/elf/header.hpp @@ -46,13 +46,32 @@ namespace asmio { LSB = 1, // Two's complement, little-endian. MSB = 2, // Two's complement, big-endian. }; + + enum struct ElfAbi : uint8_t { + NONE = 0, ///< UNIX System V ABI + SYSV = 0, ///< Alias. + HPUX = 1, ///< HP-UX + NETBSD = 2, ///< NetBSD. + GNU = 3, ///< Object uses GNU ELF extensions. + LINUX = 3, ///< Compatibility alias. + SOLARIS = 6, ///< Sun Solaris. + AIX = 7, ///< IBM AIX. + IRIX = 8, ///< SGI Irix. + FREEBSD = 9, ///< FreeBSD. + TRU64 = 10, ///< Compaq TRU64 UNIX. + MODESTO = 11, ///< Novell Modesto. + OPENBSD = 12, ///< OpenBSD. + ARM_AEABI = 64, ///< ARM EABI + ARM = 97, ///< ARM + STANDALONE = 255, ///< Standalone (embedded) application + }; struct PACKED ElfIdentification { uint8_t magic[4]; ///< Magic number identifying the file as an ELF object file ElfClass clazz; ///< Size of basic data types ElfData data; ///< Endianness uint8_t version; ///< Same as FileHeader->version - uint8_t abi; ///< Target operating system + ElfAbi abi; ///< Target operating system uint8_t abi_version; ///< Version of the ABI to which the object is targeted uint8_t pad[7]; ///< Must all be 0 }; diff --git a/src/asmio/elf/model.cpp b/src/asmio/elf/model.cpp index 22e0f4e..afe6df4 100644 --- a/src/asmio/elf/model.cpp +++ b/src/asmio/elf/model.cpp @@ -190,7 +190,7 @@ namespace asmio { ident.clazz = ElfClass::BIT_64; ident.data = ElfData::LSB; ident.version = ELF_VERSION; - ident.abi = 0; // TODO: move to enum + ident.abi = ElfAbi::SYSV; ident.abi_version = 0; memset(ident.pad, 0, sizeof(ident.pad)); diff --git a/src/asmio/program/segmented.cpp b/src/asmio/program/segmented.cpp index b77558e..f3bba74 100644 --- a/src/asmio/program/segmented.cpp +++ b/src/asmio/program/segmented.cpp @@ -148,7 +148,7 @@ namespace asmio { buffer.resize(buffer.size() + bytes, value); } - void SegmentedBuffer::insert(uint8_t* data, size_t bytes) { + void SegmentedBuffer::insert(const uint8_t* data, size_t bytes) { auto& buffer = sections[selected].buffer; buffer.insert(buffer.end(), data, data + bytes); } diff --git a/src/asmio/program/segmented.hpp b/src/asmio/program/segmented.hpp index efeab4b..8e459d0 100644 --- a/src/asmio/program/segmented.hpp +++ b/src/asmio/program/segmented.hpp @@ -158,7 +158,7 @@ namespace asmio { void fill(int64_t bytes, uint8_t value); /// Append arbitrary data into the current section - void insert(uint8_t* data, size_t bytes); + void insert(const uint8_t* data, size_t bytes); /// Select the section to use void use_section(MemoryFlags flags, const std::string& name = ""); diff --git a/src/asmio/program/writer.cpp b/src/asmio/program/writer.cpp index 321f962..ab1568d 100644 --- a/src/asmio/program/writer.cpp +++ b/src/asmio/program/writer.cpp @@ -88,8 +88,8 @@ namespace asmio { buffer.insert((uint8_t*) std::data(dwords), QWORD * dwords.size()); } - void BasicBufferWriter::put_data(size_t bytes, void* date) { - buffer.insert((uint8_t*) date, bytes); + void BasicBufferWriter::put_data(size_t bytes, const void* data) { + buffer.insert((const uint8_t*) data, bytes); } void BasicBufferWriter::put_space(size_t bytes, uint8_t value) { diff --git a/src/asmio/program/writer.hpp b/src/asmio/program/writer.hpp index 1cf22c7..138d9c0 100644 --- a/src/asmio/program/writer.hpp +++ b/src/asmio/program/writer.hpp @@ -42,7 +42,7 @@ namespace asmio { void put_qword(std::initializer_list dword); void put_qword_f(double dword); void put_qword_f(std::initializer_list dword); - void put_data(size_t bytes, void* date); + void put_data(size_t bytes, const void* data); void put_space(size_t bytes, uint8_t value = 0); }; diff --git a/src/asmio/util.hpp b/src/asmio/util.hpp index 4c43e48..bbf2978 100644 --- a/src/asmio/util.hpp +++ b/src/asmio/util.hpp @@ -262,14 +262,16 @@ namespace asmio::util { * to losslessly encode the given signed integer. */ constexpr int min_signed_bytes(int64_t value) { + const uint64_t uval = static_cast(value); + if ((value & 0xFFFF'FFFF'FFFF'FF80) == 0xFFFF'FFFF'FFFF'FF80) return 1; // 1 byte long negative - if (value <= 0x0000'0000'0000'007F) return 1; // 1 byte long positive + if (uval <= 0x0000'0000'0000'007F) return 1; // 1 byte long positive if ((value & 0xFFFF'FFFF'FFFF'8000) == 0xFFFF'FFFF'FFFF'8000) return 2; // 2 byte long negative - if (value <= 0x0000'0000'0000'7FFF) return 2; // 2 byte long positive + if (uval <= 0x0000'0000'0000'7FFF) return 2; // 2 byte long positive if ((value & 0xFFFF'FFFF'8000'0000) == 0xFFFF'FFFF'8000'0000) return 4; // 4 byte long negative - if (value <= 0x0000'0000'7FFF'FFFF) return 4; // 4 byte long positive + if (uval <= 0x0000'0000'7FFF'FFFF) return 4; // 4 byte long positive return 8; } diff --git a/src/asmio/x86/writer_cpu.cpp b/src/asmio/x86/writer_cpu.cpp index 6fef351..9c7d962 100644 --- a/src/asmio/x86/writer_cpu.cpp +++ b/src/asmio/x86/writer_cpu.cpp @@ -502,12 +502,25 @@ namespace asmio::x86 { } if (dst.is_simple() && src.is_memreg() && val.is_immediate()) { - put_inst_std_dw(0b011010, src, dst.base.pack(), pair_size(src, dst), true /* TODO: sign flag */, true); + const uint64_t imm = val.offset; + const uint8_t bytes = util::min_signed_bytes(imm); + const uint8_t size = pair_size(src, dst); + + if (bytes == BYTE) { + put_inst_std(0x6B, src, dst.base.pack(), size); + put_byte(val.offset); + return; + } - // not sure why but it looks like IMUL uses 8bit immediate values - // TODO: There is a word/dword/qword variant too under a different opcode - put_byte(val.offset); - return; + const uint8_t limit = std::min(static_cast(DWORD), size); + + if (bytes <= limit) { + put_inst_std(0x69, src, dst.base.pack(), size); + put_data(limit, &val.offset); + return; + } + + throw std::runtime_error {"Invalid immediate operand, value too long"}; } throw std::runtime_error {"Invalid operands"}; diff --git a/test/unit.cpp b/test/unit.cpp index 40b935d..e1d5d4b 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -71,6 +71,7 @@ namespace test { CHECK(util::min_signed_bytes(0x7FFF'FF01), 4); CHECK(util::min_signed_bytes(0x80), 2); CHECK(util::min_signed_bytes(0xFFFF), 4); + CHECK(util::min_signed_bytes(-1000), 2); }; diff --git a/test/x86.cpp b/test/x86.cpp index e01731a..0b9f804 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -801,6 +801,28 @@ namespace test { }; + TEST (check_imul) { + + SegmentedBuffer buffer; + BufferWriter writer {buffer}; + + // byte register not allowed + EXPECT_THROW(std::runtime_error) { + writer.put_imul(AL, AL, 1); + }; + + // can't encode 64 bit immediate + EXPECT_THROW(std::runtime_error) { + writer.put_imul(RAX, RAX, 10'000'000'000); + }; + + // can't encode 32 bit immediate for 16 operation + EXPECT_THROW(std::runtime_error) { + writer.put_imul(AX, AX, 100'000); + }; + + }; + /* * region Executable * Begin architecture depended tests for x86 @@ -1421,6 +1443,47 @@ namespace test { } + TEST(writer_exec_mul_imul_longer) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.label("imul_2"); + writer.put_mov(RAX, 2); + writer.put_imul(AX, AX, 1000); + writer.put_ret(); + + writer.label("imul_3"); + writer.put_mov(RAX, 3); + writer.put_imul(RAX, RAX, 1000); + writer.put_ret(); + + writer.label("imul_4"); + writer.put_mov(RAX, 4); + writer.put_imul(EAX, EAX, 1000000); + writer.put_ret(); + + writer.label("imul_5"); + writer.put_mov(RAX, 5); + writer.put_imul(RAX, RAX, 1000000000); + writer.put_ret(); + + writer.label("imul_6"); + writer.put_mov(EAX, 6); + writer.put_imul(AX, AX, -1000); + writer.put_movsx(RAX, AX); + writer.put_ret(); + + ExecutableBuffer buffer = to_executable(segmented); + + CHECK(buffer.call_i32("imul_2"), 2000); + CHECK(buffer.call_i32("imul_3"), 3000); + CHECK(buffer.call_u32("imul_4"), 4000000); + CHECK(buffer.call_u64("imul_5"), 5000000000); + CHECK(buffer.call_i32("imul_6"), -6000); + + } + TEST(writer_exec_div_idiv) { SegmentedBuffer segmented; From 4a71b1d289b18d4e50fe073f7bea09356f85cdce Mon Sep 17 00:00:00 2001 From: magistermaks Date: Tue, 14 Apr 2026 07:31:39 +0200 Subject: [PATCH 11/44] Separate platform specific logic --- src/asmio/elf/export.cpp | 5 +- src/asmio/elf/model.cpp | 6 +- src/asmio/elf/model.hpp | 2 +- src/asmio/elf/object.cpp | 67 +-------------- src/asmio/elf/object.hpp | 28 +++---- src/asmio/external.hpp | 11 --- src/asmio/program/executable.cpp | 16 ++-- src/asmio/program/label.hpp | 3 +- src/asmio/program/memory.cpp | 6 +- src/asmio/util/codecs.cpp | 2 +- src/asmio/util/platform.cpp | 140 +++++++++++++++++++++++++++++++ src/asmio/util/platform.hpp | 26 ++++++ src/asmio/util/tmp.cpp | 2 +- test/aarch64.cpp | 2 +- test/test.hpp | 55 +----------- test/x86.cpp | 7 +- 16 files changed, 216 insertions(+), 162 deletions(-) create mode 100644 src/asmio/util/platform.cpp create mode 100644 src/asmio/util/platform.hpp diff --git a/src/asmio/elf/export.cpp b/src/asmio/elf/export.cpp index 3ec74a8..ed7f4de 100644 --- a/src/asmio/elf/export.cpp +++ b/src/asmio/elf/export.cpp @@ -2,6 +2,7 @@ #include "dwarf/lines.hpp" #include +#include namespace asmio { @@ -83,7 +84,7 @@ namespace asmio { ElfModel to_elf(SegmentedBuffer& segmented, const Label& entry, uint64_t address, const LinkReporter& handler) { // after alignment, we will know how big the buffer needs to be - const size_t page = getpagesize(); + const size_t page = page_size(); segmented.align(page); auto unresolved = segmented.link(address, handler); @@ -138,4 +139,4 @@ namespace asmio { return model; } -} \ No newline at end of file +} diff --git a/src/asmio/elf/model.cpp b/src/asmio/elf/model.cpp index afe6df4..6ef4019 100644 --- a/src/asmio/elf/model.cpp +++ b/src/asmio/elf/model.cpp @@ -1,5 +1,7 @@ #include "model.hpp" +#include + namespace asmio { /* @@ -58,7 +60,7 @@ namespace asmio { } ElfModel::Segment* ElfModel::segment(ElfSegmentType type, uint32_t flags, uint64_t address, uint64_t tail) { - const int alignment = getpagesize(); + const int alignment = page_size(); Segment segment {}; segment.type = type; @@ -277,4 +279,4 @@ namespace asmio { return {elf.bake()}; } -} \ No newline at end of file +} diff --git a/src/asmio/elf/model.hpp b/src/asmio/elf/model.hpp index 1398105..b386200 100644 --- a/src/asmio/elf/model.hpp +++ b/src/asmio/elf/model.hpp @@ -85,7 +85,7 @@ namespace asmio { private: - static constexpr int ELF_VERSION = 1; + constexpr static int ELF_VERSION = 1; Indexer section_indexer; diff --git a/src/asmio/elf/object.cpp b/src/asmio/elf/object.cpp index 354da88..f6952a2 100644 --- a/src/asmio/elf/object.cpp +++ b/src/asmio/elf/object.cpp @@ -2,19 +2,15 @@ #include "object.hpp" #include +#include namespace asmio { std::ostream& operator<<(std::ostream& os, RunStatus c) { switch (c) { case RunStatus::SUCCESS: return os << "SUCCESS"; - case RunStatus::ARGS_ERROR: return os << "ARGS_ERROR"; - case RunStatus::MEMFD_ERROR: return os << "MEMFD_ERROR"; - case RunStatus::SEAL_ERROR: return os << "SEAL_ERROR"; - case RunStatus::FORK_ERROR: return os << "FORK_ERROR"; - case RunStatus::EXEC_ERROR: return os << "EXEC_ERROR"; - case RunStatus::WAIT_ERROR: return os << "WAIT_ERROR"; - default: return os << "UNKNOWN"; + case RunStatus::ERROR: return os << "ERROR"; + default: return os << "INVALID"; } } @@ -71,62 +67,7 @@ namespace asmio { } RunResult ObjectFile::execute(const char** argv, const char** envp) const { - // verify arguments, status can be a nullptr - if (argv == nullptr || envp == nullptr) { - return RunStatus::ARGS_ERROR; - } - - // create in-memory file descriptor - const int memfd = memfd_create("buffer", MFD_ALLOW_SEALING | MFD_CLOEXEC); - if (memfd == -1) { - return RunStatus::MEMFD_ERROR; - } - - int* flag = (int*) mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if (flag == nullptr) { - return RunStatus::MMAP_ERROR; - } - - // copy buffer into memfd - write(memfd, image.data(), image.size()); - - // we use this to check if the child really run or did execve just fail - *flag = 0; - - // add seals to memfd - if (fcntl(memfd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL) != 0) { - return RunStatus::SEAL_ERROR; - } - - const pid_t pid = fork(); - if (pid == -1) { - return RunStatus::FORK_ERROR; - } - - // replace child with memfd elf file - if (pid == 0) { - fexecve(memfd, const_cast(argv), const_cast(envp)); - - // if fexecve fails we need to kill ourselves - *flag = 1; - exit(1); - } - - int status = 0; - - // wait for child and get status code - if (waitpid(pid, &status, 0) == -1) { - return RunStatus::WAIT_ERROR; - } - - if (*flag) { - return RunStatus::EXEC_ERROR; - } - - munmap(flag, sizeof(int)); - - // obtain return code from child status - return WEXITSTATUS(status); + return run_file_image(image.data(), image.size(), argv, envp); } } diff --git a/src/asmio/elf/object.hpp b/src/asmio/elf/object.hpp index 45e4a37..8b8c33b 100644 --- a/src/asmio/elf/object.hpp +++ b/src/asmio/elf/object.hpp @@ -7,28 +7,24 @@ namespace asmio { - template - constexpr static auto supply = [] noexcept { return V; }; - enum struct RunStatus { - SUCCESS, ///< elf file was executed - ARGS_ERROR, ///< the given arguments are invalid - MEMFD_ERROR, ///< memfd failed - MMAP_ERROR, ///< mmap failed - SEAL_ERROR, ///< fcntl failed - STAT_ERROR, ///< fstat failed - FORK_ERROR, ///< fork failed - EXEC_ERROR, ///< file not executable - WAIT_ERROR, ///< waitpid failed + SUCCESS, + ERROR, }; struct RunResult { - RunResult(RunStatus type) - : type(type), status(0) { + constexpr RunResult(RunStatus type, int status) + : type(type), status(status) { + } + + /// Create result with a specific error code + constexpr static RunResult error(int error_status) { + return {RunStatus::ERROR, error_status}; } - RunResult(int status) - : type(RunStatus::SUCCESS), status(status) { + /// Create a result with a specific return code + constexpr static RunResult success(int return_status) { + return {RunStatus::SUCCESS, return_status}; } const RunStatus type; diff --git a/src/asmio/external.hpp b/src/asmio/external.hpp index e97ce67..17a895d 100644 --- a/src/asmio/external.hpp +++ b/src/asmio/external.hpp @@ -26,17 +26,6 @@ #include #include -// systems -#ifdef __linux__ -# include -# include -# include -# include -# include -#else -# error "Non-linux platforms not yet suported!" -#endif - #define NOT(expr) (!(expr)) // architectures diff --git a/src/asmio/program/executable.cpp b/src/asmio/program/executable.cpp index a1c317b..711ad19 100644 --- a/src/asmio/program/executable.cpp +++ b/src/asmio/program/executable.cpp @@ -1,6 +1,8 @@ #include "executable.hpp" +#include + namespace asmio { /* @@ -9,13 +11,13 @@ namespace asmio { ExecutableBuffer::ExecutableBuffer(size_t total) { - const size_t page = getpagesize(); + const size_t page = page_size(); // this value should already be page aligned, but let's check anyway length = util::align_up(total, page); // create a basic memory map, after this we will set the correct flags for each segment - buffer = (uint8_t*) mmap(nullptr, length, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + buffer = (uint8_t*) allocate_pages(length, MemoryFlag::W); if (buffer == nullptr) { throw std::runtime_error {"Failed to allocate memory map!"}; @@ -32,13 +34,13 @@ namespace asmio { ExecutableBuffer::ExecutableBuffer(const ExecutableBuffer& other) { labels = other.labels; - buffer = (uint8_t*) mmap(nullptr, other.length, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + buffer = (uint8_t*) allocate_pages(other.length, MemoryFlag::W); length = other.length; } ExecutableBuffer::~ExecutableBuffer() { if (buffer != nullptr) { - munmap(buffer, length); + free_pages(buffer, length); } buffer = nullptr; @@ -71,7 +73,7 @@ namespace asmio { memcpy(data, segment.buffer.data(), bytes); memset(data + bytes, segment.padder, segment.tail); - mprotect(data, segment.size(), segment.flags.to_mprotect()); + protect_pages(data, segment.size(), segment.flags); } // copy the label map @@ -97,10 +99,10 @@ namespace asmio { ExecutableBuffer to_executable(SegmentedBuffer& segmented) { - const size_t page = getpagesize(); + const size_t page = page_size(); segmented.align(page); - // after alignment we know how big the buffer needs to be + // after alignment, we know how big the buffer needs to be ExecutableBuffer buffer {segmented.total()}; // now that we have a buffer allocated we can link diff --git a/src/asmio/program/label.hpp b/src/asmio/program/label.hpp index f8c1b70..0ca4402 100644 --- a/src/asmio/program/label.hpp +++ b/src/asmio/program/label.hpp @@ -62,8 +62,9 @@ namespace asmio { : id(0), allocated(false), length(0), hash(0) { } - constexpr Label(nullptr_t) + constexpr Label(std::nullptr_t ignored) : Label() { + (void) ignored; } constexpr Label(Label&& label) noexcept diff --git a/src/asmio/program/memory.cpp b/src/asmio/program/memory.cpp index ac0abf3..ae201bd 100644 --- a/src/asmio/program/memory.cpp +++ b/src/asmio/program/memory.cpp @@ -11,9 +11,9 @@ namespace asmio { int MemoryFlags::to_mprotect() const { int protect = 0; - if (r) protect |= PROT_READ; - if (w) protect |= PROT_WRITE; - if (x) protect |= PROT_EXEC; + if (r) protect |= 0x1; // PROT_READ + if (w) protect |= 0x2; // PROT_WRITE + if (x) protect |= 0x4; // PROT_EXEC return protect; } diff --git a/src/asmio/util/codecs.cpp b/src/asmio/util/codecs.cpp index 9f4d0fe..9ce82d2 100644 --- a/src/asmio/util/codecs.cpp +++ b/src/asmio/util/codecs.cpp @@ -27,7 +27,7 @@ namespace asmio { void SignedLeb128::encode(ChunkBuffer& buffer, int64_t signed_value) { auto value = std::bit_cast(signed_value); - const auto minus_one = std::bit_cast(-1L); + constexpr auto minus_one = static_cast(-1L); const bool negative = signed_value < 0; bool next = true; diff --git a/src/asmio/util/platform.cpp b/src/asmio/util/platform.cpp new file mode 100644 index 0000000..eddb301 --- /dev/null +++ b/src/asmio/util/platform.cpp @@ -0,0 +1,140 @@ +#include "platform.hpp" +#include + +#ifdef __linux__ + +#include +#include +#include +#include +#include + +namespace asmio { + + uint32_t page_size() { + return getpagesize(); + } + + void* allocate_pages(uint64_t bytes, MemoryFlags initial) { + return mmap(nullptr, bytes, initial.to_mprotect(), MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + } + + void protect_pages(void* page, uint64_t bytes, MemoryFlags flags) { + mprotect(page, bytes, flags.to_mprotect()); + } + + void free_pages(void* page, uint64_t bytes) { + munmap(page, bytes); + } + + RunResult run_file_image(const void* image, size_t bytes, const char** argv, const char** envp) { + // verify arguments + if (argv == nullptr || envp == nullptr) { + return RunResult::error(1); + } + + // create in-memory file descriptor + const int memfd = memfd_create("buffer", MFD_ALLOW_SEALING | MFD_CLOEXEC); + if (memfd == -1) { + return RunResult::error(2); + } + + int* flag = (int*) mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (flag == nullptr) { + return RunResult::error(3); + } + + // copy buffer into memfd + write(memfd, image, bytes); + + // we use this to check if the child really run or did execve just fail + *flag = 0; + + // add seals to memfd + if (fcntl(memfd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL) != 0) { + return RunResult::error(4); + } + + const pid_t pid = fork(); + if (pid == -1) { + return RunResult::error(5); + } + + // replace child with memfd elf file + if (pid == 0) { + fexecve(memfd, const_cast(argv), const_cast(envp)); + + // if fexecve fails we need to kill ourselves + *flag = 1; + exit(1); + } + + int status = 0; + + // wait for child and get status code + if (waitpid(pid, &status, 0) == -1) { + return RunResult::error(6); + } + + if (*flag) { + return RunResult::error(7); + } + + free_pages(flag, sizeof(int)); + + // obtain return code from child status + return RunResult::success(WEXITSTATUS(status)); + } + + std::string call_shell(std::string cmd, const std::string& input) { + + // refers to the stdin/stdout of the child process + // [0] read, [1] write + int pipe_stdin[2]; + int pipe_stdout[2]; + + pipe2(pipe_stdin, 0); + pipe2(pipe_stdout, 0); + + pid_t pid = fork(); + + if (pid == 0) { + dup2(pipe_stdin[0], STDIN_FILENO); + dup2(pipe_stdout[1], STDOUT_FILENO); + dup2(pipe_stdout[1], STDERR_FILENO); + + // close the unused end + close(pipe_stdin[1]); + close(pipe_stdout[0]); + + execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); + exit(1); + } + + // close the unused end + close(pipe_stdin[0]); + close(pipe_stdout[1]); + + if (!input.empty()) { + write(pipe_stdin[1], input.data(), input.size()); + } + + close(pipe_stdin[1]); + + std::string out; + char buf[256]; + ssize_t n; + + while ((n = read(pipe_stdout[0], buf, sizeof(buf))) > 0) { + out.append(buf, n); + } + + close(pipe_stdout[0]); + waitpid(pid, nullptr, 0); + return out; + } + +} + +#endif + diff --git a/src/asmio/util/platform.hpp b/src/asmio/util/platform.hpp new file mode 100644 index 0000000..4f2f4ff --- /dev/null +++ b/src/asmio/util/platform.hpp @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace asmio { + + struct RunResult; + + /// Get size of one page in bytes + uint32_t page_size(); + + /// Allocate memory in pages, in a way that it can be used by protect_pages() + void* allocate_pages(uint64_t bytes, MemoryFlags initial); + + /// Change permissions assigned to memory pages + void protect_pages(void* page, uint64_t bytes, MemoryFlags flags); + + /// Free memory allocated with allocate_pages() + void free_pages(void* page, uint64_t bytes); + + /// Run executable file image + RunResult run_file_image(const void* image, size_t bytes, const char** argv, const char** envp); + + /// Invoke the given command, run it with the given STDIN, and return STDOUT + std::string call_shell(std::string cmd, const std::string& input = ""); + +} diff --git a/src/asmio/util/tmp.cpp b/src/asmio/util/tmp.cpp index 2b21fc0..16a4818 100644 --- a/src/asmio/util/tmp.cpp +++ b/src/asmio/util/tmp.cpp @@ -29,7 +29,7 @@ namespace asmio::util { } std::string TempFile::path() const { - return m_path; + return m_path.string(); } } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index bb29150..3c9938d 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -3,7 +3,6 @@ #define VSTL_PRINT_SKIP_REASON true #define VSTL_SUBMODULE true -#include "vstl.hpp" #include #include #include @@ -11,6 +10,7 @@ #include #include "test.hpp" +#include "vstl.hpp" namespace test { diff --git a/test/test.hpp b/test/test.hpp index 775648f..6928902 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -1,59 +1,10 @@ #pragma once #include -#include -#include +#include namespace test { - inline std::string call_shell(std::string cmd, const std::string& input = "") { - - // refers to the stdin/stdout of the child process - // [0] read, [1] write - int pipe_stdin[2]; - int pipe_stdout[2]; - - pipe2(pipe_stdin, 0); - pipe2(pipe_stdout, 0); - - pid_t pid = fork(); - - if (pid == 0) { - dup2(pipe_stdin[0], STDIN_FILENO); - dup2(pipe_stdout[1], STDOUT_FILENO); - dup2(pipe_stdout[1], STDERR_FILENO); - - // close the unused end - close(pipe_stdin[1]); - close(pipe_stdout[0]); - - execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); - exit(1); - } - - // close the unused end - close(pipe_stdin[0]); - close(pipe_stdout[1]); - - if (!input.empty()) { - write(pipe_stdin[1], input.data(), input.size()); - } - - close(pipe_stdin[1]); - - std::string out; - char buf[256]; - ssize_t n; - - while ((n = read(pipe_stdout[0], buf, sizeof(buf))) > 0) { - out.append(buf, n); - } - - close(pipe_stdout[0]); - waitpid(pid, nullptr, 0); - return out; - } - inline void dump(asmio::SegmentedBuffer& buffer) { if (buffer.elf_machine == asmio::ElfMachine::NONE) { @@ -63,7 +14,7 @@ namespace test { asmio::ObjectFile baked = asmio::to_elf(buffer, asmio::Label::UNSET).bake(); asmio::util::TempFile temp {baked}; - std::string out = call_shell("objdump --visualize-jumps -wxd -Mintel " + temp.path()); + std::string out = asmio::call_shell("objdump --visualize-jumps -wxd -Mintel " + temp.path()); printf("%s\n", out.c_str()); printf("\nAuto-generated assertions:\n\n"); @@ -98,4 +49,4 @@ namespace test { } -} \ No newline at end of file +} diff --git a/test/x86.cpp b/test/x86.cpp index 0b9f804..cb9e141 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -11,6 +11,7 @@ // private libs #include #include +#include #include #include @@ -2712,6 +2713,7 @@ namespace test { } TEST (writer_exec_int_0x80) { +#if _POSIX_C_SOURCE >= 200112L SegmentedBuffer segmented; BufferWriter writer {segmented}; @@ -2723,6 +2725,9 @@ namespace test { const uint32_t pid = to_executable(segmented).call_u32(); CHECK(pid, getpid()); +#else + SKIP ("_POSIX_C_SOURCE < 200112L") +#endif } TEST (writer_exec_long_back_jmp) { @@ -3774,7 +3779,7 @@ namespace test { writer.put_ret(); ExecutableBuffer buffer = to_executable(segmented); - CHECK(buffer.size(), getpagesize() * 2); // expect there to be two pages + CHECK(buffer.size(), page_size() * 2); // expect there to be two pages CHECK(buffer.call_u32("read"), 42); EXPECT_SIGNAL(SIGSEGV) { From ef659a6cb5b935fcdaf604b47c156f1db6f78306 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:47:57 +0200 Subject: [PATCH 12/44] Everything is broken --- CMakeLists.txt | 5 +++-- src/asmio/util/platform.cpp | 35 +++++++++++++++++++++++++++++++++++ test/aarch64.cpp | 4 ---- test/elf.cpp | 4 ---- test/tasml.cpp | 5 ----- test/unit.cpp | 4 ---- test/x86.cpp | 8 +++----- 7 files changed, 41 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46ea29e..17ef91e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ endif() FetchContent_Declare( vstl GIT_REPOSITORY https://github.com/magistermaks/lib-vstl - GIT_TAG 8e590538f66e21aca2558c3029a275725a8f2be3 + GIT_TAG 59cd364e60cab2d7fc546551756dd3c40e82f59b ) FetchContent_MakeAvailable(vstl) @@ -70,7 +70,8 @@ add_executable(test test/elf.cpp ) target_link_libraries(test PRIVATE asmiov) -target_include_directories(test PRIVATE ${ASMIOV_INCLUDE_DIRS} ${vstl_SOURCE_DIR}) +target_include_directories(test PRIVATE ${ASMIOV_INCLUDE_DIRS}) +target_link_libraries(test PRIVATE vstl) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(asmiov PUBLIC diff --git a/src/asmio/util/platform.cpp b/src/asmio/util/platform.cpp index eddb301..9f356e8 100644 --- a/src/asmio/util/platform.cpp +++ b/src/asmio/util/platform.cpp @@ -138,3 +138,38 @@ namespace asmio { #endif +#ifdef _WIN32 +#include +#include + +namespace asmio { + + uint32_t page_size() { + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwPageSize; + } + + void* allocate_pages(uint64_t bytes, MemoryFlags initial) { + return VirtualAlloc(nullptr, bytes, MEM_COMMIT, PAGE_READWRITE); + } + + void protect_pages(void* page, uint64_t bytes, MemoryFlags flags) { + VirtualProtect(page, bytes, PAGE_EXECUTE, nullptr); + } + + void free_pages(void* page, uint64_t bytes) { + VirtualFree(page, 0, MEM_RELEASE); + } + + RunResult run_file_image(const void* image, size_t bytes, const char** argv, const char** envp) { + throw std::runtime_error {"Operation not supported on this platform!"}; + } + + std::string call_shell(std::string cmd, const std::string& input) { + throw std::runtime_error {"Operation not supported on this platform!"}; + } + +} + +#endif \ No newline at end of file diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 3c9938d..4cb35a9 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -1,7 +1,3 @@ -#define DEBUG_MODE false -#define VSTL_TEST_COUNT 3 -#define VSTL_PRINT_SKIP_REASON true -#define VSTL_SUBMODULE true #include #include diff --git a/test/elf.cpp b/test/elf.cpp index e31f4af..4111056 100644 --- a/test/elf.cpp +++ b/test/elf.cpp @@ -1,8 +1,4 @@ -#define VSTL_TEST_COUNT 3 -#define VSTL_PRINT_SKIP_REASON true -#define VSTL_SUBMODULE true - #include #include #include diff --git a/test/tasml.cpp b/test/tasml.cpp index 90e8ade..b3f9d5c 100644 --- a/test/tasml.cpp +++ b/test/tasml.cpp @@ -1,9 +1,4 @@ -#define DEBUG_MODE false -#define VSTL_TEST_COUNT 3 -#define VSTL_PRINT_SKIP_REASON true -#define VSTL_SUBMODULE true - #include #include #include diff --git a/test/unit.cpp b/test/unit.cpp index e1d5d4b..7eb8846 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -1,7 +1,3 @@ -#define DEBUG_MODE false -#define VSTL_TEST_COUNT 3 -#define VSTL_PRINT_SKIP_REASON true -#define VSTL_SUBMODULE true #include #include diff --git a/test/x86.cpp b/test/x86.cpp index cb9e141..ff1813e 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -1,9 +1,4 @@ -#define DEBUG_MODE false -#define VSTL_TEST_COUNT 3 -#define VSTL_PRINT_SKIP_REASON true -#define VSTL_PRINT_MODULES true - #include "vstl.hpp" #include #include @@ -17,6 +12,9 @@ #include "test.hpp" +VCONF(repeats, 3); +VCONF(print_modules, true); + namespace test { using namespace asmio; From 97398965c17fc25b0a604fc73f1794809255c24a Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:34:06 +0200 Subject: [PATCH 13/44] Add AArch64 to Github CI --- .github/workflows/ci.yml | 23 +++++++++++++++++++++-- CMakeLists.txt | 2 +- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ebd279..cad6681 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,10 +2,10 @@ name: ci.yml on: push: - branches: - - master + branches: ['master', 'next'] pull_request: types: [ opened, synchronize, reopened ] + branches: ['master', 'next'] jobs: linux-x86: @@ -26,3 +26,22 @@ jobs: - name: Test x86 run: cd build && ./test + + linux-aarch64: + runs-on: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + + - name: Dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake g++ + + - name: Configure + run: cmake . -B build + + - name: Build + run: cmake --build build + + - name: Test AArch64 + run: cd build && ./test diff --git a/CMakeLists.txt b/CMakeLists.txt index 17ef91e..ca737b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ endif() FetchContent_Declare( vstl GIT_REPOSITORY https://github.com/magistermaks/lib-vstl - GIT_TAG 59cd364e60cab2d7fc546551756dd3c40e82f59b + GIT_TAG c7f1069ea12fbe8087067841c491940fc3f6dc7f ) FetchContent_MakeAvailable(vstl) From 3b5489665c80810e97b164529b5877c4bcdcf5ae Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:59:41 +0200 Subject: [PATCH 14/44] Add Windows to CI --- .github/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cad6681..ceb9b51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: sudo apt-get install -y cmake g++ - name: Configure - run: cmake . -B build + run: cmake -B build - name: Build run: cmake --build build @@ -38,10 +38,38 @@ jobs: sudo apt-get install -y cmake g++ - name: Configure - run: cmake . -B build + run: cmake -B build - name: Build run: cmake --build build - name: Test AArch64 run: cd build && ./test + + windows-x86: + runs-on: windows-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + + - name: Dependencies + shell: msys2 {0} + run: pacman -S git mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-python --noconfirm + + - name: Configure + shell: msys2 {0} + run: cmake -B build -G Ninja -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ + + - name: Build + shell: msys2 {0} + run: cmake --build build + + - name: Test + shell: msys2 {0} + run: cd build && ./test.exe \ No newline at end of file From 870c6a3cb9ff45ef494f3ce7d79a035a2fd971a6 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Fri, 17 Apr 2026 07:05:00 +0200 Subject: [PATCH 15/44] Windows bugfixes --- src/asmio/util.hpp | 2 +- src/asmio/util/codecs.cpp | 12 ++++++------ src/asmio/util/platform.cpp | 5 +++-- test/x86.cpp | 4 ++++ 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/asmio/util.hpp b/src/asmio/util.hpp index bbf2978..8c51682 100644 --- a/src/asmio/util.hpp +++ b/src/asmio/util.hpp @@ -254,7 +254,7 @@ namespace asmio::util { return std::numeric_limits::max(); } - return (1UL << count) - 1UL; + return (T(1) << count) - T(1); } /** diff --git a/src/asmio/util/codecs.cpp b/src/asmio/util/codecs.cpp index 9ce82d2..92e7d10 100644 --- a/src/asmio/util/codecs.cpp +++ b/src/asmio/util/codecs.cpp @@ -35,14 +35,14 @@ namespace asmio { uint8_t byte = value & 0x7F; value >>= 7; - // this is only neccecary if the implementation of >>= + // this is only necessary if the implementation of >>= // uses a logical shift, in practice most C++ implementations - // would (hopefully) use a arythmetic shift when the shifted value is - // of a signed type, however, this behaviour is not well defined and - // relies on undefined behaviour. To avoit it, we explicitly use a - // non-singed type here and implements arythmetic shift ourselves. + // would (hopefully) use an arithmetic shift when the shifted value is + // of a signed type, however, this behavior is not well-defined and + // relies on undefined behavior. To avoid it, we explicitly use a + // non-singed type here and implements arithmetic shift ourselves. if (negative) { - value |= ~0UL << (sizeof(signed_value) * 8 - 7); // sign extend + value |= ~uint64_t(0) << (sizeof(signed_value) * 8 - 7); // sign extend } // sign bit of byte is second high-order bit diff --git a/src/asmio/util/platform.cpp b/src/asmio/util/platform.cpp index 9f356e8..6e90f6d 100644 --- a/src/asmio/util/platform.cpp +++ b/src/asmio/util/platform.cpp @@ -151,11 +151,12 @@ namespace asmio { } void* allocate_pages(uint64_t bytes, MemoryFlags initial) { - return VirtualAlloc(nullptr, bytes, MEM_COMMIT, PAGE_READWRITE); + return VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); } void protect_pages(void* page, uint64_t bytes, MemoryFlags flags) { - VirtualProtect(page, bytes, PAGE_EXECUTE, nullptr); + DWORD unused; + VirtualProtect(page, bytes, PAGE_EXECUTE_READWRITE, &unused); } void free_pages(void* page, uint64_t bytes) { diff --git a/test/x86.cpp b/test/x86.cpp index ff1813e..b0f4374 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -4154,6 +4154,10 @@ namespace test { TEST (exec_cpuid) { +#ifdef _WIN32 + FAIL("This test bricks VSTL on Windows"); +#endif + SegmentedBuffer buffer; BufferWriter writer {buffer}; From 97b9176cb397971415d139cb637b21331de36f27 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Fri, 17 Apr 2026 19:44:06 +0200 Subject: [PATCH 16/44] Further Windows work --- src/asmio/program/executable.hpp | 9 +++++++++ src/asmio/program/memory.cpp | 13 +++++++++++++ src/asmio/program/memory.hpp | 3 +++ src/asmio/util/platform.cpp | 8 ++++---- test/tasml.cpp | 5 ++++- test/x86.cpp | 33 ++++++++++++++++---------------- 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/asmio/program/executable.hpp b/src/asmio/program/executable.hpp index 4a1ee01..49741df 100644 --- a/src/asmio/program/executable.hpp +++ b/src/asmio/program/executable.hpp @@ -13,6 +13,15 @@ "mm0","mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm6", \ "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)" +#if ARCH_X86 +# ifdef _WIN32 +# define SCALL_REG RCX +# endif +# ifdef __linux__ +# define SCALL_REG RDI +# endif +#endif + namespace asmio { template diff --git a/src/asmio/program/memory.cpp b/src/asmio/program/memory.cpp index ae201bd..18ad360 100644 --- a/src/asmio/program/memory.cpp +++ b/src/asmio/program/memory.cpp @@ -17,6 +17,19 @@ namespace asmio { return protect; } + uint32_t MemoryFlags::to_win32() const { + // this gotta be the dumbest way to implement memory permissions... + if (x && !r && !w) return 0x10; // PAGE_EXECUTE + if (x && r && !w) return 0x20; // PAGE_EXECUTE_READ + if (x && w) return 0x40; // PAGE_EXECUTE_READWRITE (write implies read) + if (!x && !r && !w) return 0x01; // PAGE_NOACCESS + if (!x && r && !w) return 0x02; // PAGE_READONLY + if (!x && w) return 0x04; // PAGE_READWRITE (write implies read) + + // unreachable + return 0; + } + uint32_t MemoryFlags::to_elf_segment() const { uint32_t flags = 0; if (r) flags |= ElfSegmentFlags::R; diff --git a/src/asmio/program/memory.hpp b/src/asmio/program/memory.hpp index ff667e4..ebc6ca8 100644 --- a/src/asmio/program/memory.hpp +++ b/src/asmio/program/memory.hpp @@ -34,6 +34,9 @@ namespace asmio { /// Convert to mprotect() flags int to_mprotect() const; + /// Convert to the Windows Memory Protection Constants + uint32_t to_win32() const; + /// Convert to ElfSegmentFlags flags uint32_t to_elf_segment() const; diff --git a/src/asmio/util/platform.cpp b/src/asmio/util/platform.cpp index 6e90f6d..30834e6 100644 --- a/src/asmio/util/platform.cpp +++ b/src/asmio/util/platform.cpp @@ -151,12 +151,12 @@ namespace asmio { } void* allocate_pages(uint64_t bytes, MemoryFlags initial) { - return VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + return VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, initial.to_win32()); } void protect_pages(void* page, uint64_t bytes, MemoryFlags flags) { DWORD unused; - VirtualProtect(page, bytes, PAGE_EXECUTE_READWRITE, &unused); + VirtualProtect(page, bytes, flags.to_win32(), &unused); } void free_pages(void* page, uint64_t bytes) { @@ -164,11 +164,11 @@ namespace asmio { } RunResult run_file_image(const void* image, size_t bytes, const char** argv, const char** envp) { - throw std::runtime_error {"Operation not supported on this platform!"}; + throw std::runtime_error {"Operation run_file_image() not supported on this platform!"}; } std::string call_shell(std::string cmd, const std::string& input) { - throw std::runtime_error {"Operation not supported on this platform!"}; + throw std::runtime_error {"Operation call_shell() not supported on this platform!"}; } } diff --git a/test/tasml.cpp b/test/tasml.cpp index b3f9d5c..47ef4cf 100644 --- a/test/tasml.cpp +++ b/test/tasml.cpp @@ -194,10 +194,13 @@ namespace test { util::TempFile embedded {".txt"}; embedded.write(embed); + // escape Windows path separators, replaces '\' with '\\' + auto escaped_path = std::regex_replace(embedded.path(),std::regex("\\\\"),"\\\\"); + std::string code = R"( section r export private begin: - embed ")" + embedded.path() + R"(" + embed ")" + escaped_path + R"(" export private end: )"; diff --git a/test/x86.cpp b/test/x86.cpp index b0f4374..9d7292e 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -3903,8 +3903,8 @@ namespace test { writer.label("simple_add"); writer.put_push(RBX); - writer.put_mov(RBX, ref(RAX)); - writer.put_add(RBX, ref(RAX + 8)); + writer.put_mov(RBX, ref(SCALL_REG)); + writer.put_add(RBX, ref(SCALL_REG + 8)); writer.put_mov(RAX, RBX); writer.put_pop(RBX); writer.put_ret(); @@ -3922,7 +3922,7 @@ namespace test { BufferWriter writer {segmented}; writer.label("main"); - writer.put_mov(RCX, ref(RAX)); + writer.put_mov(RCX, ref(SCALL_REG)); writer.put_mov(ref(RCX), 42); writer.put_ret(); @@ -3996,8 +3996,8 @@ namespace test { BufferWriter writer {segmented}; writer.label("add"); - writer.put_mov(RAX, ref(RDI + 0)); - writer.put_add(RAX, ref(RDI + 8)); + writer.put_mov(RAX, ref(SCALL_REG + 0)); + writer.put_add(RAX, ref(SCALL_REG + 8)); writer.put_ret(); // check if no segfault occures @@ -4154,10 +4154,6 @@ namespace test { TEST (exec_cpuid) { -#ifdef _WIN32 - FAIL("This test bricks VSTL on Windows"); -#endif - SegmentedBuffer buffer; BufferWriter writer {buffer}; @@ -4169,15 +4165,20 @@ namespace test { }; writer.label("cpuid"); + writer.put_push(RBX); + writer.put_push(RDI); + writer.put_mov(RDI, SCALL_REG); writer.put_mov(RAX, ref(RDI)); // page writer.put_cpuid(); - writer.put_mov(RDI, ref(RDI + 8)); // result* - - writer.put_mov(ref(RDI), EAX); // result->eax - writer.put_mov(ref(RDI + 4), EBX); // result->ebx - writer.put_add(RDI, 8); - writer.put_mov(ref(RDI), ECX); // result->ecx - writer.put_mov(ref(RDI + 4), EDX); // result->edx + writer.put_mov(R8, ref(RDI + 8)); // result* + + writer.put_mov(ref(R8), EAX); // result->eax + writer.put_mov(ref(R8 + 4), EBX); // result->ebx + writer.put_add(R8, 8); + writer.put_mov(ref(R8), ECX); // result->ecx + writer.put_mov(ref(R8 + 4), EDX); // result->edx + writer.put_pop(RDI); + writer.put_pop(RBX); writer.put_ret(); Result result {}; From f9f7443caa807441abe967c9899f0f43b6bd9917 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Sat, 18 Apr 2026 12:00:04 +0200 Subject: [PATCH 17/44] Windows ELF fixes --- src/asmio/elf/model.cpp | 4 +- src/asmio/elf/object.cpp | 2 +- src/asmio/elf/symbol.hpp | 4 +- src/asmio/util/platform.cpp | 108 +++++++++++++++++++++++++++++++++++- src/asmio/util/tmp.cpp | 2 +- src/asmio/util/tmp.hpp | 2 +- test/elf.cpp | 11 +++- test/unit.cpp | 8 +++ test/x86.cpp | 8 +-- 9 files changed, 134 insertions(+), 15 deletions(-) diff --git a/src/asmio/elf/model.cpp b/src/asmio/elf/model.cpp index 6ef4019..da6d977 100644 --- a/src/asmio/elf/model.cpp +++ b/src/asmio/elf/model.cpp @@ -105,7 +105,7 @@ namespace asmio { strings.type = ElfSectionType::STRTAB; strings.name = section_strings.append(".strtab"); strings.alignment = 1; - strings.buffer = section_buffer_pool->chunk(); + strings.buffer = section_buffer_pool->chunk(strings.alignment); symbol_string_table = define_section(strings); Section symtab {}; @@ -115,7 +115,7 @@ namespace asmio { symtab.alignment = 8; symtab.link = LinkInfo(symbol_string_table); symtab.info = LinkInfo(); // This is set during baking, when we know how many local symbols are there - symtab.buffer = section_buffer_pool->chunk(); + symtab.buffer = section_buffer_pool->chunk(symtab.alignment); symbol_table = define_section(symtab); Symbol symbol {}; diff --git a/src/asmio/elf/object.cpp b/src/asmio/elf/object.cpp index f6952a2..58c4c3d 100644 --- a/src/asmio/elf/object.cpp +++ b/src/asmio/elf/object.cpp @@ -35,7 +35,7 @@ namespace asmio { // if file creation fails return false try { - std::ofstream output {path}; + std::fstream output {path, std::ios::out | std::ios::trunc | std::ios::binary}; if (output.bad()) { return false; diff --git a/src/asmio/elf/symbol.hpp b/src/asmio/elf/symbol.hpp index ce9f548..32c1c54 100644 --- a/src/asmio/elf/symbol.hpp +++ b/src/asmio/elf/symbol.hpp @@ -33,4 +33,6 @@ struct PACKED ElfSymbol { uint16_t shndx = 0; ///< Symbol's section uint64_t value = 0; ///< Value (e.g. address, offset) of a symbol uint64_t ssize = 0; ///< Size of the symbol, or zero if not applicable -}; \ No newline at end of file +}; + +static_assert(sizeof(ElfSymbol) == 3*8); \ No newline at end of file diff --git a/src/asmio/util/platform.cpp b/src/asmio/util/platform.cpp index 30834e6..6370ccd 100644 --- a/src/asmio/util/platform.cpp +++ b/src/asmio/util/platform.cpp @@ -139,8 +139,7 @@ namespace asmio { #endif #ifdef _WIN32 -#include -#include +#include namespace asmio { @@ -168,7 +167,110 @@ namespace asmio { } std::string call_shell(std::string cmd, const std::string& input) { - throw std::runtime_error {"Operation call_shell() not supported on this platform!"}; + + // https://learn.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output + // https://www.mirabulus.com/it/blog/2021/05/16/hidden-issues-with-inheritable-handles-in-windows + + SECURITY_ATTRIBUTES security_attr; + security_attr.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attr.bInheritHandle = TRUE; + security_attr.lpSecurityDescriptor = nullptr; + + HANDLE child_stdin_read = nullptr; + HANDLE child_stdin_write = nullptr; + HANDLE child_stdout_read = nullptr; + HANDLE child_stdout_write = nullptr; + + CreatePipe(&child_stdout_read, &child_stdout_write, &security_attr, 0); + SetHandleInformation(child_stdout_read, HANDLE_FLAG_INHERIT, 0); + + CreatePipe(&child_stdin_read, &child_stdin_write, &security_attr, 0); + SetHandleInformation(child_stdin_write, HANDLE_FLAG_INHERIT, 0); + + PROCESS_INFORMATION process_info; + STARTUPINFO startup_info; + + ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&startup_info, sizeof(STARTUPINFO)); + + startup_info.cb = sizeof(STARTUPINFO); + startup_info.hStdError = child_stdout_write; + startup_info.hStdOutput = child_stdout_write; + startup_info.hStdInput = child_stdin_read; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + + std::string command = "cmd.exe /C " + cmd; + + if (!CreateProcess( + nullptr, + (TCHAR*) command.c_str(), + nullptr, + nullptr, + true, // inherit handles + CREATE_SUSPENDED, + nullptr, // use parent's environment + nullptr, // use parent's current directory + &startup_info, + &process_info + )) { + throw std::runtime_error {"Failed to create process '" + cmd + "'!"}; + } + + SetHandleInformation(child_stdout_write, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(child_stdin_read, HANDLE_FLAG_INHERIT, 0); + + // close handles used by the child process + CloseHandle(child_stdout_write); + CloseHandle(child_stdin_read); + + ResumeThread(process_info.hThread); + + const char* data = input.data(); + DWORD bytes = input.size(); + DWORD written = 0; + + while (bytes > 0) { + WriteFile(child_stdin_write, data, bytes, &written, nullptr); + bytes -= written; + } + + CloseHandle(child_stdin_write); + + DWORD exit_code = 1; + std::string output; + + WaitForSingleObject(process_info.hProcess, INFINITE); + GetExitCodeProcess(process_info.hProcess, &exit_code); + + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + + constexpr DWORD size = 256; + char buffer[size]; + DWORD read = 0; + + while (true) { + bool success = ReadFile(child_stdout_read, buffer, size, &read, nullptr); + + if (!success || (read == 0)) { + break; + } + + output.append(buffer, buffer + read); + } + + CloseHandle(child_stdout_read); + + std::string normalized; + normalized.reserve(output.size()); + + for (char c : output) { + if (c != '\r') { + normalized.push_back(c); + } + } + + return normalized; } } diff --git a/src/asmio/util/tmp.cpp b/src/asmio/util/tmp.cpp index 16a4818..fe7b98c 100644 --- a/src/asmio/util/tmp.cpp +++ b/src/asmio/util/tmp.cpp @@ -25,7 +25,7 @@ namespace asmio::util { } void TempFile::dump() const { - printf("Using temporary file: \"%s\"\n", m_path.c_str()); + printf("Using temporary file: \"%s\"\n", (const char*) m_path.u8string().c_str()); } std::string TempFile::path() const { diff --git a/src/asmio/util/tmp.hpp b/src/asmio/util/tmp.hpp index 20dd584..a848784 100644 --- a/src/asmio/util/tmp.hpp +++ b/src/asmio/util/tmp.hpp @@ -23,7 +23,7 @@ namespace asmio::util { } void write(const std::string& content) { - std::ofstream out(path()); + std::fstream out(path(), std::ios::out | std::ios::trunc | std::ios::binary); out << content; out.close(); } diff --git a/test/elf.cpp b/test/elf.cpp index 4111056..de9cedd 100644 --- a/test/elf.cpp +++ b/test/elf.cpp @@ -28,6 +28,7 @@ namespace test { ASSERT(!result.contains("Warning")); ASSERT(!result.contains("Error")); + ASSERT(!result.empty()); ASSERT(result.contains("ELF64")); ASSERT(result.contains("Advanced Micro Devices X86-64")); @@ -83,6 +84,7 @@ namespace test { ASSERT(!result.contains("Warning")); ASSERT(!result.contains("Error")); + ASSERT(!result.empty()); ASSERT(result.contains("0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND")); ASSERT(result.contains("1: 00000000000000db 1 OBJECT LOCAL DEFAULT 2 arda")); @@ -187,6 +189,7 @@ namespace test { ASSERT(!result.contains("Warning")); ASSERT(!result.contains("Error")); + ASSERT(!result.empty()); ASSERT(result.contains("[ 2] .rwx PROGBITS")); ASSERT(result.contains("[ 3] .rodata PROGBITS")); @@ -239,8 +242,8 @@ namespace test { // link with our object util::TempFile exec {".out"}; - std::string gcc_output = call_shell("gcc -z noexecstack -o " + exec.path() + " " + object.path() + " " + main_src.path() ); - CHECK(gcc_output, ""); + std::string gcc_output = call_shell("gcc -o " + exec.path() + " " + object.path() + " " + main_src.path()); + ASSERT(!gcc_output.contains("error")); std::string exe_output = call_shell(exec.path()); CHECK(exe_output, "hello!"); @@ -310,6 +313,7 @@ namespace test { ASSERT(!result.contains("Warning")); ASSERT(!result.contains("Error")); + ASSERT(!result.empty()); auto line_block = util::split_string(result, "Line Number Statements:").at(1); auto lines = util::normalize_strings(util::split_string(line_block)); @@ -359,6 +363,7 @@ namespace test { ASSERT(!result.contains("Warning")); ASSERT(!result.contains("Error")); + ASSERT(!result.empty()); auto line_block = util::split_string(result, "Line Number Statements:").at(1); auto lines = util::normalize_strings(util::split_string(line_block)); @@ -414,6 +419,7 @@ namespace test { ASSERT(!result.contains("Warning")); ASSERT(!result.contains("Error")); + ASSERT(!result.empty()); ASSERT(result.contains("1 DW_TAG_base_type [no children]")); ASSERT(result.contains("2 DW_TAG_pointer_type [no children]")); @@ -460,6 +466,7 @@ namespace test { ASSERT(!result.contains("Warning")); ASSERT(!result.contains("Error")); + ASSERT(!result.empty()); ASSERT(result.contains("Abbrev Number: 1 (DW_TAG_compile_unit)")); ASSERT(result.contains("Abbrev Number: 2 (DW_TAG_base_type)")); diff --git a/test/unit.cpp b/test/unit.cpp index 7eb8846..f3bf383 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "vstl.hpp" @@ -480,4 +481,11 @@ namespace test { }; + TEST (util_call_shell) { + + std::string output = call_shell("echo hello", ""); + ASSERT(output.contains("hello")); + + }; + } \ No newline at end of file diff --git a/test/x86.cpp b/test/x86.cpp index 9d7292e..f03c29d 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -4048,8 +4048,8 @@ namespace test { // link with our object util::TempFile exec {".out"}; - std::string gcc_output = call_shell("gcc -z noexecstack -o " + exec.path() + " " + object.path() + " " + main_src.path() ); - CHECK(gcc_output, ""); + std::string gcc_output = call_shell("gcc -o " + exec.path() + " " + object.path() + " " + main_src.path() ); + ASSERT(!gcc_output.contains("error")); std::string exe_output = call_shell(exec.path()); CHECK(exe_output, "42"); @@ -4144,8 +4144,8 @@ namespace test { // link with our object util::TempFile exec {".out"}; - std::string gcc_output = call_shell("gcc -Wno-format -z noexecstack -o " + exec.path() + " " + object.path() + " " + main_src.path() ); - CHECK(gcc_output, ""); + std::string gcc_output = call_shell("gcc -Wno-format -o " + exec.path() + " " + object.path() + " " + main_src.path() ); + ASSERT(!gcc_output.contains("error")); std::string exe_output = call_shell(exec.path()); CHECK(exe_output, "HELLO FURRY!"); From e5f85b1dd03d6e5d73000032e74d703f134b2d23 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Sat, 18 Apr 2026 17:30:32 +0200 Subject: [PATCH 18/44] Fix all Windows tests --- src/asmio/util.hpp | 30 ++++++++++++++--- src/asmio/util/platform.cpp | 65 ++++++++++++++++++++++++++++++++++++- src/asmio/util/tmp.hpp | 6 ++++ src/asmio/x86/module.cpp | 2 +- test/elf.cpp | 12 +++++-- test/unit.cpp | 13 ++++++++ test/x86.cpp | 21 +++++++++++- 7 files changed, 140 insertions(+), 9 deletions(-) diff --git a/src/asmio/util.hpp b/src/asmio/util.hpp index 8c51682..0bec413 100644 --- a/src/asmio/util.hpp +++ b/src/asmio/util.hpp @@ -59,10 +59,32 @@ namespace asmio::util { // https://stackoverflow.com/a/6500499 inline std::string trim(const std::string& str) { - std::string copy = str; - copy.erase(copy.find_last_not_of(' ') + 1); // Suffixing spaces - copy.erase(0, copy.find_first_not_of(' ')); // Prefixing spaces - return copy; + int prefix = 0; // will point to the first non-space char + int suffix = str.length() - 1; + + while (prefix < static_cast(str.length())) { + if (!std::isspace(str[prefix])) { + break; + } + + prefix ++; + } + + while (suffix > 0) { + if (!std::isspace(str[suffix])) { + break; + } + + suffix --; + } + + int count = suffix - prefix + 1; + + if (count < 0) { + count = 0; + } + + return str.substr(prefix, count); } // https://stackoverflow.com/a/46931770 diff --git a/src/asmio/util/platform.cpp b/src/asmio/util/platform.cpp index 6370ccd..42094c1 100644 --- a/src/asmio/util/platform.cpp +++ b/src/asmio/util/platform.cpp @@ -1,6 +1,8 @@ #include "platform.hpp" #include +#include "tmp.hpp" + #ifdef __linux__ #include @@ -163,7 +165,68 @@ namespace asmio { } RunResult run_file_image(const void* image, size_t bytes, const char** argv, const char** envp) { - throw std::runtime_error {"Operation run_file_image() not supported on this platform!"}; + + // TODO implement, an actual, in-memory CreateProcess() + // this is done in a non-perfect way, by first copying the image to a file + // there is a way to do this correctly, but it is convoluted + // https://groups.google.com/g/comp.os.ms-windows.programmer.win32/c/Md3GKPc279A/m/Ax3bYgXhpD8J + // https://web.archive.org/web/20131115160730/http://www.security.org.sg/code/loadexe.html + + // TODO don't ignore environ + (void) envp; + + util::TempFile temp; + temp.write((uint8_t*) image, bytes); + + PROCESS_INFORMATION process_info; + STARTUPINFO startup_info; + + ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&startup_info, sizeof(STARTUPINFO)); + + startup_info.cb = sizeof(STARTUPINFO); + + std::string command = temp.path() + " "; + + // start at 1 to ignore name + for (int i = 1; true; i++) { + const char* part = argv[i]; + + if (part == nullptr) { + break; + } + + command += part; + } + + if (!CreateProcess( + nullptr, + (TCHAR*) command.c_str(), + nullptr, + nullptr, + true, // inherit handles + 0, + nullptr, // use parent's environment + nullptr, // use parent's current directory + &startup_info, + &process_info + )) { + return RunResult::error(1); + } + + DWORD exit_code = 1; + + if (WaitForSingleObject(process_info.hProcess, 10000) == WAIT_TIMEOUT) { + return RunResult::error(2); + } + + GetExitCodeProcess(process_info.hProcess, &exit_code); + + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + + return RunResult::success(exit_code); + } std::string call_shell(std::string cmd, const std::string& input) { diff --git a/src/asmio/util/tmp.hpp b/src/asmio/util/tmp.hpp index a848784..8a044f3 100644 --- a/src/asmio/util/tmp.hpp +++ b/src/asmio/util/tmp.hpp @@ -28,6 +28,12 @@ namespace asmio::util { out.close(); } + void write(const uint8_t* data, size_t bytes) { + std::fstream out(path(), std::ios::out | std::ios::trunc | std::ios::binary); + out.write((char*) data, bytes); + out.close(); + } + TempFile(const char* extension = ""); ~TempFile(); diff --git a/src/asmio/x86/module.cpp b/src/asmio/x86/module.cpp index 9f197fd..d7de809 100644 --- a/src/asmio/x86/module.cpp +++ b/src/asmio/x86/module.cpp @@ -133,7 +133,7 @@ namespace asmio::x86 { const Token* index = nullptr; const Token* scale = nullptr; const Token* label = nullptr; - long offset = 0; + int64_t offset = 0; // the next token MUST be this, else parse throws const char* expect = nullptr; diff --git a/test/elf.cpp b/test/elf.cpp index de9cedd..3b7899e 100644 --- a/test/elf.cpp +++ b/test/elf.cpp @@ -339,7 +339,11 @@ namespace test { "[0x00000069] Extended opcode 1: End of Sequence", }; - CHECK(lines, expected); + ASSERT(lines.size() >= expected.size()); + + for (size_t i = 0; i < expected.size(); i ++) { + CHECK(lines[i], expected[i]); + } }; @@ -377,7 +381,11 @@ namespace test { "[0x00000042] Extended opcode 1: End of Sequence" }; - CHECK(lines, expected); + ASSERT(lines.size() >= expected.size()); + + for (size_t i = 0; i < expected.size(); i ++) { + CHECK(lines[i], expected[i]); + } }; diff --git a/test/unit.cpp b/test/unit.cpp index f3bf383..a1e9e89 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -488,4 +488,17 @@ namespace test { }; + TEST (util_string_trim) { + + CHECK(util::trim(" abc "), "abc"); + CHECK(util::trim("\tdef\n"), "def"); + CHECK(util::trim("\t \t ghi\r\n"), "ghi"); + + CHECK(util::trim("foo"), "foo"); + CHECK(util::trim(" bar"), "bar"); + CHECK(util::trim(" "), ""); + CHECK(util::trim(""), ""); + + }; + } \ No newline at end of file diff --git a/test/x86.cpp b/test/x86.cpp index f03c29d..0f4f0f7 100644 --- a/test/x86.cpp +++ b/test/x86.cpp @@ -3684,10 +3684,14 @@ namespace test { segmented.elf_machine = ElfMachine::X86_64; ObjectFile file = to_elf(segmented, "_start").bake(); +#ifdef __linux__ RunResult result = file.execute("memfd-elf-1"); CHECK(result.type, RunStatus::SUCCESS); CHECK(result.status, 42); +#else + SKIP("ELF execution not supported in this environment") +#endif } @@ -3718,10 +3722,15 @@ namespace test { segmented.elf_machine = ElfMachine::X86_64; ObjectFile file = to_elf(segmented, "_start").bake(); + +#ifdef __linux__ RunResult result = file.execute("memfd-elf-1"); CHECK(result.type, RunStatus::SUCCESS); CHECK(result.status, 13); +#else + SKIP("ELF execution not supported in this environment") +#endif } @@ -3752,10 +3761,15 @@ namespace test { segmented.elf_machine = ElfMachine::X86_64; ObjectFile file = to_elf(segmented, "_start").bake(); + +#ifdef __linux__ RunResult result = file.execute("memfd-elf-1"); CHECK(result.type, RunStatus::SUCCESS); CHECK(result.status, 20); +#else + SKIP("ELF execution not supported in this environment") +#endif } @@ -4076,10 +4090,15 @@ namespace test { segmented.elf_machine = ElfMachine::X86_64; ObjectFile file = to_elf(segmented, "_start").bake(); + +#ifdef __linux__ RunResult result = file.execute("memfd-elf-1"); CHECK(result.type, RunStatus::SUCCESS); CHECK(result.status, 42); +#else + SKIP("ELF execution not supported in this environment") +#endif }; @@ -4128,7 +4147,7 @@ namespace test { volatile uint64_t test_value = 0; // imported from asmiov - uint64_t update_text(); + void update_text(); int main() { char* a = (char*) &test_value; From 6b42802064b405bf7119ddb529dd5d7d0ecf437f Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 19 Apr 2026 16:08:39 +0200 Subject: [PATCH 19/44] AArch64 CAS instructions --- src/asmio/aarch64/argument/order.hpp | 41 +++++++++++++++++ src/asmio/aarch64/argument/shift.hpp | 2 +- src/asmio/aarch64/argument/sizing.hpp | 16 +++---- src/asmio/aarch64/module.cpp | 13 ++++++ src/asmio/aarch64/writer.hpp | 9 +++- src/asmio/aarch64/writer_basic.cpp | 32 +++++++++++++ test/aarch64.cpp | 65 +++++++++++++++++++++++++++ test/tasml.cpp | 15 +++++++ 8 files changed, 183 insertions(+), 10 deletions(-) create mode 100644 src/asmio/aarch64/argument/order.hpp diff --git a/src/asmio/aarch64/argument/order.hpp b/src/asmio/aarch64/argument/order.hpp new file mode 100644 index 0000000..b5c2652 --- /dev/null +++ b/src/asmio/aarch64/argument/order.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +namespace asmio::arm { + + // Specifies ordering semantics of instructions, + // learn more about ordering semantics: https://davekilian.com/acquire-release.html + enum struct Order : uint8_t { + // L o0 + NONE = 0b00'00000'0, ///< Ensure no ordering of operations + RELEASE = 0b00'00000'1, ///< Ensure that the preceding operations finish before this one starts + ACQUIRE = 0b10'00000'0, ///< Ensure that the following operations wait for this one to finish + ACQUIRE_RELEASE = 0b10'00000'1, ///< Ensure that both previous and preceding operations are executed in order + + // "L" flag is used to mark acquire semantics (marked with suffix "A" in mnemonics), + // and o0 to mark "release" (suffix "L" in mnemonics). How designed it like this?? + }; + + /** + * Check if Order is ACQUIRE or ACQUIRE_RELEASE + */ + constexpr bool is_order_acquire(Order order) { + return static_cast(order) & static_cast(Order::ACQUIRE); + } + + /** + * Check if Order is RELEASE or ACQUIRE_RELEASE + */ + constexpr bool is_order_release(Order order) { + return static_cast(order) & static_cast(Order::RELEASE); + } + + /** + * Convert Order to a bit mask used during instruction encoding + */ + constexpr uint32_t order_to_dword_mask(Order order) { + return static_cast(order) << 15; + } + +} \ No newline at end of file diff --git a/src/asmio/aarch64/argument/shift.hpp b/src/asmio/aarch64/argument/shift.hpp index 592ef71..f664f06 100644 --- a/src/asmio/aarch64/argument/shift.hpp +++ b/src/asmio/aarch64/argument/shift.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../../asmio/external.hpp" +#include enum struct ShiftType : uint8_t { LSL = 0b00, ///< shift left diff --git a/src/asmio/aarch64/argument/sizing.hpp b/src/asmio/aarch64/argument/sizing.hpp index 46deb15..85c12d5 100644 --- a/src/asmio/aarch64/argument/sizing.hpp +++ b/src/asmio/aarch64/argument/sizing.hpp @@ -3,12 +3,12 @@ #include enum struct Sizing : uint8_t { - UB = 0b000, ///< add unsigned byte - UH = 0b001, ///< add unsigned word - UW = 0b010, ///< add unsigned dword - UX = 0b011, ///< add unsinged qword - SB = 0b100, ///< add signed byte - SH = 0b101, ///< add signed word - SW = 0b110, ///< add signed dword - SX = 0b111, ///< add signed qword + UB = 0b000, ///< unsigned byte + UH = 0b001, ///< unsigned word + UW = 0b010, ///< unsigned dword + UX = 0b011, ///< unsinged qword + SB = 0b100, ///< signed byte + SH = 0b101, ///< signed word + SW = 0b110, ///< signed dword + SX = 0b111, ///< signed qword }; \ No newline at end of file diff --git a/src/asmio/aarch64/module.cpp b/src/asmio/aarch64/module.cpp index 93064bb..6afdce2 100644 --- a/src/asmio/aarch64/module.cpp +++ b/src/asmio/aarch64/module.cpp @@ -86,6 +86,19 @@ namespace asmio::arm { throw std::runtime_error {"Invalid argument format, expected shift specifier"}; } + template <> + Order parse_argument(TokenStream stream) { + const Token& token = stream.expect(Token::NAME); + std::string raw = util::to_lower(token.raw); + + if (raw == "a") return Order::ACQUIRE; + if (raw == "r") return Order::RELEASE; + if (raw == "ar") return Order::ACQUIRE_RELEASE; + if (raw == "ra") return Order::ACQUIRE_RELEASE; + + throw std::runtime_error {"Invalid argument format, expected order semantics"}; + } + template <> Condition parse_argument(TokenStream stream) { const Token& token = stream.expect(Token::NAME); diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index f84ba6c..ad010f9 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -5,6 +5,7 @@ #include "argument/registry.hpp" #include "argument/shift.hpp" #include "argument/condition.hpp" +#include "argument/order.hpp" #include "argument/pattern.hpp" namespace asmio::arm { @@ -45,7 +46,7 @@ namespace asmio::arm { protected: static uint8_t pack_shift(uint8_t shift, bool wide); - static uint64_t get_size(Size size); + void assert_register_triplet(Registry a, Registry b, Registry c); /// Encode generic, 16 bit, immediate move, used by MOVN, MOVK, MOVZ @@ -87,6 +88,9 @@ namespace asmio::arm { /// Encode "CSINC/CSEL/CSET/CINC" operation void put_inst_csinc(Condition condition, Registry dst, Registry truthy, Registry falsy, bool increment_truth); + /// Encode "CAS/CAB/CAH" operations + void put_inst_cas(Registry dst, Registry src, Registry cmp, Order order, uint8_t size); + public: void put_inst_add_imm(Registry destination, Registry source, uint16_t imm12, bool lsl_12 = false, bool set_flags = false); @@ -176,6 +180,9 @@ namespace asmio::arm { INST put_bfc(Registry dst, BitPattern pattern); ///< Bitfield Clear INST put_bic(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise Bit Clear INST put_bics(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise Bit Clear and set flags + INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory + INST put_cash(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap word in memory + INST put_cas(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap dword or qword in memory // control INST put_svc(uint16_t imm16); ///< Supervisor call diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 4f3c303..6f78531 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -493,6 +493,38 @@ namespace asmio::arm { put_inst_bic(dst, a, b, shift, lsl6, true); } + void BufferWriter::put_inst_cas(Registry ptr, Registry src, Registry cmp, Order order, uint8_t size) { + if (!ptr.wide()) { + throw std::runtime_error {"Invalid operand, destination register must be wide"}; + } + + if (ptr.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, destination can't be the zero register"}; + } + + if (!cmp.is(Registry::GENERAL) || !src.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operand, expected general purpose source and compare register"}; + } + + put_dword(size << 30 | order_to_dword_mask(order) | 0b001000'101 << 21 | cmp.reg << 16 | 0b11111 << 10 | ptr.reg << 5 | src.reg); + } + + void BufferWriter::put_casb(Registry dst, Registry src, Registry cmp, Order order) { + put_inst_cas(dst, src, cmp, order, 0b00); + } + + void BufferWriter::put_cash(Registry dst, Registry src, Registry cmp, Order order) { + put_inst_cas(dst, src, cmp, order, 0b01); + } + + void BufferWriter::put_cas(Registry dst, Registry src, Registry cmp, Order order) { + if (src.wide() != cmp.wide()) { + throw std::runtime_error {"Invalid operands, source and compare registers need to be of the same size"}; + } + + put_inst_cas(dst, src, cmp, order, 0b10 | (dst.wide() ? 1 : 0)); + } + void BufferWriter::put_hint(uint8_t imm7) { put_dword(0b1101010100'0'00'011'0010 << 12 | (0b1111'111 & imm7) << 5 | 0b11111); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 4cb35a9..a60cbb8 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -144,6 +144,31 @@ namespace test { }; + TEST (writer_check_cas) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + // considered valid + writer.put_cas(X0, XZR, XZR); + writer.put_casb(X0, X1, W2); + writer.put_cash(X0, X1, X2, Order::RELEASE); + writer.put_cash(X0, X1, X2, Order::ACQUIRE_RELEASE); + + EXPECT_THROW(std::runtime_error) { + writer.put_cas(X0, X1, W2); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_casb(XZR, X1, W1); + }; + + segmented.link(0); + std::vector s0 = {0x1f, 0x7c, 0xbf, 0xc8, 0x01, 0x7c, 0xa2, 0x08, 0x01, 0xfc, 0xa2, 0x48, 0x01, 0xfc, 0xe2, 0x48}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM @@ -1718,6 +1743,46 @@ namespace test { }; + TEST (writer_exec_cas) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.section(MemoryFlag::R | MemoryFlag::W); + writer.label("cell"); + writer.put_qword(11); + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.label("a"); + writer.put_adr(X0, "cell"); + writer.put_mov(X1, 13); + writer.put_mov(X2, 12); + writer.put_cas(X0, X1, X2, Order::ACQUIRE); // ordering not actually required + writer.put_ldr(X0, "cell"); + writer.put_ret(); + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.label("b"); + writer.put_adr(X0, "cell"); + writer.put_mov(X1, 12); + writer.put_mov(X2, 11); + writer.put_cas(X0, X1, X2, Order::ACQUIRE_RELEASE); // ordering not actually required + writer.put_ldr(X0, "cell"); + writer.put_ret(); + +#ifdef __ARM_FEATURE_ATOMICS + auto exec = to_executable(segmented); + CHECK(exec.call_u64("a"), 11); + CHECK(exec.call_u64("b"), 12); + CHECK(exec.call_u64("a"), 13); + CHECK(exec.call_u64("b"), 13); + CHECK(exec.call_u64("a"), 13); +#else + SKIP("Large System Extensions not supproted") +#endif + + }; + #endif } \ No newline at end of file diff --git a/test/tasml.cpp b/test/tasml.cpp index 47ef4cf..e402722 100644 --- a/test/tasml.cpp +++ b/test/tasml.cpp @@ -46,6 +46,21 @@ namespace test { }; + TEST (tasml_check_order_semantics) { + + std::string code = R"( + lang aarch64 + cas x0, x1, x2 + cas x0, x1, x2, ra + )"; + + tasml::ErrorHandler reporter {vstl_self.name(), true}; + tasml::assemble(reporter, code); + + ASSERT(reporter.ok()); + + }; + TEST (tasml_emit_x86) { std::string code = R"( From 6048fdc8f84878931fd20903723780effb5ee043 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 19 Apr 2026 20:57:53 +0200 Subject: [PATCH 20/44] AArch64 LDAR instructions --- src/asmio/aarch64/writer.hpp | 6 ++++ src/asmio/aarch64/writer_basic.cpp | 24 ++++++++++++++++ test/aarch64.cpp | 45 ++++++++++++++++++++++++++++++ test/test.hpp | 8 +++++- 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index ad010f9..6f047e5 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -91,6 +91,9 @@ namespace asmio::arm { /// Encode "CAS/CAB/CAH" operations void put_inst_cas(Registry dst, Registry src, Registry cmp, Order order, uint8_t size); + /// Encode "LDAR/LDARH/LDARB" operations + void put_inst_ldar(Registry dst, Registry src, uint8_t size); + public: void put_inst_add_imm(Registry destination, Registry source, uint16_t imm12, bool lsl_12 = false, bool set_flags = false); @@ -183,6 +186,9 @@ namespace asmio::arm { INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory INST put_cash(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap word in memory INST put_cas(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap dword or qword in memory + INST put_ldarb(Registry dst, Registry src); ///< Load-Acquire Register byte from memory + INST put_ldarh(Registry dst, Registry src); ///< Load-Acquire Register word from memory + INST put_ldar(Registry dst, Registry src); ///< Load-Acquire Register dword or qword from memory // control INST put_svc(uint16_t imm16); ///< Supervisor call diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 6f78531..4950030 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -525,6 +525,30 @@ namespace asmio::arm { put_inst_cas(dst, src, cmp, order, 0b10 | (dst.wide() ? 1 : 0)); } + void BufferWriter::put_inst_ldar(Registry dst, Registry src, uint8_t size) { + if (!src.wide()) { + throw std::runtime_error {"Invalid operand, source register must be wide"}; + } + + if (src.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, source can't be the zero register"}; + } + + put_dword(size << 30 | 0b001000'110'11111'1'11111 << 10 | src.reg << 5 | dst.reg); + } + + void BufferWriter::put_ldarb(Registry dst, Registry src) { + put_inst_ldar(dst, src, 0b00); + } + + void BufferWriter::put_ldarh(Registry dst, Registry src) { + put_inst_ldar(dst, src, 0b01); + } + + void BufferWriter::put_ldar(Registry dst, Registry src) { + put_inst_ldar(dst, src, 0b10 | (dst.wide() ? 1 : 0)); + } + void BufferWriter::put_hint(uint8_t imm7) { put_dword(0b1101010100'0'00'011'0010 << 12 | (0b1111'111 & imm7) << 5 | 0b11111); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index a60cbb8..ad4c4b6 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -169,6 +169,31 @@ namespace test { }; + TEST (writer_check_ldar) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + // considered valid + writer.put_ldarb(X11, SP); + writer.put_ldarh(WZR, X10); + writer.put_ldar(W10, SP); + writer.put_ldar(X10, SP); + + EXPECT_THROW(std::runtime_error) { + writer.put_ldarb(X0, XZR); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldar(X1, W1); // source ptr must be qword + }; + + segmented.link(0); + std::vector s0 = {0xeb, 0xff, 0xdf, 0x08, 0x5f, 0xfd, 0xdf, 0x48, 0xea, 0xff, 0xdf, 0x88, 0xea, 0xff, 0xdf, 0xc8}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM @@ -1783,6 +1808,26 @@ namespace test { }; + TEST (writer_exec_ldar) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.section(MemoryFlag::R | MemoryFlag::W); + writer.label("cell"); + writer.put_byte(42); + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.label("a"); + writer.put_adr(X1, "cell"); + writer.put_ldarb(X0, X1); + writer.put_ret(); + + auto exec = to_executable(segmented); + CHECK(exec.call_u64("a"), 42); + + }; + #endif } \ No newline at end of file diff --git a/test/test.hpp b/test/test.hpp index 6928902..89472ec 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -11,10 +11,16 @@ namespace test { buffer.elf_machine = asmio::ElfMachine::NATIVE; } + std::string extra_flags = ""; + + if (buffer.elf_machine == asmio::ElfMachine::X86_64) { + extra_flags += " -Mintel"; + } + asmio::ObjectFile baked = asmio::to_elf(buffer, asmio::Label::UNSET).bake(); asmio::util::TempFile temp {baked}; - std::string out = asmio::call_shell("objdump --visualize-jumps -wxd -Mintel " + temp.path()); + std::string out = asmio::call_shell("objdump --visualize-jumps -wxd " + extra_flags + temp.path()); printf("%s\n", out.c_str()); printf("\nAuto-generated assertions:\n\n"); From 10516dc908a35de2438832eb42c86967410a0b3b Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Mon, 20 Apr 2026 13:46:25 +0200 Subject: [PATCH 21/44] AArch64 LDADD instructions --- src/asmio/aarch64/writer.hpp | 6 ++++++ src/asmio/aarch64/writer_basic.cpp | 30 ++++++++++++++++++++++++++++++ test/aarch64.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 6f047e5..3e350ad 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -94,6 +94,9 @@ namespace asmio::arm { /// Encode "LDAR/LDARH/LDARB" operations void put_inst_ldar(Registry dst, Registry src, uint8_t size); + /// Encode "LDADD/LDADDH/LDADD" operation + void put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size); + public: void put_inst_add_imm(Registry destination, Registry source, uint16_t imm12, bool lsl_12 = false, bool set_flags = false); @@ -189,6 +192,9 @@ namespace asmio::arm { INST put_ldarb(Registry dst, Registry src); ///< Load-Acquire Register byte from memory INST put_ldarh(Registry dst, Registry src); ///< Load-Acquire Register word from memory INST put_ldar(Registry dst, Registry src); ///< Load-Acquire Register dword or qword from memory + INST put_ldaddb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on byte + INST put_ldaddh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on word + INST put_ldadd(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on dword or qword // control INST put_svc(uint16_t imm16); ///< Supervisor call diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 4950030..b360afa 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -549,6 +549,36 @@ namespace asmio::arm { put_inst_ldar(dst, src, 0b10 | (dst.wide() ? 1 : 0)); } + void BufferWriter::put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size) { + if (!src.wide()) { + throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; + } + + if (src.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, source can't be the zero register"}; + } + + uint32_t a = is_order_acquire(order) ? (1 << 23) : 0; + uint32_t r = is_order_release(order) ? (1 << 22) : 0; + put_dword(size << 30 | 0b111000'00'1 << 21 | val.reg << 16 | src.reg << 5 | dst.reg | a | r); + } + + void BufferWriter::put_ldaddb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldadd(val, dst, src, order, 0b00); + } + + void BufferWriter::put_ldaddh(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldadd(val, dst, src, order, 0b01); + } + + void BufferWriter::put_ldadd(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldadd(val, dst, src, order, 0b10 | (val.wide() ? 1 : 0)); + } + void BufferWriter::put_hint(uint8_t imm7) { put_dword(0b1101010100'0'00'011'0010 << 12 | (0b1111'111 & imm7) << 5 | 0b11111); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index ad4c4b6..e0dc884 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -194,6 +194,33 @@ namespace test { }; + TEST (writer_check_ldadd) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_ldadd(X0, X1, X2); + writer.put_ldadd(W11, W1, X2); + writer.put_ldaddb(X11, X1, X2); + writer.put_ldaddb(W0, X1, X2); + writer.put_ldaddb(X11, W0, X2); + writer.put_ldaddb(W2, W0, X2); + + EXPECT_THROW(std::runtime_error) { + writer.put_ldadd(X11, W1, X2); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldaddb(X11, X1, W2); + }; + + segmented.link(0); + std::vector s0 = {0x41, 0x00, 0x20, 0xf8, 0x41, 0x00, 0x2b, 0xb8, 0x41, 0x00, 0x2b, 0x38, 0x41, 0x00, 0x20, 0x38, 0x40, 0x00, 0x2b, 0x38, 0x40, 0x00, 0x22, 0x38}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From 061c33115afdffb329cb007e248db74a3f0fa239 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Tue, 21 Apr 2026 19:02:38 +0200 Subject: [PATCH 22/44] Better tests --- src/asmio/aarch64/writer_basic.cpp | 8 ++++++ test/aarch64.cpp | 46 ++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index b360afa..8cecfe5 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -534,6 +534,10 @@ namespace asmio::arm { throw std::runtime_error {"Invalid operand, source can't be the zero register"}; } + if (!dst.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operand, destination must be a general purpose register"}; + } + put_dword(size << 30 | 0b001000'110'11111'1'11111 << 10 | src.reg << 5 | dst.reg); } @@ -558,6 +562,10 @@ namespace asmio::arm { throw std::runtime_error {"Invalid operand, source can't be the zero register"}; } + if (!val.is(Registry::GENERAL) || !dst.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operand, value and destination must be general purpose registers"}; + } + uint32_t a = is_order_acquire(order) ? (1 << 23) : 0; uint32_t r = is_order_release(order) ? (1 << 22) : 0; put_dword(size << 30 | 0b111000'00'1 << 21 | val.reg << 16 | src.reg << 5 | dst.reg | a | r); diff --git a/test/aarch64.cpp b/test/aarch64.cpp index e0dc884..0427d70 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -188,6 +188,10 @@ namespace test { writer.put_ldar(X1, W1); // source ptr must be qword }; + EXPECT_THROW(std::runtime_error) { + writer.put_ldar(SP, X10); + }; + segmented.link(0); std::vector s0 = {0xeb, 0xff, 0xdf, 0x08, 0x5f, 0xfd, 0xdf, 0x48, 0xea, 0xff, 0xdf, 0x88, 0xea, 0xff, 0xdf, 0xc8}; CHECK(segmented.segments()[0].buffer, s0); // .rwx @@ -201,7 +205,8 @@ namespace test { segmented.elf_machine = ElfMachine::AARCH64; writer.put_ldadd(X0, X1, X2); - writer.put_ldadd(W11, W1, X2); + writer.put_ldadd(W11, W1, SP); + writer.put_ldadd(WZR, WZR, X2); writer.put_ldaddb(X11, X1, X2); writer.put_ldaddb(W0, X1, X2); writer.put_ldaddb(X11, W0, X2); @@ -215,8 +220,16 @@ namespace test { writer.put_ldaddb(X11, X1, W2); }; + EXPECT_THROW(std::runtime_error) { + writer.put_ldaddb(SP, X1, X1); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldaddb(X1, SP, X1); + }; + segmented.link(0); - std::vector s0 = {0x41, 0x00, 0x20, 0xf8, 0x41, 0x00, 0x2b, 0xb8, 0x41, 0x00, 0x2b, 0x38, 0x41, 0x00, 0x20, 0x38, 0x40, 0x00, 0x2b, 0x38, 0x40, 0x00, 0x22, 0x38}; + std::vector s0 = {0x41, 0x00, 0x20, 0xf8, 0xe1, 0x03, 0x2b, 0xb8, 0x5f, 0x00, 0x3f, 0xb8, 0x41, 0x00, 0x2b, 0x38, 0x41, 0x00, 0x20, 0x38, 0x40, 0x00, 0x2b, 0x38, 0x40, 0x00, 0x22, 0x38}; CHECK(segmented.segments()[0].buffer, s0); // .rwx }; @@ -1830,7 +1843,7 @@ namespace test { CHECK(exec.call_u64("b"), 13); CHECK(exec.call_u64("a"), 13); #else - SKIP("Large System Extensions not supproted") + SKIP("AArch64 LSE not supproted") #endif }; @@ -1855,6 +1868,33 @@ namespace test { }; + TEST (writer_exec_ldadd) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.section(MemoryFlag::R | MemoryFlag::W); + writer.label("cell"); + writer.put_dword(0x1234); + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.label("add"); + writer.put_adr(X1, "cell"); + writer.put_mov(X2, 0x1111); + writer.put_ldadd(X2, X0, X1); + writer.put_ret(); + +#ifdef __ARM_FEATURE_ATOMICS + auto exec = to_executable(segmented); + CHECK(exec.call_u64("add"), 0x2345); + CHECK(exec.call_u64("add"), 0x3456); + CHECK(exec.call_u64("add"), 0x4567); +#else + SKIP("AArch64 LSE not supproted") +#endif + + }; + #endif } \ No newline at end of file From 8112405d38851dfffeacfdb4cdc07a388c847310 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Tue, 21 Apr 2026 22:20:33 +0200 Subject: [PATCH 23/44] AArch64 LDP/LDPSW instructions --- src/asmio/aarch64/writer.hpp | 17 ++++-- src/asmio/aarch64/writer_basic.cpp | 66 +++++++++++++++++++++++ test/aarch64.cpp | 84 +++++++++++++++++++++++++++++- 3 files changed, 162 insertions(+), 5 deletions(-) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 3e350ad..c0ff838 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -97,6 +97,9 @@ namespace asmio::arm { /// Encode "LDADD/LDADDH/LDADD" operation void put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size); + /// Encode "LDP/LDPSW" operations + void put_inst_ldpx(Registry r1, Registry r2, Registry src, int64_t offset, uint32_t opcode, bool wide); + public: void put_inst_add_imm(Registry destination, Registry source, uint16_t imm12, bool lsl_12 = false, bool set_flags = false); @@ -186,12 +189,20 @@ namespace asmio::arm { INST put_bfc(Registry dst, BitPattern pattern); ///< Bitfield Clear INST put_bic(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise Bit Clear INST put_bics(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise Bit Clear and set flags - INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory - INST put_cash(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap word in memory - INST put_cas(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap dword or qword in memory INST put_ldarb(Registry dst, Registry src); ///< Load-Acquire Register byte from memory INST put_ldarh(Registry dst, Registry src); ///< Load-Acquire Register word from memory INST put_ldar(Registry dst, Registry src); ///< Load-Acquire Register dword or qword from memory + INST put_ldp(Registry r1, Registry r2, Registry src); ///< Load a Pair of Registers + INST put_ildp(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and load value pair from memory + INST put_ldpi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Load value pair from memory and increment src + INST put_ldpsw(Registry r1, Registry r2, Registry src); ///< Load and sign-extend a pair of dwords into two Registers + INST put_ildpsw(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and load sign-extended dword pair from memory + INST put_ldpswi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Load sign-extended dword pair from memory and increment src + + // large system extension + INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory + INST put_cash(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap word in memory + INST put_cas(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap dword or qword in memory INST put_ldaddb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on byte INST put_ldaddh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on word INST put_ldadd(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on dword or qword diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 8cecfe5..fab84e4 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -553,6 +553,72 @@ namespace asmio::arm { put_inst_ldar(dst, src, 0b10 | (dst.wide() ? 1 : 0)); } + void BufferWriter::put_inst_ldpx(Registry r1, Registry r2, Registry src, int64_t offset, uint32_t opcode, bool wide) { + if (!src.wide()) { + throw std::runtime_error {"Invalid operand, source register must be wide"}; + } + + if (src.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, source can't be the zero register"}; + } + + if (r1.wide() != r2.wide()) { + throw std::runtime_error {"Invalid operands, both destination registers need to be of the same size"}; + } + + if (!r1.is(Registry::GENERAL) || !r2.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operands, both destination registers need to be general purpose"}; + } + + int32_t size = wide ? QWORD : DWORD; + int32_t imm7 = offset / size; + + if (offset % size) { + throw std::runtime_error {"Invalid operand, immediate value not divisible by operand size"}; + } + + if (imm7 < -64 || imm7 > 63) { + throw std::runtime_error {"Invalid operand, immediate value out of range"}; + } + + uint16_t sf = wide ? 1 : 0; + put_dword(sf << 31 | opcode | (imm7 & 0x7f) << 15 | r2.reg << 10 | src.reg << 5 | r1.reg); + } + + void BufferWriter::put_ldp(Registry r1, Registry r2, Registry src) { + put_ldpi(r1, r2, src, 0); + } + + void BufferWriter::put_ildp(Registry r1, Registry r2, Registry src, int64_t offset) { + put_inst_ldpx(r1, r2, src, offset, 0b101'0'011'1 << 22, r1.wide()); + } + + void BufferWriter::put_ldpi(Registry r1, Registry r2, Registry src, int64_t offset) { + put_inst_ldpx(r1, r2, src, offset, 0b101'0'010'1 << 22, r1.wide()); + } + + void BufferWriter::put_ldpsw(Registry r1, Registry r2, Registry src) { + put_ldpswi(r1, r2, src, 0); + } + + void BufferWriter::put_ildpsw(Registry r1, Registry r2, Registry src, int64_t offset) { + if (!r1.wide()) { + // r2 is handled by put_inst_ldpx() + throw std::runtime_error {"Invalid operand, expected qword destination registers"}; + } + + put_inst_ldpx(r1, r2, src, offset, 0b01'101'0'011'1 << 22, false); + } + + void BufferWriter::put_ldpswi(Registry r1, Registry r2, Registry src, int64_t offset) { + if (!r1.wide()) { + // r2 is handled by put_inst_ldpx() + throw std::runtime_error {"Invalid operand, expected qword destination registers"}; + } + + put_inst_ldpx(r1, r2, src, offset, 0b01'101'0'010'1 << 22, false); + } + void BufferWriter::put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 0427d70..8b56c93 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -234,6 +234,58 @@ namespace test { }; + TEST (writer_check_ldp_ldpsw) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + // ldp + writer.put_ldp(X2, X3, X0); + writer.put_ldp(W2, W3, X0); + writer.put_ldp(W2, W3, SP); + writer.put_ldpi(W2, W3, X0, 12); + writer.put_ildp(X2, X3, X0, -8); + + // ldpsw + writer.put_ldpsw(X2, X3, X0); + writer.put_ldpswi(X2, X3, X0, 8); + writer.put_ildpsw(X2, X3, X0, -8); + + EXPECT_THROW(std::runtime_error) { + writer.put_ldp(W2, X3, X0); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ildp(X3, X3, X0, 12); // 12 is not div by 8 + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldp(X2, W3, X0); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldp(X1, X2, W4); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldpi(X1, X2, X4, 512); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldpsw(W2, W3, X0); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ildpsw(W2, W3, X0, 260); + }; + + segmented.link(0); + std::vector s0 = {0x02, 0x0c, 0x40, 0xa9, 0x02, 0x0c, 0x40, 0x29, 0xe2, 0x0f, 0x40, 0x29, 0x02, 0x8c, 0x41, 0x29, 0x02, 0x8c, 0xff, 0xa9, 0x02, 0x0c, 0x40, 0x69, 0x02, 0x0c, 0x41, 0x69, 0x02, 0x0c, 0xff, 0x69}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM @@ -1843,7 +1895,7 @@ namespace test { CHECK(exec.call_u64("b"), 13); CHECK(exec.call_u64("a"), 13); #else - SKIP("AArch64 LSE not supproted") + SKIP("AArch64 LSE not supported") #endif }; @@ -1890,11 +1942,39 @@ namespace test { CHECK(exec.call_u64("add"), 0x3456); CHECK(exec.call_u64("add"), 0x4567); #else - SKIP("AArch64 LSE not supproted") + SKIP("AArch64 LSE not supported") #endif }; + TEST (writer_exec_ldpsw_ldp) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.section(MemoryFlag::R | MemoryFlag::W); + writer.label("cell"); + writer.put_dword(234); + writer.put_dword(-312); + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.label("a"); + writer.put_adr(X3, "cell"); + writer.put_ldpsw(X1, X2, X3); + writer.put_add(X0, X1, X2); + writer.put_ret(); + + writer.section(MemoryFlag::R | MemoryFlag::X); + writer.label("b"); + writer.put_adr(X3, "cell"); + writer.put_ldp(W1, W0, X3); + writer.put_ret(); + + auto exec = to_executable(segmented); + CHECK(exec.call_u64("b"), uint32_t(-312)); + + }; + #endif } \ No newline at end of file From c1bc823e52e8c01e64ec00ec7770e1c1cf792570 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Wed, 22 Apr 2026 07:37:22 +0200 Subject: [PATCH 24/44] AArch64 LDNP instruction --- src/asmio/aarch64/writer.hpp | 1 + src/asmio/aarch64/writer_basic.cpp | 4 ++++ test/aarch64.cpp | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index c0ff838..ec75788 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -198,6 +198,7 @@ namespace asmio::arm { INST put_ldpsw(Registry r1, Registry r2, Registry src); ///< Load and sign-extend a pair of dwords into two Registers INST put_ildpsw(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and load sign-extended dword pair from memory INST put_ldpswi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Load sign-extended dword pair from memory and increment src + INST put_ldnp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Load a non-temporal Pair of Registers // large system extension INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index fab84e4..7a7170c 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -619,6 +619,10 @@ namespace asmio::arm { put_inst_ldpx(r1, r2, src, offset, 0b01'101'0'010'1 << 22, false); } + void BufferWriter::put_ldnp(Registry r1, Registry r2, Registry src, int64_t offset) { + put_inst_ldpx(r1, r2, src, offset, 0b101'0'000'1 << 22, r1.wide()); + } + void BufferWriter::put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 8b56c93..fd97fba 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -286,6 +286,25 @@ namespace test { }; + TEST (writer_check_ldnp) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_ldnp(X1, X2, X3, 16); + writer.put_ldnp(W1, W2, X3, 12); + + EXPECT_THROW(std::runtime_error) { + writer.put_ldnp(X1, X2, X3, 12); + }; + + segmented.link(0); + std::vector s0 = {0x61, 0x08, 0x41, 0xa8, 0x61, 0x88, 0x41, 0x28}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From bef5ba0e3cb896efcdf082fd569708d38a53f126 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Wed, 22 Apr 2026 22:07:56 +0200 Subject: [PATCH 25/44] AArch64 LDXP instruction --- src/asmio/aarch64/writer.hpp | 1 + src/asmio/aarch64/writer_basic.cpp | 22 ++++++++++++++++++++++ test/aarch64.cpp | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index ec75788..31c887a 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -199,6 +199,7 @@ namespace asmio::arm { INST put_ildpsw(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and load sign-extended dword pair from memory INST put_ldpswi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Load sign-extended dword pair from memory and increment src INST put_ldnp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Load a non-temporal Pair of Registers + INST put_ldxp(Registry r1, Registry r2, Registry src); ///< Load exclusive pair of registers // large system extension INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 7a7170c..8db97bd 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -623,6 +623,28 @@ namespace asmio::arm { put_inst_ldpx(r1, r2, src, offset, 0b101'0'000'1 << 22, r1.wide()); } + void BufferWriter::put_ldxp(Registry r1, Registry r2, Registry src) { + + if (!src.wide()) { + throw std::runtime_error {"Invalid operand, source register must be wide"}; + } + + if (src.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, source can't be the zero register"}; + } + + if (r1.wide() != r2.wide()) { + throw std::runtime_error {"Invalid operands, both destination registers need to be of the same size"}; + } + + if (!r1.is(Registry::GENERAL) || !r2.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operands, both destination registers need to be general purpose"}; + } + + uint16_t sf = r1.wide() ? 1 : 0; + put_dword(1 << 31 | sf << 30 | 0b0010000'11'11111'0 << 15 | r2.reg << 10 | src.reg << 5 | r1.reg); + } + void BufferWriter::put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; diff --git a/test/aarch64.cpp b/test/aarch64.cpp index fd97fba..a23c315 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -305,6 +305,25 @@ namespace test { }; + TEST (writer_check_ldxp) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_ldxp(X1, X2, X3); + writer.put_ldxp(W1, W2, X3); + + EXPECT_THROW(std::runtime_error) { + writer.put_ldnp(W1, X2, X3); + }; + + segmented.link(0); + std::vector s0 = {0x61, 0x08, 0x7f, 0xc8, 0x61, 0x08, 0x7f, 0x88}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From ea857c3a1f04edb83243896f20a03376545f2942 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 23 Apr 2026 14:03:30 +0200 Subject: [PATCH 26/44] Fixed encoding --- src/asmio/aarch64/writer.hpp | 6 ++--- src/asmio/aarch64/writer_basic.cpp | 37 ++++++++++++++++++------------ test/aarch64.cpp | 4 ++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 31c887a..c8579cb 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -98,7 +98,7 @@ namespace asmio::arm { void put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size); /// Encode "LDP/LDPSW" operations - void put_inst_ldpx(Registry r1, Registry r2, Registry src, int64_t offset, uint32_t opcode, bool wide); + void put_inst_ldpx(Registry r1, Registry r2, Registry src, int64_t offset, MemoryOperation op, uint32_t size, bool load, uint32_t opc); public: @@ -192,10 +192,10 @@ namespace asmio::arm { INST put_ldarb(Registry dst, Registry src); ///< Load-Acquire Register byte from memory INST put_ldarh(Registry dst, Registry src); ///< Load-Acquire Register word from memory INST put_ldar(Registry dst, Registry src); ///< Load-Acquire Register dword or qword from memory - INST put_ldp(Registry r1, Registry r2, Registry src); ///< Load a Pair of Registers + INST put_ldp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Load a Pair of Registers INST put_ildp(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and load value pair from memory INST put_ldpi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Load value pair from memory and increment src - INST put_ldpsw(Registry r1, Registry r2, Registry src); ///< Load and sign-extend a pair of dwords into two Registers + INST put_ldpsw(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Load and sign-extend a pair of dwords into two Registers INST put_ildpsw(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and load sign-extended dword pair from memory INST put_ldpswi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Load sign-extended dword pair from memory and increment src INST put_ldnp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Load a non-temporal Pair of Registers diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 8db97bd..0fc0f5b 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -553,7 +553,7 @@ namespace asmio::arm { put_inst_ldar(dst, src, 0b10 | (dst.wide() ? 1 : 0)); } - void BufferWriter::put_inst_ldpx(Registry r1, Registry r2, Registry src, int64_t offset, uint32_t opcode, bool wide) { + void BufferWriter::put_inst_ldpx(Registry r1, Registry r2, Registry src, int64_t offset, MemoryOperation op, uint32_t size, bool load, uint32_t opc) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source register must be wide"}; } @@ -570,7 +570,6 @@ namespace asmio::arm { throw std::runtime_error {"Invalid operands, both destination registers need to be general purpose"}; } - int32_t size = wide ? QWORD : DWORD; int32_t imm7 = offset / size; if (offset % size) { @@ -581,46 +580,54 @@ namespace asmio::arm { throw std::runtime_error {"Invalid operand, immediate value out of range"}; } - uint16_t sf = wide ? 1 : 0; - put_dword(sf << 31 | opcode | (imm7 & 0x7f) << 15 | r2.reg << 10 | src.reg << 5 | r1.reg); + // TODO maybe we can find some common bit pattern for this enum? + uint32_t bits = 0; + if (op == POST) bits = 0b01; + else if (op == PRE) bits = 0b11; + else if (op == OFFSET) bits = 0b10; + + put_dword(opc << 30 | 0b101 << 27 | bits << 23 | load << 22 | (imm7 & 0x7f) << 15 | r2.reg << 10 | src.reg << 5 | r1.reg); } - void BufferWriter::put_ldp(Registry r1, Registry r2, Registry src) { - put_ldpi(r1, r2, src, 0); + void BufferWriter::put_ldp(Registry r1, Registry r2, Registry src, int64_t offset) { + put_inst_ldpx(r1, r2, src, offset, OFFSET, r1.size, true, r1.wide() << 1); } void BufferWriter::put_ildp(Registry r1, Registry r2, Registry src, int64_t offset) { - put_inst_ldpx(r1, r2, src, offset, 0b101'0'011'1 << 22, r1.wide()); + put_inst_ldpx(r1, r2, src, offset, PRE, r1.size, true, r1.wide() << 1); } void BufferWriter::put_ldpi(Registry r1, Registry r2, Registry src, int64_t offset) { - put_inst_ldpx(r1, r2, src, offset, 0b101'0'010'1 << 22, r1.wide()); + put_inst_ldpx(r1, r2, src, offset, POST, r1.size, true, r1.wide() << 1); } - void BufferWriter::put_ldpsw(Registry r1, Registry r2, Registry src) { - put_ldpswi(r1, r2, src, 0); + void BufferWriter::put_ldpsw(Registry r1, Registry r2, Registry src, int64_t offset) { + if (!r1.wide()) { + throw std::runtime_error {"Invalid operand, expected qword destination registers"}; + } + + put_inst_ldpx(r1, r2, src, offset, OFFSET, DWORD, true, 1); } void BufferWriter::put_ildpsw(Registry r1, Registry r2, Registry src, int64_t offset) { if (!r1.wide()) { - // r2 is handled by put_inst_ldpx() throw std::runtime_error {"Invalid operand, expected qword destination registers"}; } - put_inst_ldpx(r1, r2, src, offset, 0b01'101'0'011'1 << 22, false); + put_inst_ldpx(r1, r2, src, offset, PRE, DWORD, true, 1); } void BufferWriter::put_ldpswi(Registry r1, Registry r2, Registry src, int64_t offset) { if (!r1.wide()) { - // r2 is handled by put_inst_ldpx() throw std::runtime_error {"Invalid operand, expected qword destination registers"}; } - put_inst_ldpx(r1, r2, src, offset, 0b01'101'0'010'1 << 22, false); + put_inst_ldpx(r1, r2, src, offset, POST, DWORD, true, 1); } void BufferWriter::put_ldnp(Registry r1, Registry r2, Registry src, int64_t offset) { - put_inst_ldpx(r1, r2, src, offset, 0b101'0'000'1 << 22, r1.wide()); + // FIXME this cast is an ugly hack, we use this to get bits == 0 in put_inst_ldpx() + put_inst_ldpx(r1, r2, src, offset, static_cast(-1), r1.size, true, r1.wide() << 1); } void BufferWriter::put_ldxp(Registry r1, Registry r2, Registry src) { diff --git a/test/aarch64.cpp b/test/aarch64.cpp index a23c315..3b228d4 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -281,7 +281,7 @@ namespace test { }; segmented.link(0); - std::vector s0 = {0x02, 0x0c, 0x40, 0xa9, 0x02, 0x0c, 0x40, 0x29, 0xe2, 0x0f, 0x40, 0x29, 0x02, 0x8c, 0x41, 0x29, 0x02, 0x8c, 0xff, 0xa9, 0x02, 0x0c, 0x40, 0x69, 0x02, 0x0c, 0x41, 0x69, 0x02, 0x0c, 0xff, 0x69}; + std::vector s0 = {0x02, 0x0c, 0x40, 0xa9, 0x02, 0x0c, 0x40, 0x29, 0xe2, 0x0f, 0x40, 0x29, 0x02, 0x8c, 0xc1, 0x28, 0x02, 0x8c, 0xff, 0xa9, 0x02, 0x0c, 0x40, 0x69, 0x02, 0x0c, 0xc1, 0x68, 0x02, 0x0c, 0xff, 0x69}; CHECK(segmented.segments()[0].buffer, s0); // .rwx }; @@ -315,7 +315,7 @@ namespace test { writer.put_ldxp(W1, W2, X3); EXPECT_THROW(std::runtime_error) { - writer.put_ldnp(W1, X2, X3); + writer.put_ldxp(W1, X2, X3); }; segmented.link(0); From fb731d845a59c88fbea044d9b943a084ead5e83d Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 23 Apr 2026 18:32:01 +0200 Subject: [PATCH 27/44] AArch64 STP/STNP instructions --- src/asmio/aarch64/writer.hpp | 4 ++++ src/asmio/aarch64/writer_basic.cpp | 17 +++++++++++++++++ test/aarch64.cpp | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index c8579cb..1e31769 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -200,6 +200,10 @@ namespace asmio::arm { INST put_ldpswi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Load sign-extended dword pair from memory and increment src INST put_ldnp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Load a non-temporal Pair of Registers INST put_ldxp(Registry r1, Registry r2, Registry src); ///< Load exclusive pair of registers + INST put_stp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Store a Pair of Registers + INST put_istp(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and store value pair to memory + INST put_stpi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Store value pair to memory and increment src + INST put_stnp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Store a non-temporal Pair of Registers // large system extension INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 0fc0f5b..fe68da5 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -652,6 +652,23 @@ namespace asmio::arm { put_dword(1 << 31 | sf << 30 | 0b0010000'11'11111'0 << 15 | r2.reg << 10 | src.reg << 5 | r1.reg); } + void BufferWriter::put_stp(Registry r1, Registry r2, Registry src, int64_t offset) { + put_inst_ldpx(r1, r2, src, offset, OFFSET, r1.size, false, r1.wide() << 1); + } + + void BufferWriter::put_istp(Registry r1, Registry r2, Registry src, int64_t offset) { + put_inst_ldpx(r1, r2, src, offset, PRE, r1.size, false, r1.wide() << 1); + } + + void BufferWriter::put_stpi(Registry r1, Registry r2, Registry src, int64_t offset) { + put_inst_ldpx(r1, r2, src, offset, POST, r1.size, false, r1.wide() << 1); + } + + void BufferWriter::put_stnp(Registry r1, Registry r2, Registry src, int64_t offset) { + // FIXME this cast is an ugly hack, we use this to get bits == 0 in put_inst_ldpx() + put_inst_ldpx(r1, r2, src, offset, static_cast(-1), r1.size, false, r1.wide() << 1); + } + void BufferWriter::put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 3b228d4..7199a24 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -324,6 +324,24 @@ namespace test { }; + TEST (writer_check_stp_stnp) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_stp(X1, X2, X3); + writer.put_stp(W1, W2, X3, 128); + writer.put_stpi(X3, X4, X3, 32); + writer.put_istp(W1, W2, X3, 64); + writer.put_stnp(X1, X2, X3, 48); + + segmented.link(0); + std::vector s0 = {0x61, 0x08, 0x00, 0xa9, 0x61, 0x08, 0x10, 0x29, 0x63, 0x10, 0x82, 0xa8, 0x61, 0x08, 0x88, 0x29, 0x61, 0x08, 0x03, 0xa8}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From ac08d5a69beda09c03f39b164e7d2603ff2c22f6 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 23 Apr 2026 18:53:46 +0200 Subject: [PATCH 28/44] AArch64 ERET instructions --- src/asmio/aarch64/writer.hpp | 1 + src/asmio/aarch64/writer_basic.cpp | 4 ++++ test/aarch64.cpp | 16 ++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 1e31769..1454121 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -122,6 +122,7 @@ namespace asmio::arm { INST put_mov(Registry dst, uint64_t imm); ///< Move immediate into register INST put_mov(Registry dst, Registry src); ///< Move value between registers INST put_ret(); ///< Return from procedure using link register + INST put_eret(); ///< Exception return INST put_ret(Registry src); ///< Return from procedure INST put_rbit(Registry dst, Registry src); ///< Reverse bits INST put_clz(Registry dst, Registry src); ///< Count leading zeros diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index fe68da5..063d8d3 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -105,6 +105,10 @@ namespace asmio::arm { put_ret(LR); } + void BufferWriter::put_eret() { + put_dword(0b110'101'1'0100'11111'0000'0'0'11111'00000); + } + void BufferWriter::put_ret(Registry registry) { if (!registry.wide()) { diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 7199a24..a9772dc 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -342,6 +342,22 @@ namespace test { }; + TEST (writer_check_eret_hlt_hvc) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_eret(); + writer.put_hlt(0x42); + writer.put_hvc(0x69); + + segmented.link(0); + std::vector s0 = {0xe0, 0x03, 0x9f, 0xd6, 0x40, 0x08, 0x40, 0xd4, 0x22, 0x0d, 0x00, 0xd4}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From 79267a224ea92dbfed494f133f1f9fda77b2ecac Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Thu, 23 Apr 2026 19:29:06 +0200 Subject: [PATCH 29/44] AArch64 atomic load instructions --- src/asmio/aarch64/writer.hpp | 23 +++++- src/asmio/aarch64/writer_basic.cpp | 122 +++++++++++++++++++++++++++-- test/aarch64.cpp | 21 +++++ 3 files changed, 160 insertions(+), 6 deletions(-) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 1454121..c9723c0 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -95,7 +95,7 @@ namespace asmio::arm { void put_inst_ldar(Registry dst, Registry src, uint8_t size); /// Encode "LDADD/LDADDH/LDADD" operation - void put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size); + void put_inst_ldop(Registry val, Registry dst, Registry src, Order order, uint8_t size, uint32_t opc); /// Encode "LDP/LDPSW" operations void put_inst_ldpx(Registry r1, Registry r2, Registry src, int64_t offset, MemoryOperation op, uint32_t size, bool load, uint32_t opc); @@ -213,6 +213,27 @@ namespace asmio::arm { INST put_ldaddb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on byte INST put_ldaddh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on word INST put_ldadd(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic add on dword or qword + INST put_ldclrb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bit clear on byte + INST put_ldclrh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bit clear on word + INST put_ldclr(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bit clear on dword or qword + INST put_ldeorb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bitwise xor on byte + INST put_ldeorh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bitwise xor on word + INST put_ldeor(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bitwise xor on dword or qword + INST put_ldsetb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bit set on byte + INST put_ldseth(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bit set on word + INST put_ldset(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic bit set on dword or qword + INST put_ldsmaxb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on byte + INST put_ldsmaxh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on word + INST put_ldsmax(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword + INST put_ldumaxb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on byte + INST put_ldumaxh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on word + INST put_ldumax(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword + INST put_ldsminb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on byte + INST put_ldsminh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on word + INST put_ldsmin(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword + INST put_lduminb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on byte + INST put_lduminh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on word + INST put_ldumin(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword // control INST put_svc(uint16_t imm16); ///< Supervisor call diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 063d8d3..4762f66 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -673,7 +673,7 @@ namespace asmio::arm { put_inst_ldpx(r1, r2, src, offset, static_cast(-1), r1.size, false, r1.wide() << 1); } - void BufferWriter::put_inst_ldadd(Registry val, Registry dst, Registry src, Order order, uint8_t size) { + void BufferWriter::put_inst_ldop(Registry val, Registry dst, Registry src, Order order, uint8_t size, uint32_t opc) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; } @@ -688,15 +688,15 @@ namespace asmio::arm { uint32_t a = is_order_acquire(order) ? (1 << 23) : 0; uint32_t r = is_order_release(order) ? (1 << 22) : 0; - put_dword(size << 30 | 0b111000'00'1 << 21 | val.reg << 16 | src.reg << 5 | dst.reg | a | r); + put_dword(size << 30 | 0b111000'00'1 << 21 | val.reg << 16 | opc << 12 | src.reg << 5 | dst.reg | a | r); } void BufferWriter::put_ldaddb(Registry val, Registry dst, Registry src, Order order) { - put_inst_ldadd(val, dst, src, order, 0b00); + put_inst_ldop(val, dst, src, order, 0b00, 0b000); } void BufferWriter::put_ldaddh(Registry val, Registry dst, Registry src, Order order) { - put_inst_ldadd(val, dst, src, order, 0b01); + put_inst_ldop(val, dst, src, order, 0b01, 0b000); } void BufferWriter::put_ldadd(Registry val, Registry dst, Registry src, Order order) { @@ -704,7 +704,119 @@ namespace asmio::arm { throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; } - put_inst_ldadd(val, dst, src, order, 0b10 | (val.wide() ? 1 : 0)); + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b000); + } + + void BufferWriter::put_ldclrb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b00, 0b001); + } + + void BufferWriter::put_ldclrh(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b01, 0b001); + } + + void BufferWriter::put_ldclr(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b001); + } + + void BufferWriter::put_ldeorb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b00, 0b010); + } + + void BufferWriter::put_ldeorh(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b01, 0b010); + } + + void BufferWriter::put_ldeor(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b010); + } + + void BufferWriter::put_ldsetb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b00, 0b011); + } + + void BufferWriter::put_ldseth(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b01, 0b011); + } + + void BufferWriter::put_ldset(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b011); + } + + void BufferWriter::put_ldsmaxb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b00, 0b100); + } + + void BufferWriter::put_ldsmaxh(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b01, 0b100); + } + + void BufferWriter::put_ldsmax(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b100); + } + + void BufferWriter::put_ldumaxb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b00, 0b110); + } + + void BufferWriter::put_ldumaxh(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b01, 0b110); + } + + void BufferWriter::put_ldumax(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b110); + } + + void BufferWriter::put_ldsminb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b00, 0b101); + } + + void BufferWriter::put_ldsminh(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b01, 0b101); + } + + void BufferWriter::put_ldsmin(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b101); + } + + void BufferWriter::put_lduminb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b00, 0b111); + } + + void BufferWriter::put_lduminh(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b01, 0b111); + } + + void BufferWriter::put_ldumin(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b111); } void BufferWriter::put_hint(uint8_t imm7) { diff --git a/test/aarch64.cpp b/test/aarch64.cpp index a9772dc..3484996 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -358,6 +358,27 @@ namespace test { }; + TEST (writer_check_ldops) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_ldaddb(W1, W2, X8, Order::ACQUIRE); + writer.put_ldclr(X5, X6, X11, Order::RELEASE); + writer.put_ldeor(X3, X24, X4, Order::ACQUIRE_RELEASE); + writer.put_ldseth(X6, X6, X6, Order::NONE); + writer.put_ldsmax(X1, X5, X9, Order::RELEASE); + writer.put_ldumax(X2, X6, X10, Order::ACQUIRE); + writer.put_ldsmin(X3, X7, X11, Order::NONE); + writer.put_ldumin(X4, X8, X12, Order::ACQUIRE_RELEASE); + + segmented.link(0); + std::vector s0 = {0x02, 0x01, 0xa1, 0x38, 0x66, 0x11, 0x65, 0xf8, 0x98, 0x20, 0xe3, 0xf8, 0xc6, 0x30, 0x26, 0x78, 0x25, 0x41, 0x61, 0xf8, 0x46, 0x61, 0xa2, 0xf8, 0x67, 0x51, 0x23, 0xf8, 0x88, 0x71, 0xe4, 0xf8}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From 5b16f173cc81217bfffa0b664592b520eb2b56bf Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Fri, 24 Apr 2026 19:59:02 +0200 Subject: [PATCH 30/44] AArch64 CCMP/CCMN instructions --- src/asmio/aarch64/writer.hpp | 4 ++++ src/asmio/aarch64/writer_basic.cpp | 24 ++++++++++++++++++++++++ test/aarch64.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index c9723c0..7a1bf93 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -205,6 +205,10 @@ namespace asmio::arm { INST put_istp(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and store value pair to memory INST put_stpi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Store value pair to memory and increment src INST put_stnp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Store a non-temporal Pair of Registers + INST put_ccmp(Condition condition, Condition flags, Registry reg, uint8_t imm5); ///< Conditional compare + INST put_ccmp(Condition condition, Condition flags, Registry reg, Registry val); ///< Conditional compare + INST put_ccmn(Condition condition, Condition flags, Registry reg, uint8_t imm5); ///< Conditional compare negative + INST put_ccmn(Condition condition, Condition flags, Registry reg, Registry val); ///< Conditional compare negative // large system extension INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 4762f66..d4db61a 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -673,6 +673,30 @@ namespace asmio::arm { put_inst_ldpx(r1, r2, src, offset, static_cast(-1), r1.size, false, r1.wide() << 1); } + void BufferWriter::put_ccmp(Condition condition, Condition flags, Registry val, uint8_t imm5) { + put_dword(val.wide() << 31 | 0b11'11010010 << 21 | (imm5 & 0b11111) << 16 | uint32_t(condition) << 12 | 0b10 << 10 | val.reg << 5 | uint32_t(flags)); + } + + void BufferWriter::put_ccmn(Condition condition, Condition flags, Registry val, uint8_t imm5) { + put_dword(val.wide() << 31 | 0b01'11010010 << 21 | (imm5 & 0b11111) << 16 | uint32_t(condition) << 12 | 0b10 << 10 | val.reg << 5 | uint32_t(flags)); + } + + void BufferWriter::put_ccmp(Condition condition, Condition flags, Registry val, Registry second) { + if (val.size != second.size) { + throw std::runtime_error {"Invalid operands, both registers need to be of the same size"}; + } + + put_dword(val.wide() << 31 | 0b11'11010010 << 21 | second.reg << 16 | uint32_t(condition) << 12 | 0b00 << 10 | val.reg << 5 | uint32_t(flags)); + } + + void BufferWriter::put_ccmn(Condition condition, Condition flags, Registry val, Registry second) { + if (val.size != second.size) { + throw std::runtime_error {"Invalid operands, both registers need to be of the same size"}; + } + + put_dword(val.wide() << 31 | 0b01'11010010 << 21 | second.reg << 16 | uint32_t(condition) << 12 | 0b00 << 10 | val.reg << 5 | uint32_t(flags)); + } + void BufferWriter::put_inst_ldop(Registry val, Registry dst, Registry src, Order order, uint8_t size, uint32_t opc) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 3484996..d7a3c99 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -379,6 +379,33 @@ namespace test { }; + TEST (writer_check_ccmp_ccmn) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_ccmp(Condition::LE, Condition::NE, X2, X7); + writer.put_ccmn(Condition::GE, Condition::CC, X5, X3); + writer.put_ccmp(Condition::LE, Condition::NE, X2, 15); + writer.put_ccmn(Condition::CC, Condition::CC, X5, 3); + + // check overflow + writer.put_ccmn(Condition::CC, Condition::CC, X5, 0xff); + writer.put_ccmp(Condition::CC, Condition::CC, X5, 0xff); + + EXPECT_THROW(std::runtime_error) { + writer.put_ccmp(Condition::LE, Condition::NE, X2, W7); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ccmn(Condition::GE, Condition::CC, W5, X3); + }; + + dump(segmented); + + }; + /* * region Executable * Begin architecture depended tests for ARM From 3f884827545ec2607a7c001eec9715e5b7397a49 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sat, 25 Apr 2026 13:28:46 +0200 Subject: [PATCH 31/44] AArch64 SWP instruction --- src/asmio/aarch64/writer.hpp | 3 +++ src/asmio/aarch64/writer_basic.cpp | 16 ++++++++++++++++ test/aarch64.cpp | 21 ++++++++++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 7a1bf93..2a881ee 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -211,6 +211,9 @@ namespace asmio::arm { INST put_ccmn(Condition condition, Condition flags, Registry reg, Registry val); ///< Conditional compare negative // large system extension + INST put_swpb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap byte from 'src' to memory + INST put_swph(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap word from 'src' to memory + INST put_swp(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap dword or qword from 'src' to memory INST put_casb(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap byte in memory INST put_cash(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap word in memory INST put_cas(Registry ptr, Registry src, Registry cmp, Order order = Order::NONE); ///< Compare and Swap dword or qword in memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index d4db61a..0abb544 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -513,6 +513,22 @@ namespace asmio::arm { put_dword(size << 30 | order_to_dword_mask(order) | 0b001000'101 << 21 | cmp.reg << 16 | 0b11111 << 10 | ptr.reg << 5 | src.reg); } + void BufferWriter::put_swpb(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b00, 0b1000); + } + + void BufferWriter::put_swph(Registry val, Registry dst, Registry src, Order order) { + put_inst_ldop(val, dst, src, order, 0b01, 0b1000); + } + + void BufferWriter::put_swp(Registry val, Registry dst, Registry src, Order order) { + if (val.wide() != dst.wide()) { + throw std::runtime_error {"Invalid operand, value and destination need to be of the same size"}; + } + + put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b1000); + } + void BufferWriter::put_casb(Registry dst, Registry src, Registry cmp, Order order) { put_inst_cas(dst, src, cmp, order, 0b00); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index d7a3c99..588b431 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -379,6 +379,23 @@ namespace test { }; + TEST (writer_check_swp) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_swpb(W1, W2, X8, Order::ACQUIRE); + writer.put_swph(X5, X6, X11, Order::RELEASE); + writer.put_swp(X3, X24, X4, Order::ACQUIRE_RELEASE); + writer.put_swp(W3, W24, X4, Order::NONE); + + segmented.link(0); + std::vector s0 = {0x02, 0x81, 0xa1, 0x38, 0x66, 0x81, 0x65, 0x78, 0x98, 0x80, 0xe3, 0xf8, 0x98, 0x80, 0x23, 0xb8}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + TEST (writer_check_ccmp_ccmn) { SegmentedBuffer segmented; @@ -402,7 +419,9 @@ namespace test { writer.put_ccmn(Condition::GE, Condition::CC, W5, X3); }; - dump(segmented); + segmented.link(0); + std::vector s0 = {0x41, 0xd0, 0x47, 0xfa, 0xa3, 0xa0, 0x43, 0xba, 0x41, 0xd8, 0x4f, 0xfa, 0xa3, 0x38, 0x43, 0xba, 0xa3, 0x38, 0x5f, 0xba, 0xa3, 0x38, 0x5f, 0xfa}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx }; From 3fd63e44ac7e661ebdf3d14becafecba9c299a24 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 26 Apr 2026 16:02:14 +0200 Subject: [PATCH 32/44] AArch64 ADD/SUB/ADDS/SUBS (immediate) instructions --- src/asmio/aarch64/writer.cpp | 18 +++++++++-------- src/asmio/aarch64/writer.hpp | 8 +++++++- src/asmio/aarch64/writer_basic.cpp | 16 +++++++++++++++ test/aarch64.cpp | 32 ++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/asmio/aarch64/writer.cpp b/src/asmio/aarch64/writer.cpp index 5c6ab36..0a03be2 100644 --- a/src/asmio/aarch64/writer.cpp +++ b/src/asmio/aarch64/writer.cpp @@ -293,19 +293,21 @@ namespace asmio::arm { put_dword(sf << 31 | 0b00'11010100 << 21 | falsy.reg << 16 | uint32_t(condition) << 12 | increment_truth << 10 | truthy.reg << 5 | dst.reg); } - void BufferWriter::put_inst_add_imm(Registry destination, Registry source, uint16_t imm12, bool lsl_12, bool set_flags) { - - if (source.is(Registry::ZERO) || destination.is(Registry::ZERO)) { - throw std::runtime_error {"Invalid operands, zero register can't be used here"}; + void BufferWriter::put_inst_add_imm(Registry dst, Registry src, uint16_t imm12, bool lsl_12, bool set_flags, bool subtract) { + if (imm12 > 0xfff) { // 12 bits + throw std::runtime_error {"Invalid operand, immediate value out of bounds"}; } - if (destination.wide() != source.wide()) { + if (dst.size != src.size) { throw std::runtime_error {"Invalid operands, all given registers need to be of the same width"}; } - uint16_t sf = destination.wide() ? 1 : 0; - uint32_t fb = (set_flags ? 1 : 0) << 29; // S bit - put_dword(sf << 31 | 0b0'0'10001 << 24 | fb | (lsl_12 ? 0b01 : 0x00) << 22 | imm12 << 10 | source.reg << 5 | destination.reg); + if (dst.is(Registry::ZERO) || src.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operands, zero register can't be used here"}; + } + + uint32_t sf = dst.wide() ? 1 : 0; + put_dword(sf << 31 | subtract << 30 | set_flags << 29 | 0b100010 << 23 | lsl_12 << 22 | imm12 << 10 | src.reg << 5 | dst.reg); } void BufferWriter::put_inst_add_shifted(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6, bool set_flags) { diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 2a881ee..d4dacaa 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -100,9 +100,11 @@ namespace asmio::arm { /// Encode "LDP/LDPSW" operations void put_inst_ldpx(Registry r1, Registry r2, Registry src, int64_t offset, MemoryOperation op, uint32_t size, bool load, uint32_t opc); + /// Encode "ADD/ADDS/SUB/SUBS (immediate)" operation + void put_inst_add_imm(Registry destination, Registry source, uint16_t imm12, bool lsl_12, bool set_flags, bool subtract); + public: - void put_inst_add_imm(Registry destination, Registry source, uint16_t imm12, bool lsl_12 = false, bool set_flags = false); void put_inst_add_shifted(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6, bool set_flags = false); public: @@ -114,6 +116,8 @@ namespace asmio::arm { INST put_adcs(Registry dst, Registry a, Registry b); ///< Add with carry and set flags INST put_add(Registry dst, Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Add two registers, potentially extending one of them INST put_adds(Registry dst, Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Add two registers, set the flags, potentially extending one of them + INST put_add(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Add 12bit, unsigned immediate (optionally shifted by 12 bits) to a register + INST put_adds(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Add with Carry 12bit, unsigned immediate (optionally shifted by 12 bits) to a register INST put_adr(Registry destination, Label label); ///< Form a PC-relative address INST put_adrp(Registry destination, Label label); ///< Form a PC-page-relative address INST put_movz(Registry dst, uint16_t imm, uint16_t shift = 0); ///< Move shifted WORD into register, zero other bits @@ -146,6 +150,8 @@ namespace asmio::arm { INST put_sbcs(Registry dst, Registry a, Registry b); ///< Subtract with Carry and set flags INST put_sub(Registry dst, Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Add two registers, potentially extending one of them INST put_subs(Registry dst, Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Add two registers, set the flags, potentially extending one of them + INST put_sub(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Subtract 12bit, unsigned immediate (optionally shifted by 12 bits) to a register + INST put_subs(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Subtract with Carry 12bit, unsigned immediate (optionally shifted by 12 bits) to a register INST put_cmp(Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Compare INST put_cmn(Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Compare negative INST put_madd(Registry dst, Registry a, Registry b, Registry addend); ///< Multiply and Add 64 bit registers diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 0abb544..ad71303 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -23,6 +23,14 @@ namespace asmio::arm { put_inst_extended_register(0b0'0'01011001, dst, a, b, size, lsl3, true); } + void BufferWriter::put_add(Registry dst, Registry src, uint16_t imm12, bool shift12) { + put_inst_add_imm(dst, src, imm12, shift12, false, false); + } + + void BufferWriter::put_adds(Registry dst, Registry src, uint16_t imm12, bool shift12) { + put_inst_add_imm(dst, src, imm12, shift12, true, false); + } + void BufferWriter::put_adr(Registry destination, Label label) { buffer.add_linkage(label, LinkageType::AARCH64_21_5_LO_HI); put_dword(0b0 << 31 | 0b10000 << 24 | destination.reg); @@ -243,6 +251,14 @@ namespace asmio::arm { put_inst_extended_register(0b1'0'01011001, dst, a, b, size, lsl3, true); } + void BufferWriter::put_sub(Registry dst, Registry src, uint16_t imm12, bool shift_12) { + put_inst_add_imm(dst, src, imm12, shift_12, false, true); + } + + void BufferWriter::put_subs(Registry dst, Registry src, uint16_t imm12, bool shift_12) { + put_inst_add_imm(dst, src, imm12, shift_12, true, true); + } + void BufferWriter::put_cmp(Registry a, Registry b, Sizing size, uint8_t lsl3) { put_subs(a.wide() ? XZR : WZR, a, b, size, lsl3); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 588b431..a6b76f9 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -425,6 +425,22 @@ namespace test { }; + TEST (writer_check_add_sub) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.put_add(X1, X8, 7, true); + writer.put_adds(X2, X7, 0xfff); + writer.put_sub(X3, X6, 700); + writer.put_subs(X4, X5, 0xfff, true); + + segmented.link(0); + std::vector s0 = {0x01, 0x1d, 0x40, 0x91, 0xe2, 0xfc, 0x3f, 0xb1, 0xc3, 0xf0, 0x0a, 0xd1, 0xa4, 0xfc, 0x7f, 0xf1}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM @@ -2114,6 +2130,22 @@ namespace test { }; + TEST (writer_exec_add_sub_imm) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.put_mov(X1, 13); + writer.put_add(X2, X1, 7); + writer.put_sub(X3, X1, 3); + writer.put_add(X0, X2, X3); + writer.put_ret(); + + auto exec = to_executable(segmented); + CHECK(exec.call_i64(), 30); + + }; + #endif } \ No newline at end of file From c774222f3c8a7c43ce16a7c1ca531383f7977965 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 26 Apr 2026 16:46:33 +0200 Subject: [PATCH 33/44] AArch64 ADD/SUB/ADDS/SUBS (shifted register) instructions --- src/asmio/aarch64/writer.cpp | 11 ++++++---- src/asmio/aarch64/writer.hpp | 9 +++++--- src/asmio/aarch64/writer_basic.cpp | 16 +++++++++++++++ test/aarch64.cpp | 33 +++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/asmio/aarch64/writer.cpp b/src/asmio/aarch64/writer.cpp index 0a03be2..dbf429c 100644 --- a/src/asmio/aarch64/writer.cpp +++ b/src/asmio/aarch64/writer.cpp @@ -109,7 +109,7 @@ namespace asmio::arm { uint16_t sf = destination.wide() ? 1 : 0; uint32_t fb = (set_flags ? 1 : 0) << 29; // S bit - put_dword(sf << 31 | opcode_from_21 << 21 | fb | b.reg << 16 | uint8_t(add) << 13 | imm3 << 10 | a.reg << 5 | destination.reg); + put_dword(sf << 31 | opcode_from_21 << 21 | fb | b.reg << 16 | uint8_t(add) << 13 | (imm3 & 0b111) << 10 | a.reg << 5 | destination.reg); } void BufferWriter::put_inst_adc(Registry destination, Registry a, Registry b, bool set_flags) { @@ -310,16 +310,19 @@ namespace asmio::arm { put_dword(sf << 31 | subtract << 30 | set_flags << 29 | 0b100010 << 23 | lsl_12 << 22 | imm12 << 10 | src.reg << 5 | dst.reg); } - void BufferWriter::put_inst_add_shifted(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6, bool set_flags) { + void BufferWriter::put_inst_add_shifted(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6, bool set_flags, bool subtract) { assert_register_triplet(a, b, destination); + if (!destination.is(Registry::GENERAL) || !a.is(Registry::GENERAL) || !b.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operands, expected general purpose registers"}; + } + if (shift == ShiftType::ROR) { throw std::runtime_error {"Invalid shift type, ROR shift type is not allowed here"}; } uint32_t sf = destination.wide() ? 1 : 0; - uint32_t fb = (set_flags ? 1 : 0) << 29; // S bit - put_dword(sf << 31 | 0b0'0'01011 << 24 | fb | uint8_t(shift) << 22 | b.reg << 16 | imm6 << 10 | a.reg << 5 | destination.reg); + put_dword(sf << 31 | subtract << 30 | set_flags << 29 | 0b0'0'01011 << 24 | uint8_t(shift) << 22 | b.reg << 16 | (imm6 & 0b111111) << 10 | a.reg << 5 | destination.reg); } } \ No newline at end of file diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index d4dacaa..650dbb8 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -103,9 +103,8 @@ namespace asmio::arm { /// Encode "ADD/ADDS/SUB/SUBS (immediate)" operation void put_inst_add_imm(Registry destination, Registry source, uint16_t imm12, bool lsl_12, bool set_flags, bool subtract); - public: - - void put_inst_add_shifted(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6, bool set_flags = false); + /// Encode "ADD/ADDS/SUB/SUBS (shifted register)" operation + void put_inst_add_shifted(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6, bool set_flags, bool subtract); public: @@ -118,6 +117,8 @@ namespace asmio::arm { INST put_adds(Registry dst, Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Add two registers, set the flags, potentially extending one of them INST put_add(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Add 12bit, unsigned immediate (optionally shifted by 12 bits) to a register INST put_adds(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Add with Carry 12bit, unsigned immediate (optionally shifted by 12 bits) to a register + INST put_add(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Add an optionally-shifted register + INST put_adds(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Add, with Carry, an optionally-shifted register INST put_adr(Registry destination, Label label); ///< Form a PC-relative address INST put_adrp(Registry destination, Label label); ///< Form a PC-page-relative address INST put_movz(Registry dst, uint16_t imm, uint16_t shift = 0); ///< Move shifted WORD into register, zero other bits @@ -152,6 +153,8 @@ namespace asmio::arm { INST put_subs(Registry dst, Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Add two registers, set the flags, potentially extending one of them INST put_sub(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Subtract 12bit, unsigned immediate (optionally shifted by 12 bits) to a register INST put_subs(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Subtract with Carry 12bit, unsigned immediate (optionally shifted by 12 bits) to a register + INST put_sub(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Add an optionally-shifted register + INST put_subs(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Add, with Carry, an optionally-shifted register INST put_cmp(Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Compare INST put_cmn(Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Compare negative INST put_madd(Registry dst, Registry a, Registry b, Registry addend); ///< Multiply and Add 64 bit registers diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index ad71303..b1c8968 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -31,6 +31,14 @@ namespace asmio::arm { put_inst_add_imm(dst, src, imm12, shift12, true, false); } + void BufferWriter::put_add(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6) { + put_inst_add_shifted(destination, a, b, shift, imm6, false, false); + } + + void BufferWriter::put_adds(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6) { + put_inst_add_shifted(destination, a, b, shift, imm6, true, false); + } + void BufferWriter::put_adr(Registry destination, Label label) { buffer.add_linkage(label, LinkageType::AARCH64_21_5_LO_HI); put_dword(0b0 << 31 | 0b10000 << 24 | destination.reg); @@ -259,6 +267,14 @@ namespace asmio::arm { put_inst_add_imm(dst, src, imm12, shift_12, true, true); } + void BufferWriter::put_sub(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6) { + put_inst_add_shifted(destination, a, b, shift, imm6, false, true); + } + + void BufferWriter::put_subs(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6) { + put_inst_add_shifted(destination, a, b, shift, imm6, true, true); + } + void BufferWriter::put_cmp(Registry a, Registry b, Sizing size, uint8_t lsl3) { put_subs(a.wide() ? XZR : WZR, a, b, size, lsl3); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index a6b76f9..1d0369a 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -425,7 +425,7 @@ namespace test { }; - TEST (writer_check_add_sub) { + TEST (writer_check_add_sub_imm) { SegmentedBuffer segmented; BufferWriter writer {segmented}; @@ -441,6 +441,22 @@ namespace test { }; + TEST (writer_check_add_sub_shifted) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.put_add(X1, X8, X9, ShiftType::LSL, 3); + writer.put_adds(X2, X7, X10, ShiftType::ASR, 5); + writer.put_sub(X3, X6, X11, ShiftType::LSR, 7); + writer.put_subs(X4, X5, X12, ShiftType::LSL, 0); + + segmented.link(0); + std::vector s0 = {0x01, 0x0d, 0x09, 0x8b, 0xe2, 0x14, 0x8a, 0xab, 0xc3, 0x1c, 0x4b, 0xcb, 0xa4, 0x00, 0x0c, 0xeb}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM @@ -2146,6 +2162,21 @@ namespace test { }; + TEST (writer_exec_add_shifted) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.put_mov(X1, 0x17); + writer.put_mov(X2, 0x03); + writer.put_add(X0, X2, X1, ShiftType::LSL, 4); + writer.put_ret(); + + auto exec = to_executable(segmented); + CHECK(exec.call_i64(), 0x173); + + }; + #endif } \ No newline at end of file From e7cba779bc59fa8ce5784a0df9ace3a7cc788d69 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 26 Apr 2026 19:24:54 +0200 Subject: [PATCH 34/44] AArch64 CSINV/CINV/CSETM/CSNEG/CNEG instructions --- src/asmio/aarch64/writer.cpp | 4 ++-- src/asmio/aarch64/writer.hpp | 7 ++++++- src/asmio/aarch64/writer_basic.cpp | 25 +++++++++++++++++++++++-- test/aarch64.cpp | 18 ++++++++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/asmio/aarch64/writer.cpp b/src/asmio/aarch64/writer.cpp index dbf429c..3b2931e 100644 --- a/src/asmio/aarch64/writer.cpp +++ b/src/asmio/aarch64/writer.cpp @@ -282,7 +282,7 @@ namespace asmio::arm { put_dword(sf << 31 | 0b0011010110 << 21 | bits.reg << 16 | 0b0010 << 12 | uint32_t(shift) << 10 | src.reg << 5 | dst.reg); } - void BufferWriter::put_inst_csinc(Condition condition, Registry dst, Registry truthy, Registry falsy, bool increment_truth) { + void BufferWriter::put_inst_csinc(Condition condition, Registry dst, Registry truthy, Registry falsy, bool increment, bool invert) { assert_register_triplet(dst, truthy, falsy); if (!dst.is(Registry::GENERAL) || !truthy.is(Registry::GENERAL) || !falsy.is(Registry::GENERAL)) { @@ -290,7 +290,7 @@ namespace asmio::arm { } const uint16_t sf = dst.wide() ? 1 : 0; - put_dword(sf << 31 | 0b00'11010100 << 21 | falsy.reg << 16 | uint32_t(condition) << 12 | increment_truth << 10 | truthy.reg << 5 | dst.reg); + put_dword(sf << 31 | invert << 30 | 0b11010100 << 21 | falsy.reg << 16 | uint32_t(condition) << 12 | increment << 10 | truthy.reg << 5 | dst.reg); } void BufferWriter::put_inst_add_imm(Registry dst, Registry src, uint16_t imm12, bool lsl_12, bool set_flags, bool subtract) { diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 650dbb8..4e1dc72 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -86,7 +86,7 @@ namespace asmio::arm { void put_inst_shift_v(Registry dst, Registry src, Registry bits, ShiftType shift); /// Encode "CSINC/CSEL/CSET/CINC" operation - void put_inst_csinc(Condition condition, Registry dst, Registry truthy, Registry falsy, bool increment_truth); + void put_inst_csinc(Condition condition, Registry dst, Registry truthy, Registry falsy, bool increment, bool invert); /// Encode "CAS/CAB/CAH" operations void put_inst_cas(Registry dst, Registry src, Registry cmp, Order order, uint8_t size); @@ -218,6 +218,11 @@ namespace asmio::arm { INST put_ccmp(Condition condition, Condition flags, Registry reg, Registry val); ///< Conditional compare INST put_ccmn(Condition condition, Condition flags, Registry reg, uint8_t imm5); ///< Conditional compare negative INST put_ccmn(Condition condition, Condition flags, Registry reg, Registry val); ///< Conditional compare negative + INST put_csinv(Condition condition, Registry dst, Registry truthy, Registry falsy); ///< Conditional select truthy or invert falsy + INST put_cinv(Condition condition, Registry dst, Registry src); ///< Conditional select or invert + INST put_csetm(Condition condition, Registry dst); ///< Conditional set mask + INST put_csneg(Condition condition, Registry dst, Registry truthy, Registry falsy); ///< Conditional select truthy or negate falsy + INST put_cneg(Condition condition, Registry dst, Registry src); ///< Conditional select or negate // large system extension INST put_swpb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap byte from 'src' to memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index b1c8968..4c50ff7 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -459,11 +459,11 @@ namespace asmio::arm { } void BufferWriter::put_csel(Condition condition, Registry dst, Registry truthy, Registry falsy) { - put_inst_csinc(condition, dst, truthy, falsy, false); + put_inst_csinc(condition, dst, truthy, falsy, false, false); } void BufferWriter::put_csinc(Condition condition, Registry dst, Registry truthy, Registry falsy) { - put_inst_csinc(condition, dst, truthy, falsy, true); + put_inst_csinc(condition, dst, truthy, falsy, true, false); } void BufferWriter::put_cinc(Condition condition, Registry dst, Registry src) { @@ -745,6 +745,27 @@ namespace asmio::arm { put_dword(val.wide() << 31 | 0b01'11010010 << 21 | second.reg << 16 | uint32_t(condition) << 12 | 0b00 << 10 | val.reg << 5 | uint32_t(flags)); } + void BufferWriter::put_csinv(Condition condition, Registry dst, Registry truthy, Registry falsy) { + put_inst_csinc(condition, dst, truthy, falsy, false, true); + } + + void BufferWriter::put_cinv(Condition condition, Registry dst, Registry src) { + put_inst_csinc(invert(condition), dst, src, src, false, true); + } + + void BufferWriter::put_csetm(Condition condition, Registry dst) { + Registry zero = dst.wide() ? XZR : WZR; + put_inst_csinc(invert(condition), dst, zero, zero, false, true); + } + + void BufferWriter::put_csneg(Condition condition, Registry dst, Registry truthy, Registry falsy) { + put_inst_csinc(condition, dst, truthy, falsy, true, true); + } + + void BufferWriter::put_cneg(Condition condition, Registry dst, Registry src) { + put_inst_csinc(condition, dst, src, src, true, true); + } + void BufferWriter::put_inst_ldop(Registry val, Registry dst, Registry src, Order order, uint8_t size, uint32_t opc) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 1d0369a..858ac66 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -457,6 +457,24 @@ namespace test { }; + TEST (writer_check_csinv_cinv_csetm) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.put_csinv(Condition::PL, X0, X2, X3); + writer.put_csinc(Condition::GE, X6, X7, X8); + writer.put_cinv(Condition::HI, X0, X6); + writer.put_csetm(Condition::LE, X3); + writer.put_csneg(Condition::LE, X0, X2, X3); + writer.put_cneg(Condition::LE, X0, X11); + + segmented.link(0); + std::vector s0 = {0x40, 0x50, 0x83, 0xda, 0xe6, 0xa4, 0x88, 0x9a, 0xc0, 0x90, 0x86, 0xda, 0xe3, 0xc3, 0x9f, 0xda, 0x40, 0xd4, 0x83, 0xda, 0x60, 0xd5, 0x8b, 0xda}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From 8c7868c99fed9d688cc15392b17d6e75f727be11 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 26 Apr 2026 19:34:37 +0200 Subject: [PATCH 35/44] AArch64 CLREX instruction --- src/asmio/aarch64/writer.hpp | 1 + src/asmio/aarch64/writer_basic.cpp | 4 ++++ test/aarch64.cpp | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 4e1dc72..d7bf4c3 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -257,6 +257,7 @@ namespace asmio::arm { INST put_ldumin(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword // control + INST put_clrex(uint8_t imm4 = 15); ///< This instruction clears the local monitor of the executing PE. INST put_svc(uint16_t imm16); ///< Supervisor call INST put_hvc(uint16_t imm16); ///< Hypervisor Call INST put_smc(uint16_t imm16); ///< Secure Monitor Call diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 4c50ff7..d15ae65 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -239,6 +239,10 @@ namespace asmio::arm { put_inst_shifted_register(0b0101010, 0, dst, a, b, imm6, shift); } + void BufferWriter::put_clrex(uint8_t imm4) { + put_dword(0b110'101'01000000110011 << 12 | (imm4 & 0b1111) << 8 | 0b010'11111); + } + void BufferWriter::put_svc(uint16_t imm16) { put_dword(0b11010100000 << 21 | imm16 << 5 | 0b00001); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 858ac66..3ca3088 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -349,11 +349,12 @@ namespace test { segmented.elf_machine = ElfMachine::AARCH64; writer.put_eret(); + writer.put_clrex(); writer.put_hlt(0x42); writer.put_hvc(0x69); segmented.link(0); - std::vector s0 = {0xe0, 0x03, 0x9f, 0xd6, 0x40, 0x08, 0x40, 0xd4, 0x22, 0x0d, 0x00, 0xd4}; + std::vector s0 = {0xe0, 0x03, 0x9f, 0xd6, 0x5f, 0x3f, 0x03, 0xd5, 0x40, 0x08, 0x40, 0xd4, 0x22, 0x0d, 0x00, 0xd4}; CHECK(segmented.segments()[0].buffer, s0); // .rwx }; From a24cc1b9be3f69c431967874c4effacdb18affd5 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sun, 26 Apr 2026 19:50:49 +0200 Subject: [PATCH 36/44] AArch64 immediate CMP/CMN --- src/asmio/aarch64/writer.cpp | 8 ++++++-- src/asmio/aarch64/writer.hpp | 4 ++++ src/asmio/aarch64/writer_basic.cpp | 20 ++++++++++++++++++++ test/aarch64.cpp | 16 ++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/asmio/aarch64/writer.cpp b/src/asmio/aarch64/writer.cpp index 3b2931e..14ef329 100644 --- a/src/asmio/aarch64/writer.cpp +++ b/src/asmio/aarch64/writer.cpp @@ -302,8 +302,12 @@ namespace asmio::arm { throw std::runtime_error {"Invalid operands, all given registers need to be of the same width"}; } - if (dst.is(Registry::ZERO) || src.is(Registry::ZERO)) { - throw std::runtime_error {"Invalid operands, zero register can't be used here"}; + if (src.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, zero register can't be used as source"}; + } + + if (!dst.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operand, destination must be a general purpose register"}; } uint32_t sf = dst.wide() ? 1 : 0; diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index d7bf4c3..300e251 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -223,6 +223,10 @@ namespace asmio::arm { INST put_csetm(Condition condition, Registry dst); ///< Conditional set mask INST put_csneg(Condition condition, Registry dst, Registry truthy, Registry falsy); ///< Conditional select truthy or negate falsy INST put_cneg(Condition condition, Registry dst, Registry src); ///< Conditional select or negate + INST put_cmn(Registry src, uint16_t imm12, bool shift_12 = false); ///< Compare negative with immediate + INST put_cmn(Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Compare negative with shifted register + INST put_cmp(Registry src, uint16_t imm12, bool shift_12 = false); ///< Compare with immediate + INST put_cmp(Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Compare with shifted register // large system extension INST put_swpb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap byte from 'src' to memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index d15ae65..594590e 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -770,6 +770,26 @@ namespace asmio::arm { put_inst_csinc(condition, dst, src, src, true, true); } + void BufferWriter::put_cmn(Registry src, uint16_t imm12, bool shift_12) { + Registry zero = src.wide() ? XZR : WZR; + put_adds(zero, src, imm12, shift_12); + } + + void BufferWriter::put_cmn(Registry a, Registry b, ShiftType shift, uint8_t imm6) { + Registry zero = a.wide() ? XZR : WZR; + put_adds(zero, a, b, shift, imm6); + } + + void BufferWriter::put_cmp(Registry src, uint16_t imm12, bool shift_12) { + Registry zero = src.wide() ? XZR : WZR; + put_subs(zero, src, imm12, shift_12); + } + + void BufferWriter::put_cmp(Registry a, Registry b, ShiftType shift, uint8_t imm6) { + Registry zero = a.wide() ? XZR : WZR; + put_subs(zero, a, b, shift, imm6); + } + void BufferWriter::put_inst_ldop(Registry val, Registry dst, Registry src, Order order, uint8_t size, uint32_t opc) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 3ca3088..15564bc 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -476,6 +476,22 @@ namespace test { }; + TEST (writer_check_cmn_cmp) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.put_cmn(X2, 0x7b); + writer.put_cmn(X6, X7, ShiftType::LSL, 8); + writer.put_cmp(X2, 0x1a4); + writer.put_cmp(X6, X7, ShiftType::LSL, 8); + + segmented.link(0); + std::vector s0 = {0x5f, 0xec, 0x01, 0xb1, 0xdf, 0x20, 0x07, 0xab, 0x5f, 0x90, 0x06, 0xf1, 0xdf, 0x20, 0x07, 0xeb}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From 5a48d00cd6bcc5a2af305c3844279fdfabfa4be9 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Mon, 27 Apr 2026 20:48:12 +0200 Subject: [PATCH 37/44] AArch64 EON/LDAXP instructions --- src/asmio/aarch64/writer.hpp | 2 ++ src/asmio/aarch64/writer_basic.cpp | 24 ++++++++++++++++++++++++ test/aarch64.cpp | 14 ++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 300e251..2b8b6c0 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -145,6 +145,7 @@ namespace asmio::arm { INST put_and(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise AND between two register, shifting the second one INST put_eor(Registry destination, Registry source, BitPattern pattern); ///< Bitwise XOR between register and bit pattern INST put_eor(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise XOR between two register, shifting the second one + INST put_eon(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise XOR NOT between two register, shifting the second one INST put_orr(Registry destination, Registry source, BitPattern pattern); ///< Bitwise OR between register and bit pattern INST put_orr(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise OR between two register, shifting the second one INST put_sbc(Registry dst, Registry a, Registry b); ///< Subtract with Carry @@ -227,6 +228,7 @@ namespace asmio::arm { INST put_cmn(Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Compare negative with shifted register INST put_cmp(Registry src, uint16_t imm12, bool shift_12 = false); ///< Compare with immediate INST put_cmp(Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Compare with shifted register + INST put_ldaxp(Registry r1, Registry r2, Registry src); ///< Load-acquire exclusive pair of registers // large system extension INST put_swpb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap byte from 'src' to memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 594590e..75206b1 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -225,6 +225,10 @@ namespace asmio::arm { put_inst_shifted_register(0b1001010, 0, dst, a, b, imm6, shift); } + void BufferWriter::put_eon(Registry dst, Registry a, Registry b, ShiftType shift, uint8_t imm6) { + put_inst_shifted_register(0b1001010, 1, dst, a, b, imm6, shift); + } + void BufferWriter::put_orr(Registry destination, Registry source, BitPattern pattern) { // destination can be SP @@ -790,6 +794,26 @@ namespace asmio::arm { put_subs(zero, a, b, shift, imm6); } + void BufferWriter::put_ldaxp(Registry r1, Registry r2, Registry src) { + if (!src.wide()) { + throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; + } + + if (src.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, source can't be the zero register"}; + } + + if (!r1.is(Registry::GENERAL) || !r2.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operand, destinations must be general purpose registers"}; + } + + if (r1.wide() != r2.wide()) { + throw std::runtime_error {"Invalid operand, destinations must be of the same size"}; + } + + put_dword(1 << 31 | r1.wide() << 30 | 0b0010000'11'11111'1 << 15 | r2.reg << 10 | src.reg << 5 | r1.reg); + } + void BufferWriter::put_inst_ldop(Registry val, Registry dst, Registry src, Order order, uint8_t size, uint32_t opc) { if (!src.wide()) { throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 15564bc..86c49a7 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -492,6 +492,20 @@ namespace test { }; + TEST (writer_check_eon_ldaxp) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.put_eon(X2, X3, X4, ShiftType::LSL, 3); + writer.put_ldaxp(X2, X3, X4); + + segmented.link(0); + std::vector s0 = {0x62, 0x0c, 0x24, 0xca, 0x82, 0x8c, 0x7f, 0xc8}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From 200c87405630f361e8d5d274732c5de5957b45b1 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Tue, 28 Apr 2026 17:15:42 +0200 Subject: [PATCH 38/44] AArch64 exclusive load/stores --- src/asmio/aarch64/writer.cpp | 22 +++++++ src/asmio/aarch64/writer.hpp | 23 +++++++- src/asmio/aarch64/writer_basic.cpp | 92 ++++++++++++++++++------------ test/aarch64.cpp | 24 +++++++- 4 files changed, 120 insertions(+), 41 deletions(-) diff --git a/src/asmio/aarch64/writer.cpp b/src/asmio/aarch64/writer.cpp index 14ef329..45c156c 100644 --- a/src/asmio/aarch64/writer.cpp +++ b/src/asmio/aarch64/writer.cpp @@ -329,4 +329,26 @@ namespace asmio::arm { put_dword(sf << 31 | subtract << 30 | set_flags << 29 | 0b0'0'01011 << 24 | uint8_t(shift) << 22 | b.reg << 16 | (imm6 & 0b111111) << 10 | a.reg << 5 | destination.reg); } + void BufferWriter::put_inst_ldstx(Registry r1, Registry r2, Registry status, Registry mem, uint8_t size, bool load, bool barrier, bool pair) { + if (!mem.wide()) { + throw std::runtime_error {"Invalid operand, source register must be wide"}; + } + + if (mem.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, source can't be the zero register"}; + } + + if (pair) { + if (r1.wide() != r2.wide()) { + throw std::runtime_error {"Invalid operands, both destination registers need to be of the same size"}; + } + + if (!r1.is(Registry::GENERAL) || !r2.is(Registry::GENERAL)) { + throw std::runtime_error {"Invalid operands, both destination registers need to be general purpose"}; + } + } + + put_dword(size << 30 | 0b0010000 << 23 | load << 22 | pair << 21 | status.reg << 16 | barrier << 15 | r2.reg << 10 | mem.reg << 5 | r1.reg); + } + } \ No newline at end of file diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 2b8b6c0..5e67cf5 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -106,6 +106,9 @@ namespace asmio::arm { /// Encode "ADD/ADDS/SUB/SUBS (shifted register)" operation void put_inst_add_shifted(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6, bool set_flags, bool subtract); + /// Encode exclusive load/store operations + void put_inst_ldstx(Registry r1, Registry r2, Registry status, Registry mem, uint8_t size, bool load, bool barrier, bool pair); + public: BufferWriter(SegmentedBuffer& buffer); @@ -210,7 +213,6 @@ namespace asmio::arm { INST put_ildpsw(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and load sign-extended dword pair from memory INST put_ldpswi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Load sign-extended dword pair from memory and increment src INST put_ldnp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Load a non-temporal Pair of Registers - INST put_ldxp(Registry r1, Registry r2, Registry src); ///< Load exclusive pair of registers INST put_stp(Registry r1, Registry r2, Registry src, int64_t offset = 0); ///< Store a Pair of Registers INST put_istp(Registry r1, Registry r2, Registry src, int64_t offset); ///< Increment src and store value pair to memory INST put_stpi(Registry r1, Registry r2, Registry src, int64_t offset); ///< Store value pair to memory and increment src @@ -221,14 +223,29 @@ namespace asmio::arm { INST put_ccmn(Condition condition, Condition flags, Registry reg, Registry val); ///< Conditional compare negative INST put_csinv(Condition condition, Registry dst, Registry truthy, Registry falsy); ///< Conditional select truthy or invert falsy INST put_cinv(Condition condition, Registry dst, Registry src); ///< Conditional select or invert - INST put_csetm(Condition condition, Registry dst); ///< Conditional set mask + INST put_csetm(Condition condition, Registry dst); ///< Conditional set mask INST put_csneg(Condition condition, Registry dst, Registry truthy, Registry falsy); ///< Conditional select truthy or negate falsy INST put_cneg(Condition condition, Registry dst, Registry src); ///< Conditional select or negate INST put_cmn(Registry src, uint16_t imm12, bool shift_12 = false); ///< Compare negative with immediate INST put_cmn(Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Compare negative with shifted register INST put_cmp(Registry src, uint16_t imm12, bool shift_12 = false); ///< Compare with immediate INST put_cmp(Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Compare with shifted register - INST put_ldaxp(Registry r1, Registry r2, Registry src); ///< Load-acquire exclusive pair of registers + INST put_ldxp(Registry r1, Registry r2, Registry src); ///< Load exclusive pair of registers + INST put_ldaxp(Registry r1, Registry r2, Registry src); ///< Load-acquire exclusive pair of registers + INST put_stxp(Registry status, Registry r1, Registry r2, Registry src); ///< Store exclusive pair of registers + INST put_stlxp(Registry status, Registry r1, Registry r2, Registry src); ///< Store-release exclusive pair of registers + INST put_ldaxr(Registry dst, Registry src); ///< Load-acquire dword/qword exclusive register + INST put_ldaxrh(Registry dst, Registry src); ///< Load-acquire word exclusive register + INST put_ldaxrb(Registry dst, Registry src); ///< Load-acquire byte exclusive register + INST put_ldxr(Registry dst, Registry src); ///< Load dword/qword exclusive register + INST put_ldxrh(Registry dst, Registry src); ///< Load word exclusive register + INST put_ldxrb(Registry dst, Registry src); ///< Load byte exclusive register + INST put_stlxr(Registry status, Registry dst, Registry src); ///< Store-release dword/qword exclusive register + INST put_stlxrh(Registry status, Registry dst, Registry src); ///< Store-release word exclusive register + INST put_stlxrb(Registry status, Registry dst, Registry src); ///< Store-release byte exclusive register + INST put_stxr(Registry status, Registry dst, Registry src); ///< Store dword/qword exclusive register + INST put_stxrh(Registry status, Registry dst, Registry src); ///< Store word exclusive register + INST put_stxrb(Registry status, Registry dst, Registry src); ///< Store byte exclusive register // large system extension INST put_swpb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap byte from 'src' to memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 75206b1..e29bd82 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -690,28 +690,6 @@ namespace asmio::arm { put_inst_ldpx(r1, r2, src, offset, static_cast(-1), r1.size, true, r1.wide() << 1); } - void BufferWriter::put_ldxp(Registry r1, Registry r2, Registry src) { - - if (!src.wide()) { - throw std::runtime_error {"Invalid operand, source register must be wide"}; - } - - if (src.is(Registry::ZERO)) { - throw std::runtime_error {"Invalid operand, source can't be the zero register"}; - } - - if (r1.wide() != r2.wide()) { - throw std::runtime_error {"Invalid operands, both destination registers need to be of the same size"}; - } - - if (!r1.is(Registry::GENERAL) || !r2.is(Registry::GENERAL)) { - throw std::runtime_error {"Invalid operands, both destination registers need to be general purpose"}; - } - - uint16_t sf = r1.wide() ? 1 : 0; - put_dword(1 << 31 | sf << 30 | 0b0010000'11'11111'0 << 15 | r2.reg << 10 | src.reg << 5 | r1.reg); - } - void BufferWriter::put_stp(Registry r1, Registry r2, Registry src, int64_t offset) { put_inst_ldpx(r1, r2, src, offset, OFFSET, r1.size, false, r1.wide() << 1); } @@ -794,24 +772,68 @@ namespace asmio::arm { put_subs(zero, a, b, shift, imm6); } + void BufferWriter::put_ldxp(Registry r1, Registry r2, Registry src) { + put_inst_ldstx(r1, r2, XZR, src, 0b10 | r1.wide(), true, false, true); + } + void BufferWriter::put_ldaxp(Registry r1, Registry r2, Registry src) { - if (!src.wide()) { - throw std::runtime_error {"Invalid operand, source and destination register must be wide"}; - } + put_inst_ldstx(r1, r2, XZR, src, 0b10 | r1.wide(), true, true, true); + } - if (src.is(Registry::ZERO)) { - throw std::runtime_error {"Invalid operand, source can't be the zero register"}; - } + void BufferWriter::put_ldaxr(Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, XZR, src, 0b10 | dst.wide(), true, true, false); + } - if (!r1.is(Registry::GENERAL) || !r2.is(Registry::GENERAL)) { - throw std::runtime_error {"Invalid operand, destinations must be general purpose registers"}; - } + void BufferWriter::put_ldaxrh(Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, XZR, src, 0b01, true, true, false); + } - if (r1.wide() != r2.wide()) { - throw std::runtime_error {"Invalid operand, destinations must be of the same size"}; - } + void BufferWriter::put_ldaxrb(Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, XZR, src, 0b00, true, true, false); + } + + void BufferWriter::put_ldxr(Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, XZR, src, 0b10 | dst.wide(), true, false, false); + } + + void BufferWriter::put_ldxrh(Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, XZR, src, 0b01, true, false, false); + } + + void BufferWriter::put_ldxrb(Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, XZR, src, 0b00, true, false, false); + } + + void BufferWriter::put_stlxr(Registry status, Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, status, src, 0b10 | dst.wide(), false, true, false); + } + + void BufferWriter::put_stlxrh(Registry status, Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, status, src, 0b01, false, true, false); + } + + void BufferWriter::put_stlxrb(Registry status, Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, status, src, 0b00, false, true, false); + } + + void BufferWriter::put_stxr(Registry status, Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, status, src, 0b10 | dst.wide(), false, false, false); + } + + void BufferWriter::put_stxrh(Registry status, Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, status, src, 0b01, false, false, false); + } + + void BufferWriter::put_stxrb(Registry status, Registry dst, Registry src) { + put_inst_ldstx(dst, XZR, status, src, 0b00, false, false, false); + } + + void BufferWriter::put_stlxp(Registry status, Registry r1, Registry r2, Registry src) { + put_inst_ldstx(r1, r2, status, src, 0b10 | r1.wide(), false, true, true); + } - put_dword(1 << 31 | r1.wide() << 30 | 0b0010000'11'11111'1 << 15 | r2.reg << 10 | src.reg << 5 | r1.reg); + void BufferWriter::put_stxp(Registry status, Registry r1, Registry r2, Registry src) { + put_inst_ldstx(r2, r2, status, src, 0b10 | r1.wide(), false, false, true); } void BufferWriter::put_inst_ldop(Registry val, Registry dst, Registry src, Order order, uint8_t size, uint32_t opc) { diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 86c49a7..c665182 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -492,16 +492,34 @@ namespace test { }; - TEST (writer_check_eon_ldaxp) { + TEST (writer_check_eon) { SegmentedBuffer segmented; BufferWriter writer {segmented}; writer.put_eon(X2, X3, X4, ShiftType::LSL, 3); - writer.put_ldaxp(X2, X3, X4); segmented.link(0); - std::vector s0 = {0x62, 0x0c, 0x24, 0xca, 0x82, 0x8c, 0x7f, 0xc8}; + std::vector s0 = {0x62, 0x0c, 0x24, 0xca}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + + TEST (writer_check_exclusive_ops) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_ldxr(X3, X7); + writer.put_ldxrb(X3, X7); + writer.put_ldaxrh(X3, X7); + writer.put_stlxr(W1, X2, X5); + writer.put_stlxp(W1, X2, X3, X5); + writer.put_stxp(W1, X2, X3, X5); + + segmented.link(0); + std::vector s0 = {0xe3, 0x7c, 0x5f, 0xc8, 0xe3, 0x7c, 0x5f, 0x08, 0xe3, 0xfc, 0x5f, 0x48, 0xa2, 0xfc, 0x01, 0xc8, 0xa2, 0x8c, 0x21, 0xc8, 0xa3, 0x0c, 0x21, 0xc8}; CHECK(segmented.segments()[0].buffer, s0); // .rwx }; From 179561232ece1254d4362b9a51191d7ab48d7de7 Mon Sep 17 00:00:00 2001 From: magistermaks <53909807+magistermaks@users.noreply.github.com> Date: Tue, 28 Apr 2026 17:30:27 +0200 Subject: [PATCH 39/44] CMake bugfix --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca737b9..d3a6b16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ set(ASMIO_WRITERS set(ASMIO_BRIDGES "${ASMIO_WRITERS}") -list(TRANSFORM ASMIO_BRIDGES REPLACE "src/asm/(.*)/writer.hpp" "${PROJECT_BINARY_DIR}/src/generated/\\1.hpp") +list(TRANSFORM ASMIO_BRIDGES REPLACE "src/asmio/(.*)/writer.hpp" "${PROJECT_BINARY_DIR}/src/generated/\\1.hpp") list(TRANSFORM ASMIO_WRITERS PREPEND "${PROJECT_SOURCE_DIR}/") add_custom_command( From 898365bc66d039534427c11e3e3765f2c68f46bd Mon Sep 17 00:00:00 2001 From: magistermaks Date: Tue, 28 Apr 2026 22:07:01 +0200 Subject: [PATCH 40/44] Minor cleanup --- src/asmio/aarch64/argument/condition.hpp | 2 +- src/asmio/aarch64/argument/order.hpp | 19 ++++--------------- src/asmio/aarch64/writer_basic.cpp | 7 ++++++- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/asmio/aarch64/argument/condition.hpp b/src/asmio/aarch64/argument/condition.hpp index d9b5108..6848a5f 100644 --- a/src/asmio/aarch64/argument/condition.hpp +++ b/src/asmio/aarch64/argument/condition.hpp @@ -23,7 +23,7 @@ namespace asmio::arm { */ constexpr Condition invert(Condition condition) { if (condition == Condition::AL) throw std::runtime_error {"The 'always' condition can't be inverted, as there is no 'never' condition!"}; - return (Condition) (uint32_t(condition) ^ 1); + return static_cast(static_cast(condition) ^ 1); } } \ No newline at end of file diff --git a/src/asmio/aarch64/argument/order.hpp b/src/asmio/aarch64/argument/order.hpp index b5c2652..1611abf 100644 --- a/src/asmio/aarch64/argument/order.hpp +++ b/src/asmio/aarch64/argument/order.hpp @@ -7,14 +7,10 @@ namespace asmio::arm { // Specifies ordering semantics of instructions, // learn more about ordering semantics: https://davekilian.com/acquire-release.html enum struct Order : uint8_t { - // L o0 - NONE = 0b00'00000'0, ///< Ensure no ordering of operations - RELEASE = 0b00'00000'1, ///< Ensure that the preceding operations finish before this one starts - ACQUIRE = 0b10'00000'0, ///< Ensure that the following operations wait for this one to finish - ACQUIRE_RELEASE = 0b10'00000'1, ///< Ensure that both previous and preceding operations are executed in order - - // "L" flag is used to mark acquire semantics (marked with suffix "A" in mnemonics), - // and o0 to mark "release" (suffix "L" in mnemonics). How designed it like this?? + NONE = 0b00, ///< Ensure no ordering of operations + RELEASE = 0b01, ///< Ensure that the preceding operations finish before this one starts + ACQUIRE = 0b10, ///< Ensure that the following operations wait for this one to finish + ACQUIRE_RELEASE = 0b11, ///< Ensure that both previous and preceding operations are executed in order }; /** @@ -31,11 +27,4 @@ namespace asmio::arm { return static_cast(order) & static_cast(Order::RELEASE); } - /** - * Convert Order to a bit mask used during instruction encoding - */ - constexpr uint32_t order_to_dword_mask(Order order) { - return static_cast(order) << 15; - } - } \ No newline at end of file diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index e29bd82..74a59a8 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -550,7 +550,12 @@ namespace asmio::arm { throw std::runtime_error {"Invalid operand, expected general purpose source and compare register"}; } - put_dword(size << 30 | order_to_dword_mask(order) | 0b001000'101 << 21 | cmp.reg << 16 | 0b11111 << 10 | ptr.reg << 5 | src.reg); + // "L" flag is used to mark acquire semantics (marked with suffix "A" in mnemonics), + // and o0 to mark "release" (suffix "L" in mnemonics). How designed it like this?? + const uint32_t lf = is_order_acquire(order) ? (1 << 22) : 0; + const uint32_t of = is_order_release(order) ? (1 << 15) : 0; + + put_dword(size << 30 | lf | of | 0b001000'101 << 21 | cmp.reg << 16 | 0b11111 << 10 | ptr.reg << 5 | src.reg); } void BufferWriter::put_swpb(Registry val, Registry dst, Registry src, Order order) { From d13c8100e921f0e543dea53edaf18bdbcc07573b Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sat, 2 May 2026 14:37:46 +0200 Subject: [PATCH 41/44] AArch64 LDTR/STTR/LDUR/STUR instructions --- src/asmio/aarch64/writer.cpp | 16 ++++++++++ src/asmio/aarch64/writer.hpp | 15 ++++++++++ src/asmio/aarch64/writer_basic.cpp | 48 ++++++++++++++++++++++++++++++ test/aarch64.cpp | 37 +++++++++++++++++++++++ 4 files changed, 116 insertions(+) diff --git a/src/asmio/aarch64/writer.cpp b/src/asmio/aarch64/writer.cpp index 45c156c..90e8499 100644 --- a/src/asmio/aarch64/writer.cpp +++ b/src/asmio/aarch64/writer.cpp @@ -351,4 +351,20 @@ namespace asmio::arm { put_dword(size << 30 | 0b0010000 << 23 | load << 22 | pair << 21 | status.reg << 16 | barrier << 15 | r2.reg << 10 | mem.reg << 5 | r1.reg); } + void BufferWriter::put_inst_ldst_simm9(Registry dst, Registry src, int16_t offset, uint32_t size, bool load, uint32_t opc) { + if (!src.wide()) { + throw std::runtime_error {"Invalid operand, source register must be wide"}; + } + + if (src.is(Registry::ZERO)) { + throw std::runtime_error {"Invalid operand, source can't be the zero register"}; + } + + if ((offset < -256) || (offset > 255)) { + throw std::runtime_error {"Invalid operand, offset out of valid range"}; + } + + put_dword(size << 30 | 0b111'0'00'00'0 << 21 | load << 22 | (offset & 0x1ff) << 12 | opc << 10 | src.reg << 5 | dst.reg); + } + } \ No newline at end of file diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 5e67cf5..058329a 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -109,6 +109,9 @@ namespace asmio::arm { /// Encode exclusive load/store operations void put_inst_ldstx(Registry r1, Registry r2, Registry status, Registry mem, uint8_t size, bool load, bool barrier, bool pair); + /// Encode load/store operations with base-destination registers and 9 bit signed offsets + void put_inst_ldst_simm9(Registry dst, Registry src, int16_t offset, uint32_t size, bool load, uint32_t opc); + public: BufferWriter(SegmentedBuffer& buffer); @@ -246,6 +249,18 @@ namespace asmio::arm { INST put_stxr(Registry status, Registry dst, Registry src); ///< Store dword/qword exclusive register INST put_stxrh(Registry status, Registry dst, Registry src); ///< Store word exclusive register INST put_stxrb(Registry status, Registry dst, Registry src); ///< Store byte exclusive register + INST put_ldtr(Registry dst, Registry src, int16_t offset); ///< Load dword/qword register (unprivileged) at unscaled offset + INST put_ldtrh(Registry dst, Registry src, int16_t offset); ///< Load word register (unprivileged) at unscaled offset + INST put_ldtrb(Registry dst, Registry src, int16_t offset); ///< Load byte register (unprivileged) at unscaled offset + INST put_sttr(Registry dst, Registry src, int16_t offset); ///< Store dword/qword register (unprivileged) at unscaled offset + INST put_sttrh(Registry dst, Registry src, int16_t offset); ///< Store word register (unprivileged) at unscaled offset + INST put_sttrb(Registry dst, Registry src, int16_t offset); ///< Store byte register (unprivileged) at unscaled offset + INST put_ldur(Registry dst, Registry src, int16_t offset); ///< Load dword/qword register at unscaled offset + INST put_ldurh(Registry dst, Registry src, int16_t offset); ///< Load word register at unscaled offset + INST put_ldurb(Registry dst, Registry src, int16_t offset); ///< Load byte register at unscaled offset + INST put_stur(Registry dst, Registry src, int16_t offset); ///< Store dword/qword register at unscaled offset + INST put_sturh(Registry dst, Registry src, int16_t offset); ///< Store word register at unscaled offset + INST put_sturb(Registry dst, Registry src, int16_t offset); ///< Store byte register at unscaled offset // large system extension INST put_swpb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap byte from 'src' to memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 74a59a8..5f69d8f 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -833,6 +833,54 @@ namespace asmio::arm { put_inst_ldstx(dst, XZR, status, src, 0b00, false, false, false); } + void BufferWriter::put_ldtr(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b10 | dst.wide(), true, 0b10); + } + + void BufferWriter::put_ldtrh(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b01, true, 0b10); + } + + void BufferWriter::put_ldtrb(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b00, true, 0b10); + } + + void BufferWriter::put_sttr(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b10 | dst.wide(), false, 0b10); + } + + void BufferWriter::put_sttrh(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b01, false, 0b10); + } + + void BufferWriter::put_sttrb(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b00, false, 0b10); + } + + void BufferWriter::put_ldur(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b10 | dst.wide(), true, 0b00); + } + + void BufferWriter::put_ldurh(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b01, true, 0b00); + } + + void BufferWriter::put_ldurb(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b00, true, 0b00); + } + + void BufferWriter::put_stur(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b10 | dst.wide(), false, 0b00); + } + + void BufferWriter::put_sturh(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b01, false, 0b00); + } + + void BufferWriter::put_sturb(Registry dst, Registry src, int16_t offset) { + put_inst_ldst_simm9(dst, src, offset, 0b00, false, 0b00); + } + void BufferWriter::put_stlxp(Registry status, Registry r1, Registry r2, Registry src) { put_inst_ldstx(r1, r2, status, src, 0b10 | r1.wide(), false, true, true); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index c665182..efd4811 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -524,6 +524,43 @@ namespace test { }; + TEST (writer_check_ldtr_sttr_ldur_stur) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_ldtr(X0, X11, 123); + writer.put_ldtrh(X1, X10, -123); + writer.put_ldtrb(X2, X9, -123); + writer.put_ldur(W3, X8, 123); + writer.put_ldurh(X4, X7, 123); + writer.put_ldurb(X5, X6, 123); + writer.put_sttr(X0, X11, 123); + writer.put_sttrh(X1, X10, 123); + writer.put_sttrb(X2, X9, 123); + writer.put_stur(X3, X8, 123); + writer.put_sturh(X4, X7, 123); + writer.put_sturb(X5, X6, 123); + + EXPECT_THROW(std::runtime_error) { + writer.put_ldtr(X0, X1, 256); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldtr(X0, XZR, 6); + }; + + EXPECT_THROW(std::runtime_error) { + writer.put_ldtr(X0, W2, 6); + }; + + segmented.link(0); + std::vector s0 = {0x60, 0xb9, 0x47, 0xf8, 0x41, 0x59, 0x58, 0x78, 0x22, 0x59, 0x58, 0x38, 0x03, 0xb1, 0x47, 0xb8, 0xe4, 0xb0, 0x47, 0x78, 0xc5, 0xb0, 0x47, 0x38, 0x60, 0xb9, 0x07, 0xf8, 0x41, 0xb9, 0x07, 0x78, 0x22, 0xb9, 0x07, 0x38, 0x03, 0xb1, 0x07, 0xf8, 0xe4, 0xb0, 0x07, 0x78, 0xc5, 0xb0, 0x07, 0x38}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From c748b1c0ccd0fe60685c7504f87ff8c48c8b3203 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sat, 2 May 2026 19:07:07 +0200 Subject: [PATCH 42/44] More AArch64 instruction aliases --- src/asmio/aarch64/writer.cpp | 14 --- src/asmio/aarch64/writer.hpp | 44 ++++++++- src/asmio/aarch64/writer_basic.cpp | 144 ++++++++++++++++++++++++++++- test/aarch64.cpp | 60 +++++++++++- test/test.hpp | 2 +- 5 files changed, 241 insertions(+), 23 deletions(-) diff --git a/src/asmio/aarch64/writer.cpp b/src/asmio/aarch64/writer.cpp index 90e8499..366cc3c 100644 --- a/src/asmio/aarch64/writer.cpp +++ b/src/asmio/aarch64/writer.cpp @@ -66,20 +66,6 @@ namespace asmio::arm { put_dword(sf << 31 | opc << 23 | hw << 21 | imm << 5 | registry.reg); } - void BufferWriter::put_inst_orr_bitmask(Registry destination, Registry source, uint16_t n_immr_imms) { - - // destination can be SP - if (!source.is(Registry::GENERAL)) { - throw std::runtime_error {"Invalid operand, expected source to be a general purpose register."}; - } - - if (destination.wide() != source.wide()) { - throw std::runtime_error {"Invalid operands, all given registers need to be of the same width."}; - } - - put_inst_bitmask_immediate(0b01100100, destination, source, n_immr_imms); - } - void BufferWriter::put_inst_extended_register(uint32_t opcode_from_21, Registry destination, Registry a, Registry b, Sizing add, uint8_t imm3, bool set_flags) { // we can only accept SP as destination IF set_flags is false diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 058329a..28aea8a 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -52,9 +52,6 @@ namespace asmio::arm { /// Encode generic, 16 bit, immediate move, used by MOVN, MOVK, MOVZ void put_inst_mov(Registry registry, uint16_t opc, uint16_t imm, uint16_t shift); - /// Encode ORR instruction, using the given N:R:S fields - void put_inst_orr_bitmask(Registry destination, Registry source, uint16_t n_immr_imms); - /// Encode "ADC/ADCS (extended register)" operation void put_inst_adc(Registry destination, Registry a, Registry b, bool set_flags); @@ -132,6 +129,12 @@ namespace asmio::arm { INST put_movn(Registry dst, uint16_t imm, uint16_t shift = 0); ///< Move shifted WORD into register, zero other bits, then NOT the register INST put_mov(Registry dst, uint64_t imm); ///< Move immediate into register INST put_mov(Registry dst, Registry src); ///< Move value between registers + INST put_movn(Registry dst, Registry src); ///< Move inverted value between registers + INST put_mov(Registry dst, BitPattern pattern); ///< Move BitPattern encoded immediate to a register + INST put_neg(Registry dst, Registry src, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Negate + INST put_negs(Registry dst, Registry src, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Negate, setting flags + INST put_ngc(Registry dst, Registry src); ///< Negate with carry + INST put_ngcs(Registry dst, Registry src); ///< Negate with carry, setting flags INST put_ret(); ///< Return from procedure using link register INST put_eret(); ///< Exception return INST put_ret(Registry src); ///< Return from procedure @@ -154,14 +157,15 @@ namespace asmio::arm { INST put_eon(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise XOR NOT between two register, shifting the second one INST put_orr(Registry destination, Registry source, BitPattern pattern); ///< Bitwise OR between register and bit pattern INST put_orr(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise OR between two register, shifting the second one + INST put_orn(Registry dst, Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Bitwise OR NOT between two register, shifting the second one INST put_sbc(Registry dst, Registry a, Registry b); ///< Subtract with Carry INST put_sbcs(Registry dst, Registry a, Registry b); ///< Subtract with Carry and set flags INST put_sub(Registry dst, Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Add two registers, potentially extending one of them INST put_subs(Registry dst, Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Add two registers, set the flags, potentially extending one of them INST put_sub(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Subtract 12bit, unsigned immediate (optionally shifted by 12 bits) to a register INST put_subs(Registry dst, Registry src, uint16_t imm12, bool shift_12 = false); ///< Subtract with Carry 12bit, unsigned immediate (optionally shifted by 12 bits) to a register - INST put_sub(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Add an optionally-shifted register - INST put_subs(Registry destination, Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Add, with Carry, an optionally-shifted register + INST put_sub(Registry dst, Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Add an optionally-shifted register + INST put_subs(Registry dst, Registry a, Registry b, ShiftType shift, uint8_t imm6); ///< Add, with Carry, an optionally-shifted register INST put_cmp(Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Compare INST put_cmn(Registry a, Registry b, Sizing size = Sizing::UX, uint8_t lsl3 = 0); ///< Compare negative INST put_madd(Registry dst, Registry a, Registry b, Registry addend); ///< Multiply and Add 64 bit registers @@ -197,6 +201,7 @@ namespace asmio::arm { INST put_cinc(Condition condition, Registry dst, Registry src);///< Conditional Increment if true INST put_cinc(Condition condition, Registry dst); ///< Conditional Increment if true INST put_cset(Condition condition, Registry dst); ///< Conditional Set if true + INST put_tst(Registry reg, BitPattern pattern); ///< Test bits (immediate) INST put_tst(Registry a, Registry b, ShiftType shift = ShiftType::LSL, uint8_t lsl6 = 0); ///< Test shifted register INST put_sbfm(Registry dst, Registry src, BitPattern pattern); ///< Signed Bitfield Insert in Zero INST put_ubfm(Registry dst, Registry src, BitPattern pattern); ///< Unsigned Bitfield Insert in Zero @@ -261,6 +266,9 @@ namespace asmio::arm { INST put_stur(Registry dst, Registry src, int16_t offset); ///< Store dword/qword register at unscaled offset INST put_sturh(Registry dst, Registry src, int16_t offset); ///< Store word register at unscaled offset INST put_sturb(Registry dst, Registry src, int16_t offset); ///< Store byte register at unscaled offset + INST put_sxtw(Registry dst, Registry src); ///< Sign extend dword + INST put_sxth(Registry dst, Registry src); ///< Sign extend word + INST put_sxtb(Registry dst, Registry src); ///< Sign extend byte // large system extension INST put_swpb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap byte from 'src' to memory @@ -294,6 +302,32 @@ namespace asmio::arm { INST put_lduminh(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on word INST put_ldumin(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword + // large system extension aliases + INST put_staddb(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic add on byte + INST put_staddh(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic add on word + INST put_stadd(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic add on dword or qword + INST put_stclrb(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bit clear on byte + INST put_stclrh(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bit clear on word + INST put_stclr(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bit clear on dword or qword + INST put_steorb(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bitwise xor on byte + INST put_steorh(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bitwise xor on word + INST put_steor(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bitwise xor on dword or qword + INST put_stsetb(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bit set on byte + INST put_stseth(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bit set on word + INST put_stset(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic bit set on dword or qword + INST put_stsmaxb(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on byte + INST put_stsmaxh(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on word + INST put_stsmax(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword + INST put_stumaxb(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on byte + INST put_stumaxh(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on word + INST put_stumax(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword + INST put_stsminb(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on byte + INST put_stsminh(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on word + INST put_stsmin(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword + INST put_stuminb(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on byte + INST put_stuminh(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on word + INST put_stumin(Registry val, Registry ptr, Order order = Order::NONE); ///< Atomic signed maximum on dword or qword + // control INST put_clrex(uint8_t imm4 = 15); ///< This instruction clears the local monitor of the executing PE. INST put_svc(uint16_t imm16); ///< Supervisor call diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index 5f69d8f..bf1c9e0 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -82,7 +82,7 @@ namespace asmio::arm { const auto nrs = BitPattern::try_pack(imm); if (nrs.ok()) { - return put_orr(dst, dst.wide() ? XZR : WZR, nrs); + return put_mov(dst, nrs); } const size_t length = dst.wide() ? 64 : 32; @@ -107,7 +107,7 @@ namespace asmio::arm { // when dealing with SP zero can't be used if (src.is(Registry::ZERO) || dst.is(Registry::ZERO)) { - throw std::runtime_error {"Invalid operands, zero registry can't be used int this context"}; + throw std::runtime_error {"Invalid operands, zero registry can't be used in this context"}; } put_add(dst, src, XZR); @@ -117,6 +117,30 @@ namespace asmio::arm { put_orr(dst, src, dst.wide() ? XZR : WZR); } + void BufferWriter::put_movn(Registry dst, Registry src) { + put_orn(dst, dst.wide() ? XZR : WZR, src); + } + + void BufferWriter::put_mov(Registry dst, BitPattern pattern) { + put_orr(dst, dst.wide() ? XZR : WZR, pattern); + } + + void BufferWriter::put_neg(Registry dst, Registry src, ShiftType shift, uint8_t imm6) { + put_sub(dst, dst.wide() ? XZR : WZR, src, shift, imm6); + } + + void BufferWriter::put_negs(Registry dst, Registry src, ShiftType shift, uint8_t imm6) { + put_subs(dst, dst.wide() ? XZR : WZR, src, shift, imm6); + } + + void BufferWriter::put_ngc(Registry dst, Registry src) { + put_sbc(dst, dst.wide() ? XZR : WZR, src); + } + + void BufferWriter::put_ngcs(Registry dst, Registry src) { + put_sbcs(dst, dst.wide() ? XZR : WZR, src); + } + void BufferWriter::put_ret() { put_ret(LR); } @@ -243,6 +267,10 @@ namespace asmio::arm { put_inst_shifted_register(0b0101010, 0, dst, a, b, imm6, shift); } + void BufferWriter::put_orn(Registry dst, Registry a, Registry b, ShiftType shift, uint8_t imm6) { + put_inst_shifted_register(0b0101010, 1, dst, a, b, imm6, shift); + } + void BufferWriter::put_clrex(uint8_t imm4) { put_dword(0b110'101'01000000110011 << 12 | (imm4 & 0b1111) << 8 | 0b010'11111); } @@ -486,6 +514,10 @@ namespace asmio::arm { put_cinc(condition, dst, dst.wide() ? XZR : WZR); } + void BufferWriter::put_tst(Registry reg, BitPattern pattern) { + put_ands(reg.wide() ? XZR : WZR, reg, pattern); + } + void BufferWriter::put_tst(Registry a, Registry b, ShiftType shift, uint8_t lsl6) { put_ands(a.wide() ? XZR : WZR, a, b, shift, lsl6); } @@ -881,6 +913,18 @@ namespace asmio::arm { put_inst_ldst_simm9(dst, src, offset, 0b00, false, 0b00); } + void BufferWriter::put_sxtw(Registry dst, Registry src) { + put_sbfm(dst, src, BitPattern::try_pack(0xffffffff)); + } + + void BufferWriter::put_sxth(Registry dst, Registry src) { + put_sbfm(dst, src, BitPattern::try_pack(0xffff)); + } + + void BufferWriter::put_sxtb(Registry dst, Registry src) { + put_sbfm(dst, src, BitPattern::try_pack(0xff)); + } + void BufferWriter::put_stlxp(Registry status, Registry r1, Registry r2, Registry src) { put_inst_ldstx(r1, r2, status, src, 0b10 | r1.wide(), false, true, true); } @@ -1035,6 +1079,102 @@ namespace asmio::arm { put_inst_ldop(val, dst, src, order, 0b10 | val.wide(), 0b111); } + void BufferWriter::put_staddb(Registry val, Registry ptr, Order order) { + put_ldaddb(val, WZR, ptr, order); + } + + void BufferWriter::put_staddh(Registry val, Registry ptr, Order order) { + put_ldaddh(val, WZR, ptr, order); + } + + void BufferWriter::put_stadd(Registry val, Registry ptr, Order order) { + put_ldadd(val, val.wide() ? XZR : WZR, ptr, order); + } + + void BufferWriter::put_stclrb(Registry val, Registry ptr, Order order) { + put_ldclrb(val, WZR, ptr, order); + } + + void BufferWriter::put_stclrh(Registry val, Registry ptr, Order order) { + put_ldclrh(val, WZR, ptr, order); + } + + void BufferWriter::put_stclr(Registry val, Registry ptr, Order order) { + put_ldclr(val, val.wide() ? XZR : WZR, ptr, order); + } + + void BufferWriter::put_steorb(Registry val, Registry ptr, Order order) { + put_ldeorb(val, WZR, ptr, order); + } + + void BufferWriter::put_steorh(Registry val, Registry ptr, Order order) { + put_ldeorh(val, WZR, ptr, order); + } + + void BufferWriter::put_steor(Registry val, Registry ptr, Order order) { + put_ldeor(val, val.wide() ? XZR : WZR, ptr, order); + } + + void BufferWriter::put_stsetb(Registry val, Registry ptr, Order order) { + put_ldsetb(val, WZR, ptr, order); + } + + void BufferWriter::put_stseth(Registry val, Registry ptr, Order order) { + put_ldseth(val, WZR, ptr, order); + } + + void BufferWriter::put_stset(Registry val, Registry ptr, Order order) { + put_ldset(val, val.wide() ? XZR : WZR, ptr, order); + } + + void BufferWriter::put_stsmaxb(Registry val, Registry ptr, Order order) { + put_ldsmaxb(val, WZR, ptr, order); + } + + void BufferWriter::put_stsmaxh(Registry val, Registry ptr, Order order) { + put_ldsmaxh(val, WZR, ptr, order); + } + + void BufferWriter::put_stsmax(Registry val, Registry ptr, Order order) { + put_ldsmax(val, val.wide() ? XZR : WZR, ptr, order); + } + + void BufferWriter::put_stumaxb(Registry val, Registry ptr, Order order) { + put_ldumaxb(val, WZR, ptr, order); + } + + void BufferWriter::put_stumaxh(Registry val, Registry ptr, Order order) { + put_ldumaxh(val, WZR, ptr, order); + } + + void BufferWriter::put_stumax(Registry val, Registry ptr, Order order) { + put_ldumax(val, val.wide() ? XZR : WZR, ptr, order); + } + + void BufferWriter::put_stsminb(Registry val, Registry ptr, Order order) { + put_ldsminb(val, WZR, ptr, order); + } + + void BufferWriter::put_stsminh(Registry val, Registry ptr, Order order) { + put_ldsminh(val, WZR, ptr, order); + } + + void BufferWriter::put_stsmin(Registry val, Registry ptr, Order order) { + put_ldsmin(val, val.wide() ? XZR : WZR, ptr, order); + } + + void BufferWriter::put_stuminb(Registry val, Registry ptr, Order order) { + put_lduminb(val, WZR, ptr, order); + } + + void BufferWriter::put_stuminh(Registry val, Registry ptr, Order order) { + put_lduminh(val, WZR, ptr, order); + } + + void BufferWriter::put_stumin(Registry val, Registry ptr, Order order) { + put_ldumin(val, val.wide() ? XZR : WZR, ptr, order); + } + void BufferWriter::put_hint(uint8_t imm7) { put_dword(0b1101010100'0'00'011'0010 << 12 | (0b1111'111 & imm7) << 5 | 0b11111); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index efd4811..346d83e 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -9,7 +9,6 @@ #include "vstl.hpp" namespace test { - using namespace asmio; using namespace asmio::arm; @@ -45,11 +44,20 @@ namespace test { SegmentedBuffer segmented; BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_orr(X1, X2, X3); + writer.put_orn(X1, X2, X3); + writer.put_movn(X12, X11); EXPECT_THROW(std::runtime_error) { writer.put_orr(W(0), W(0), X(0)); }; + segmented.link(0); + std::vector s0 = {0x41, 0x00, 0x03, 0xaa, 0x41, 0x00, 0x23, 0xaa, 0xec, 0x03, 0x2b, 0xaa}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + }; TEST (writer_fail_orr_imm_invalid) { @@ -561,6 +569,42 @@ namespace test { }; + TEST (writer_check_neg_ngc) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_neg(X1, X7); + writer.put_negs(X2, X8); + writer.put_ngc(X3, X9); + writer.put_ngcs(X4, X10); + + writer.put_neg(X5, X11, ShiftType::LSL, 4); + writer.put_negs(X6, X12, ShiftType::LSL, 4); + + segmented.link(0); + std::vector s0 = {0xe1, 0x03, 0x07, 0xcb, 0xe2, 0x03, 0x08, 0xeb, 0xe3, 0x03, 0x09, 0xda, 0xe4, 0x03, 0x0a, 0xfa, 0xe5, 0x13, 0x0b, 0xcb, 0xe6, 0x13, 0x0c, 0xeb}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + + TEST (writer_check_sxt) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_sxtb(X1, X7); + writer.put_sxth(X2, X8); + writer.put_sxtw(X2, X9); + + segmented.link(0); + std::vector s0 = {0xe1, 0x1c, 0x40, 0x93, 0x02, 0x3d, 0x40, 0x93, 0x22, 0x7d, 0x40, 0x93}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM @@ -2281,6 +2325,20 @@ namespace test { }; + TEST (writer_exec_sxtb) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + + writer.put_mov(X1, 0xff); + writer.put_sxtb(X0, X1); + writer.put_ret(); + + auto exec = to_executable(segmented); + CHECK(exec.call_i64(), -1); + + }; + #endif } \ No newline at end of file diff --git a/test/test.hpp b/test/test.hpp index 89472ec..962f676 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -14,7 +14,7 @@ namespace test { std::string extra_flags = ""; if (buffer.elf_machine == asmio::ElfMachine::X86_64) { - extra_flags += " -Mintel"; + extra_flags += "-Mintel "; } asmio::ObjectFile baked = asmio::to_elf(buffer, asmio::Label::UNSET).bake(); From 504a42b56ecd961fa89be50c23858a51726c0fe7 Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sat, 2 May 2026 19:40:33 +0200 Subject: [PATCH 43/44] Add basic PRFM --- src/asmio/aarch64/argument/prefetch.hpp | 81 +++++++++++++++++++++++++ src/asmio/aarch64/argument/shift.hpp | 16 +++-- src/asmio/aarch64/argument/sizing.hpp | 24 +++++--- src/asmio/aarch64/module.cpp | 8 +++ src/asmio/aarch64/writer.hpp | 2 + src/asmio/aarch64/writer_basic.cpp | 5 ++ test/aarch64.cpp | 14 +++++ 7 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 src/asmio/aarch64/argument/prefetch.hpp diff --git a/src/asmio/aarch64/argument/prefetch.hpp b/src/asmio/aarch64/argument/prefetch.hpp new file mode 100644 index 0000000..08f018f --- /dev/null +++ b/src/asmio/aarch64/argument/prefetch.hpp @@ -0,0 +1,81 @@ +#pragma once +#include +#include + +namespace asmio::arm { + + // The Prefetch enum specifies the prefetch hint as follows: + // + // Access type: + // - PLD for prefetch for load data. + // - PLI for prefetch for load instruction. + // - PST for prefetch for store. + // + // Target cache level: + // - L1 for Level 1 cache. + // - L2 for Level 2 cache. + // - L3 for Level 3 cache. + // - SLC for system level cache (requires FEAT_PRFMSLC support). + // + // Policy: + // - KEEP for retained or temporal prefetch, allocated in the cache normally. + // - STRM for streaming or non-temporal prefetch, for data that is used only once. + enum struct Prefetch { + + PLD_L1_KEEP = 0b00000, + PLD_L1_STRM = 0b00001, + PLD_L2_KEEP = 0b00010, + PLD_L2_STRM = 0b00011, + PLD_L3_KEEP = 0b00100, + PLD_L3_STRM = 0b00101, + PLD_SLC_KEEP = 0b00110, + PLD_SLC_STRM = 0b00111, + PLI_L1_KEEP = 0b01000, + PLI_L1_STRM = 0b01001, + PLI_L2_KEEP = 0b01010, + PLI_L2_STRM = 0b01011, + PLI_L3_KEEP = 0b01100, + PLI_L3_STRM = 0b01101, + PLI_SLC_KEEP = 0b01110, + PLI_SLC_STRM = 0b01111, + PST_L1_KEEP = 0b10000, + PST_L1_STRM = 0b10001, + PST_L2_KEEP = 0b10010, + PST_L2_STRM = 0b10011, + PST_L3_KEEP = 0b10100, + PST_L3_STRM = 0b10101, + PST_SLC_KEEP = 0b10110, + PST_SLC_STRM = 0b10111, + + }; + + inline Prefetch parse_prefetch_enum(const std::string_view& view) { + if (view == "PLD_L1_KEEP") return Prefetch::PLD_L1_KEEP; + if (view == "PLD_L1_STRM") return Prefetch::PLD_L1_STRM; + if (view == "PLD_L2_KEEP") return Prefetch::PLD_L2_KEEP; + if (view == "PLD_L2_STRM") return Prefetch::PLD_L2_STRM; + if (view == "PLD_L3_KEEP") return Prefetch::PLD_L3_KEEP; + if (view == "PLD_L3_STRM") return Prefetch::PLD_L3_STRM; + if (view == "PLD_SLC_KEEP") return Prefetch::PLD_SLC_KEEP; + if (view == "PLD_SLC_STRM") return Prefetch::PLD_SLC_STRM; + if (view == "PLI_L1_KEEP") return Prefetch::PLI_L1_KEEP; + if (view == "PLI_L1_STRM") return Prefetch::PLI_L1_STRM; + if (view == "PLI_L2_KEEP") return Prefetch::PLI_L2_KEEP; + if (view == "PLI_L2_STRM") return Prefetch::PLI_L2_STRM; + if (view == "PLI_L3_KEEP") return Prefetch::PLI_L3_KEEP; + if (view == "PLI_L3_STRM") return Prefetch::PLI_L3_STRM; + if (view == "PLI_SLC_KEEP") return Prefetch::PLI_SLC_KEEP; + if (view == "PLI_SLC_STRM") return Prefetch::PLI_SLC_STRM; + if (view == "PST_L1_KEEP") return Prefetch::PST_L1_KEEP; + if (view == "PST_L1_STRM") return Prefetch::PST_L1_STRM; + if (view == "PST_L2_KEEP") return Prefetch::PST_L2_KEEP; + if (view == "PST_L2_STRM") return Prefetch::PST_L2_STRM; + if (view == "PST_L3_KEEP") return Prefetch::PST_L3_KEEP; + if (view == "PST_L3_STRM") return Prefetch::PST_L3_STRM; + if (view == "PST_SLC_KEEP") return Prefetch::PST_SLC_KEEP; + if (view == "PST_SLC_STRM") return Prefetch::PST_SLC_STRM; + + throw std::runtime_error {"Invalid prefetch enumeration"}; + } + +} diff --git a/src/asmio/aarch64/argument/shift.hpp b/src/asmio/aarch64/argument/shift.hpp index f664f06..daf79cd 100644 --- a/src/asmio/aarch64/argument/shift.hpp +++ b/src/asmio/aarch64/argument/shift.hpp @@ -2,9 +2,13 @@ #include -enum struct ShiftType : uint8_t { - LSL = 0b00, ///< shift left - LSR = 0b01, ///< shift right - ASR = 0b10, ///< arithmetic shift right - ROR = 0b11 ///< rotate right -}; \ No newline at end of file +namespace asmio::arm { + + enum struct ShiftType : uint8_t { + LSL = 0b00, ///< shift left + LSR = 0b01, ///< shift right + ASR = 0b10, ///< arithmetic shift right + ROR = 0b11 ///< rotate right + }; + +} \ No newline at end of file diff --git a/src/asmio/aarch64/argument/sizing.hpp b/src/asmio/aarch64/argument/sizing.hpp index 85c12d5..de35d7b 100644 --- a/src/asmio/aarch64/argument/sizing.hpp +++ b/src/asmio/aarch64/argument/sizing.hpp @@ -2,13 +2,17 @@ #include -enum struct Sizing : uint8_t { - UB = 0b000, ///< unsigned byte - UH = 0b001, ///< unsigned word - UW = 0b010, ///< unsigned dword - UX = 0b011, ///< unsinged qword - SB = 0b100, ///< signed byte - SH = 0b101, ///< signed word - SW = 0b110, ///< signed dword - SX = 0b111, ///< signed qword -}; \ No newline at end of file +namespace asmio::arm { + + enum struct Sizing : uint8_t { + UB = 0b000, ///< unsigned byte + UH = 0b001, ///< unsigned word + UW = 0b010, ///< unsigned dword + UX = 0b011, ///< unsinged qword + SB = 0b100, ///< signed byte + SH = 0b101, ///< signed word + SW = 0b110, ///< signed dword + SX = 0b111, ///< signed qword + }; + +} \ No newline at end of file diff --git a/src/asmio/aarch64/module.cpp b/src/asmio/aarch64/module.cpp index 6afdce2..16b21ea 100644 --- a/src/asmio/aarch64/module.cpp +++ b/src/asmio/aarch64/module.cpp @@ -124,6 +124,14 @@ namespace asmio::arm { throw std::runtime_error {"Invalid argument format, expected condition specifier"}; } + template <> + Prefetch parse_argument(TokenStream stream) { + const Token& token = stream.expect(Token::NAME); + + // FIXME this one is case sensitive and requires uppercase + return parse_prefetch_enum(token.raw); + } + template <> BitPattern parse_argument(TokenStream stream) { if (const Token* token = stream.accept(Token::INT)) { diff --git a/src/asmio/aarch64/writer.hpp b/src/asmio/aarch64/writer.hpp index 28aea8a..6469c3f 100644 --- a/src/asmio/aarch64/writer.hpp +++ b/src/asmio/aarch64/writer.hpp @@ -7,6 +7,7 @@ #include "argument/condition.hpp" #include "argument/order.hpp" #include "argument/pattern.hpp" +#include "argument/prefetch.hpp" namespace asmio::arm { @@ -269,6 +270,7 @@ namespace asmio::arm { INST put_sxtw(Registry dst, Registry src); ///< Sign extend dword INST put_sxth(Registry dst, Registry src); ///< Sign extend word INST put_sxtb(Registry dst, Registry src); ///< Sign extend byte + INST put_prfm(Prefetch mode, Registry base, Registry index); ///< Prefetch memory // large system extension INST put_swpb(Registry val, Registry dst, Registry src, Order order = Order::NONE); ///< Swap byte from 'src' to memory diff --git a/src/asmio/aarch64/writer_basic.cpp b/src/asmio/aarch64/writer_basic.cpp index bf1c9e0..aae7494 100644 --- a/src/asmio/aarch64/writer_basic.cpp +++ b/src/asmio/aarch64/writer_basic.cpp @@ -925,6 +925,11 @@ namespace asmio::arm { put_sbfm(dst, src, BitPattern::try_pack(0xff)); } + void BufferWriter::put_prfm(Prefetch mode, Registry base, Registry index) { + // TODO: this is a partial implementation + put_dword(0b11'11'1000'10'1 << 21 | index.reg << 16 | 0b011 << 13 /* LSL */ | 0b010 << 10 | base.reg << 5 | static_cast(mode)); + } + void BufferWriter::put_stlxp(Registry status, Registry r1, Registry r2, Registry src) { put_inst_ldstx(r1, r2, status, src, 0b10 | r1.wide(), false, true, true); } diff --git a/test/aarch64.cpp b/test/aarch64.cpp index 346d83e..24f17f7 100644 --- a/test/aarch64.cpp +++ b/test/aarch64.cpp @@ -605,6 +605,20 @@ namespace test { }; + TEST (writer_check_prfm_basic) { + + SegmentedBuffer segmented; + BufferWriter writer {segmented}; + segmented.elf_machine = ElfMachine::AARCH64; + + writer.put_prfm(Prefetch::PLD_L1_STRM, X1, X7); + + segmented.link(0); + std::vector s0 = {0x21, 0x68, 0xa7, 0xf8}; + CHECK(segmented.segments()[0].buffer, s0); // .rwx + + }; + /* * region Executable * Begin architecture depended tests for ARM From 7fac87c4d029851fe9fd54a8232e0c6b6a4dd7ea Mon Sep 17 00:00:00 2001 From: magistermaks Date: Sat, 2 May 2026 19:48:48 +0200 Subject: [PATCH 44/44] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index e0dfbf6..e2573d5 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ code generation using the build-in C++ API with syntax closely matching the sour The project consists of a `tasml` CLI assembler and a `test` unit test runner. The code unique to the CLI utility is separated from the rest of -the library and located in the `/src/tasml`. The `/src/file` contains binary file IO, and `/src/asm` -implements the assembler itself. +the library (located in `/src/asmio`) and located in the `/src/tasml`. ## Utility The example of the syntax used by `tasml` utility, the API version of the exact same code can be seen below.