asmjit_test_x86_sections.cpp (5623B)
1 // This file is part of AsmJit project <https://asmjit.com> 2 // 3 // See <asmjit/core.h> or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 // ---------------------------------------------------------------------------- 7 // This is a working example that demonstrates how multiple sections can be 8 // used in a JIT-based code generator. It shows also the necessary tooling 9 // that is expected to be done by the user when the feature is used. It's 10 // important to handle the following cases: 11 // 12 // - Assign offsets to sections when the code generation is finished. 13 // - Tell the CodeHolder to resolve unresolved fixups and check whether 14 // all fixups were resolved. 15 // - Relocate the code 16 // - Copy the code to the destination address. 17 // ---------------------------------------------------------------------------- 18 19 #include <asmjit/core.h> 20 #if ASMJIT_ARCH_X86 && !defined(ASMJIT_NO_X86) && !defined(ASMJIT_NO_JIT) 21 22 #include <asmjit/x86.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 using namespace asmjit; 28 29 // The generated function is very simple, it only accesses the built-in data 30 // (from .data section) at the index as provided by its first argument. This 31 // data is inlined into the resulting function so we can use it this array 32 // for verification that the function returns correct values. 33 static const uint8_t data_array[] = { 2, 9, 4, 7, 1, 3, 8, 5, 6, 0 }; 34 35 static void fail(const char* message, Error err) { 36 printf("** FAILURE: %s (%s) **\n", message, DebugUtils::error_as_string(err)); 37 exit(1); 38 } 39 40 int main() { 41 printf("AsmJit X86 Sections Test\n\n"); 42 43 Environment env = Environment::host(); 44 JitAllocator allocator; 45 46 #ifndef ASMJIT_NO_LOGGING 47 FileLogger logger(stdout); 48 logger.set_indentation(FormatIndentationGroup::kCode, 2); 49 #endif 50 51 CodeHolder code; 52 code.init(env); 53 54 #ifndef ASMJIT_NO_LOGGING 55 code.set_logger(&logger); 56 #endif 57 58 Section* data_section; 59 Error err = code.new_section(Out(data_section), ".data", SIZE_MAX, SectionFlags::kNone, 8); 60 61 if (err != Error::kOk) { 62 fail("Failed to create a .data section", err); 63 } 64 else { 65 printf("Generating code:\n"); 66 x86::Assembler a(&code); 67 x86::Gp idx = a.zax(); 68 x86::Gp addr = a.zcx(); 69 70 Label data = a.new_label(); 71 72 FuncDetail func; 73 func.init(FuncSignature::build<size_t, size_t>(), code.environment()); 74 75 FuncFrame frame; 76 frame.init(func); 77 frame.add_dirty_regs(idx, addr); 78 79 FuncArgsAssignment args(&func); 80 args.assign_all(idx); 81 args.update_func_frame(frame); 82 frame.finalize(); 83 84 a.emit_prolog(frame); 85 a.emit_args_assignment(frame, args); 86 87 a.lea(addr, x86::ptr(data)); 88 a.movzx(idx, x86::byte_ptr(addr, idx)); 89 90 a.emit_epilog(frame); 91 92 a.section(data_section); 93 a.bind(data); 94 95 a.embed(data_array, sizeof(data_array)); 96 } 97 98 // Manually change he offsets of each section, start at 0. This code is very similar to 99 // what `CodeHolder::flatten()` does, however, it's shown here how to do it explicitly. 100 printf("\nCalculating section offsets:\n"); 101 uint64_t offset = 0; 102 for (Section* section : code.sections_by_order()) { 103 offset = Support::align_up(offset, section->alignment()); 104 section->set_offset(offset); 105 offset += section->real_size(); 106 107 printf(" [0x%08X %s] {Id=%u Size=%u}\n", 108 uint32_t(section->offset()), 109 section->name(), 110 section->section_id(), 111 uint32_t(section->real_size())); 112 } 113 size_t code_size = size_t(offset); 114 printf(" Final code size: %zu\n", code_size); 115 116 // Resolve cross-section fixups (if any). On 32-bit X86 this is not necessary 117 // as this is handled through relocations as the addressing is different. 118 if (code.has_unresolved_fixups()) { 119 printf("\nResolving cross-section fixups:\n"); 120 printf(" Before 'resolve_cross_section_fixups()': %zu\n", code.unresolved_fixup_count()); 121 122 err = code.resolve_cross_section_fixups(); 123 if (err != Error::kOk) { 124 fail("Failed to resolve cross-section fixups", err); 125 } 126 printf(" After 'resolve_cross_section_fixups()': %zu\n", code.unresolved_fixup_count()); 127 } 128 129 // Allocate memory for the function and relocate it there. 130 JitAllocator::Span span; 131 err = allocator.alloc(Out(span), code_size); 132 if (err != Error::kOk) 133 fail("Failed to allocate executable memory", err); 134 135 // Relocate to the base-address of the allocated memory. 136 code.relocate_to_base(uint64_t(uintptr_t(span.rx()))); 137 138 allocator.write(span, [&](JitAllocator::Span& span) noexcept -> Error { 139 // Copy the flattened code into `mem.rw`. There are two ways. You can either copy 140 // everything manually by iterating over all sections or use `copy_flattened_data`. 141 // This code is similar to what `copy_flattened_data(p, code_size, 0)` would do: 142 for (Section* section : code.sections_by_order()) 143 memcpy(static_cast<uint8_t*>(span.rw()) + size_t(section->offset()), section->data(), section->buffer_size()); 144 return Error::kOk; 145 }); 146 147 // Execute the function and test whether it works. 148 using Func = size_t (*)(size_t idx); 149 Func fn = (Func)span.rx(); 150 151 printf("\n"); 152 if (fn(0) != data_array[0] || 153 fn(3) != data_array[3] || 154 fn(6) != data_array[6] || 155 fn(9) != data_array[9] ) { 156 printf("** FAILURE: The generated function returned incorrect result(s) **\n"); 157 return 1; 158 } 159 160 printf("** SUCCESS **\n"); 161 return 0; 162 } 163 164 #else 165 int main() { 166 printf("!! This test is disabled: ASMJIT_NO_JIT or unsuitable target architecture !!\n\n"); 167 return 0; 168 } 169 #endif // ASMJIT_ARCH_X86 && !ASMJIT_NO_X86 && !ASMJIT_NO_JIT