├── .gitignore ├── LICENSE.md ├── README.md ├── VTIL-Common.licenseheader ├── VTIL-Common.sln ├── VTIL-Common.vcxproj ├── VTIL-Common.vcxproj.filters ├── amd64 ├── assembler.cpp ├── assembler.hpp ├── disassembly.cpp ├── disassembly.hpp ├── register_details.cpp └── register_details.hpp ├── includes └── vtil │ ├── amd64 │ ├── common │ ├── io │ ├── math │ ├── query │ ├── utility │ └── vtil ├── io ├── asserts.hpp ├── formatting.hpp ├── logger.cpp └── logger.hpp ├── math ├── bitwise.hpp ├── operable.hpp ├── operators.cpp └── operators.hpp ├── query ├── fixed_iterator.hpp ├── query_descriptor.hpp ├── range_iterator.hpp ├── range_iterator_contract.hpp ├── recursive_view.hpp └── view.hpp └── util ├── concept.hpp ├── copy_on_write.hpp ├── critical_section.cpp ├── critical_section.hpp ├── fnv128.hpp ├── fnv64.hpp ├── hashable.hpp ├── priority_list.hpp ├── reducable.hpp ├── stack_container.hpp ├── variant.cpp └── variant.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.ipch 3 | *.opendb 4 | *.user 5 | *.log 6 | *.exe 7 | *.tlog 8 | *.obj 9 | *.exp 10 | *.pdb 11 | *.lib 12 | *.suo 13 | *.ilk 14 | *.db-shm 15 | *.db-wal 16 | .vs/ 17 | x64/Debug/ 18 | x64/Release/ 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2020, Can Bölük 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of [project] nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VTIL 2 | 3 | ## 1) What is VTIL? 4 | VTIL (Virtual-machine Translation Intermediate Language) Project is a set of tools that can be used for binary deobfuscation and devirtualization. 5 | 6 | ## 2) What is this repostiory? 7 | 8 | This repository contains the IL definition and any generic helpers and wrappers we use across the VTIL toolchain. 9 | 10 | Components are likely incomplete as initial release is not done yet, and documentation and FAQ will be on this README file and the organization website once they're done. 11 | 12 | Until the initial release you can keep up to date with the VTIL project by checking my [personal twitter account](https://twitter.com/_can1357) or the VTIL website [vtil.org](https://vtil.org/). 13 | 14 | ## 3) What happened to the previous repository? 15 | All the contents of that repository will be eventually merged into this organization account once they are needed, in one way or another, likely next week. -------------------------------------------------------------------------------- /VTIL-Common.licenseheader: -------------------------------------------------------------------------------- 1 | extensions: .hpp .cpp .h .c 2 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are met: 7 | // 8 | // 1. Redistributions of source code must retain the above copyright notice, 9 | // this list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 3. Neither the name of mosquitto nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | // POSSIBILITY OF SUCH DAMAGE. 28 | // -------------------------------------------------------------------------------- /VTIL-Common.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29709.97 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VTIL-Common", "VTIL-Common.vcxproj", "{EC6B8F7F-730C-4086-B143-4664CC16DF8F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F}.Debug|x64.ActiveCfg = Debug|x64 15 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F}.Debug|x64.Build.0 = Debug|x64 16 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F}.Release|x64.ActiveCfg = Release|x64 17 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {7152B430-9587-4928-BBF8-343854EA2DA0} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /VTIL-Common.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 16.0 15 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F} 16 | Win32Proj 17 | VTILCommon 18 | 10.0 19 | 20 | 21 | 22 | StaticLibrary 23 | true 24 | v142 25 | Unicode 26 | 27 | 28 | StaticLibrary 29 | false 30 | v142 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | true 48 | $(SolutionDir)..\Capstone\include;$(SolutionDir)..\Keystone\include;$(IncludePath) 49 | $(SolutionDir)..\Keystone\llvm\lib\Debug;$(SolutionDir)..\Capstone\msvc\x64\Release;$(LibraryPath) 50 | $(ProjectDir)$(Platform)\$(Configuration)\ 51 | 52 | 53 | false 54 | $(SolutionDir)..\Capstone\include;$(SolutionDir)..\Keystone\include;$(IncludePath) 55 | $(SolutionDir)..\Keystone\llvm\lib\Release;$(SolutionDir)..\Capstone\msvc\x64\Release;$(LibraryPath) 56 | $(ProjectDir)$(Platform)\$(Configuration)\ 57 | 58 | 59 | 60 | NotUsing 61 | Level3 62 | true 63 | _DEBUG;_LIB;%(PreprocessorDefinitions) 64 | true 65 | 66 | 67 | stdcpplatest 68 | 69 | 70 | 71 | 72 | Windows 73 | true 74 | 75 | 76 | keystone.lib;capstone.lib; 77 | 78 | 79 | 80 | 81 | NotUsing 82 | Level3 83 | true 84 | true 85 | true 86 | NDEBUG;_LIB;%(PreprocessorDefinitions) 87 | true 88 | pch.h 89 | stdcpplatest 90 | 91 | 92 | 93 | 94 | Windows 95 | true 96 | true 97 | true 98 | 99 | 100 | keystone.lib;capstone.lib; 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /VTIL-Common.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {14333603-a637-4356-854a-db631a744d42} 6 | 7 | 8 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 9 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 10 | 11 | 12 | {cb6f5bfd-fdca-47fe-bbb7-e91d8e5ba642} 13 | 14 | 15 | {dbf4733d-3814-4218-9474-cf4b4e3c5719} 16 | 17 | 18 | {e5d9bf65-c2c0-4dcf-aa87-65a7059e4e90} 19 | 20 | 21 | {226c5e44-6592-44d0-adb0-31369963cb86} 22 | 23 | 24 | {91e43426-e8a8-4753-8d61-374c3c62f081} 25 | 26 | 27 | 28 | 29 | amd64 30 | 31 | 32 | amd64\Core 33 | 34 | 35 | amd64\Core 36 | 37 | 38 | I/O 39 | 40 | 41 | I/O 42 | 43 | 44 | I/O 45 | 46 | 47 | Includes 48 | 49 | 50 | Includes 51 | 52 | 53 | Includes 54 | 55 | 56 | Math 57 | 58 | 59 | Math 60 | 61 | 62 | Math 63 | 64 | 65 | Includes 66 | 67 | 68 | Utility 69 | 70 | 71 | Includes 72 | 73 | 74 | Queries 75 | 76 | 77 | Queries 78 | 79 | 80 | Queries 81 | 82 | 83 | Queries 84 | 85 | 86 | Queries 87 | 88 | 89 | Includes 90 | 91 | 92 | Queries 93 | 94 | 95 | Utility 96 | 97 | 98 | Utility 99 | 100 | 101 | Utility 102 | 103 | 104 | Utility 105 | 106 | 107 | Utility 108 | 109 | 110 | Utility 111 | 112 | 113 | Utility 114 | 115 | 116 | Utility 117 | 118 | 119 | Utility 120 | 121 | 122 | 123 | 124 | amd64 125 | 126 | 127 | amd64\Core 128 | 129 | 130 | amd64\Core 131 | 132 | 133 | I/O 134 | 135 | 136 | Math 137 | 138 | 139 | Utility 140 | 141 | 142 | Utility 143 | 144 | 145 | 146 | 147 | 148 | Includes 149 | 150 | 151 | -------------------------------------------------------------------------------- /amd64/assembler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | 29 | // Furthermore, the following pieces of software have additional copyrights 30 | // licenses, and/or restrictions: 31 | // 32 | // |--------------------------------------------------------------------------| 33 | // | File name | Link for further information | 34 | // |-------------------------|------------------------------------------------| 35 | // | amd64/* | https://github.com/aquynh/capstone/ | 36 | // | | https://github.com/keystone-engine/keystone/ | 37 | // |--------------------------------------------------------------------------| 38 | // 39 | #include "assembler.hpp" 40 | #include 41 | 42 | namespace keystone 43 | { 44 | ks_struct* get_handle() 45 | { 46 | // Keystone engine is not created until the first call. 47 | // 48 | static ks_engine* handle = [ ] () 49 | { 50 | ks_engine* handle; 51 | if ( ks_open( KS_ARCH_X86, KS_MODE_64, &handle ) != KS_ERR_OK ) 52 | throw std::exception( "Failed to create the Keystone engine!" ); 53 | return handle; 54 | }( ); 55 | return handle; 56 | } 57 | 58 | std::vector assemble( const std::string& src, uint64_t va ) 59 | { 60 | // Assemble the given instruction in text format. 61 | // - (Not too sure why I have to do the .code64; hack, but won't question.) 62 | // 63 | size_t size; 64 | size_t count; 65 | unsigned char* encode = nullptr; 66 | if ( ks_asm( get_handle(), ( ".code64;" + src ).data(), va, &encode, &size, &count ) ) 67 | { 68 | // Free (if relevant) and return on failure. 69 | // 70 | if ( encode ) ks_free( encode ); 71 | return {}; 72 | } 73 | 74 | // Convert to a vector of bytes, free the encoding and return it. 75 | // 76 | std::vector output = { encode, encode + size }; 77 | ks_free( encode ); 78 | return output; 79 | } 80 | }; -------------------------------------------------------------------------------- /amd64/assembler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | 29 | // Furthermore, the following pieces of software have additional copyrights 30 | // licenses, and/or restrictions: 31 | // 32 | // |--------------------------------------------------------------------------| 33 | // | File name | Link for further information | 34 | // |-------------------------|------------------------------------------------| 35 | // | amd64/* | https://github.com/aquynh/capstone/ | 36 | // | | https://github.com/keystone-engine/keystone/ | 37 | // |--------------------------------------------------------------------------| 38 | // 39 | #pragma once 40 | #include 41 | #include 42 | 43 | // Simple wrapper around Keystone disassembler. 44 | // 45 | struct ks_struct; 46 | namespace keystone 47 | { 48 | ks_struct* get_handle(); 49 | std::vector assemble( const std::string& src, uint64_t va = 0 ); 50 | }; 51 | -------------------------------------------------------------------------------- /amd64/disassembly.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | 29 | // Furthermore, the following pieces of software have additional copyrights 30 | // licenses, and/or restrictions: 31 | // 32 | // |--------------------------------------------------------------------------| 33 | // | File name | Link for further information | 34 | // |-------------------------|------------------------------------------------| 35 | // | amd64/* | https://github.com/aquynh/capstone/ | 36 | // | | https://github.com/keystone-engine/keystone/ | 37 | // |--------------------------------------------------------------------------| 38 | // 39 | #include "disassembly.hpp" 40 | 41 | namespace capstone 42 | { 43 | csh get_handle() 44 | { 45 | // Capstone engine is not created until the first call. 46 | // 47 | static csh handle = [ ] () 48 | { 49 | csh handle; 50 | if ( cs_open( CS_ARCH_X86, CS_MODE_64, &handle ) != CS_ERR_OK 51 | || cs_option( handle, CS_OPT_DETAIL, CS_OPT_ON ) != CS_ERR_OK ) 52 | throw std::exception( "Failed to create the Capstone engine!" ); 53 | return handle; 54 | }( ); 55 | return handle; 56 | } 57 | 58 | std::vector disasm( const void* bytes, uint64_t address, size_t size, size_t count ) 59 | { 60 | // Disasemble the instruction. 61 | // 62 | cs_insn* ins; 63 | count = cs_disasm 64 | ( 65 | get_handle(), 66 | ( uint8_t* ) bytes, 67 | size ? size : -1, 68 | address, 69 | size ? 0 : count, 70 | &ins 71 | ); 72 | 73 | // Convert each output into vtil::amd64 format and push it to a vector. 74 | // 75 | std::vector vec; 76 | for ( int i = 0; i < count; i++ ) 77 | { 78 | vtil::amd64::instruction out; 79 | cs_insn& in = ins[ i ]; 80 | 81 | // Copy cs_insn base. 82 | // 83 | out.id = in.id; 84 | out.address = in.address; 85 | out.mnemonic = in.mnemonic; 86 | out.operand_string = in.op_str; 87 | out.bytes = { in.bytes, in.bytes + in.size }; 88 | 89 | // Copy cs_insn::detail. 90 | // 91 | out.regs_read = { in.detail->regs_read, in.detail->regs_read + in.detail->regs_read_count }; 92 | out.regs_write = { in.detail->regs_write, in.detail->regs_write + in.detail->regs_write_count }; 93 | out.groups = { in.detail->groups, in.detail->groups + in.detail->groups_count }; 94 | 95 | // Copy cs_insn::detail::x86. 96 | // 97 | std::copy( std::begin( in.detail->x86.prefix ), std::end( in.detail->x86.prefix ), out.prefix ); 98 | for ( int i = 0; i < 4 && in.detail->x86.opcode[ i ] != 0x0; i++ ) 99 | out.opcode.push_back( in.detail->x86.opcode[ i ] ); 100 | out.rex = in.detail->x86.rex; 101 | out.addr_size = in.detail->x86.addr_size; 102 | out.modrm = in.detail->x86.modrm; 103 | out.sib = in.detail->x86.sib; 104 | out.disp = in.detail->x86.disp; 105 | out.sib_index = in.detail->x86.sib_index; 106 | out.sib_scale = in.detail->x86.sib_scale; 107 | out.sib_base = in.detail->x86.sib_base; 108 | out.xop_cc = in.detail->x86.xop_cc; 109 | out.sse_cc = in.detail->x86.sse_cc; 110 | out.avx_cc = in.detail->x86.avx_cc; 111 | out.avx_sae = in.detail->x86.avx_sae; 112 | out.avx_rm = in.detail->x86.avx_rm; 113 | out.eflags = in.detail->x86.eflags; 114 | out.operands = { in.detail->x86.operands, in.detail->x86.operands + in.detail->x86.op_count }; 115 | out.encoding = in.detail->x86.encoding; 116 | 117 | // Push it to up the vector. 118 | // 119 | vec.push_back( std::move( out ) ); 120 | } 121 | 122 | // Free the output from Capstone and return the vector. 123 | // 124 | cs_free( ins, count ); 125 | return vec; 126 | } 127 | 128 | }; -------------------------------------------------------------------------------- /amd64/disassembly.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | 29 | // Furthermore, the following pieces of software have additional copyrights 30 | // licenses, and/or restrictions: 31 | // 32 | // |--------------------------------------------------------------------------| 33 | // | File name | Link for further information | 34 | // |-------------------------|------------------------------------------------| 35 | // | amd64/* | https://github.com/aquynh/capstone/ | 36 | // | | https://github.com/keystone-engine/keystone/ | 37 | // |--------------------------------------------------------------------------| 38 | // 39 | #pragma once 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include "..\io\formatting.hpp" 47 | 48 | namespace vtil::amd64 49 | { 50 | struct instruction 51 | { 52 | // Data copied from base of [cs_insn]. 53 | // 54 | uint32_t id = 0; 55 | uint64_t address = 0; 56 | std::vector bytes; 57 | std::string mnemonic; 58 | std::string operand_string; 59 | 60 | // Data copied from [cs_insn::detail]. 61 | // 62 | std::set regs_read; 63 | std::set regs_write; 64 | std::set groups; 65 | 66 | // Data copied from [cs_insn::detail::x86] 67 | // 68 | uint8_t prefix[ 4 ]; 69 | std::vector opcode; 70 | 71 | uint8_t rex; 72 | uint8_t addr_size; 73 | uint8_t modrm; 74 | uint8_t sib; 75 | int64_t disp; 76 | 77 | x86_reg sib_index; 78 | int8_t sib_scale; 79 | x86_reg sib_base; 80 | 81 | x86_xop_cc xop_cc; 82 | x86_sse_cc sse_cc; 83 | x86_avx_cc avx_cc; 84 | 85 | bool avx_sae; 86 | x86_avx_rm avx_rm; 87 | 88 | union 89 | { 90 | uint64_t eflags; 91 | uint64_t fpu_flags; 92 | }; 93 | 94 | std::vector operands; 95 | cs_x86_encoding encoding; 96 | 97 | // Returns human readable disassembly. 98 | // 99 | std::string to_string() const 100 | { 101 | return format::str( "%p: %s\t%s", address, mnemonic, operand_string ); 102 | } 103 | 104 | // Helper to check if instruction is of type . 105 | // 106 | bool is( uint32_t idx, const std::vector& operands_t ) const 107 | { 108 | if ( id != idx ) return false; 109 | if ( operands.size() != operands_t.size() ) return false; 110 | for ( int i = 0; i < operands.size(); i++ ) 111 | if ( operands[ i ].type != operands_t[ i ] ) 112 | return false; 113 | return true; 114 | } 115 | 116 | // Helper to check if instruction belongs to the given group. 117 | // 118 | bool in_group( uint8_t group_searched ) const 119 | { 120 | return std::find( groups.begin(), groups.end(), group_searched ) != groups.end(); 121 | } 122 | }; 123 | }; 124 | 125 | // Simple wrapper around Capstone disasembler. 126 | // 127 | namespace capstone 128 | { 129 | csh get_handle(); 130 | std::vector disasm( const void* bytes, uint64_t address, size_t size = 0, size_t count = 1 ); 131 | }; -------------------------------------------------------------------------------- /amd64/register_details.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | 29 | // Furthermore, the following pieces of software have additional copyrights 30 | // licenses, and/or restrictions: 31 | // 32 | // |--------------------------------------------------------------------------| 33 | // | File name | Link for further information | 34 | // |-------------------------|------------------------------------------------| 35 | // | amd64/* | https://github.com/aquynh/capstone/ | 36 | // | | https://github.com/keystone-engine/keystone/ | 37 | // |--------------------------------------------------------------------------| 38 | // 39 | #include "register_details.hpp" 40 | 41 | namespace vtil::amd64 42 | { 43 | // List of all physical registers and the base registers they map to <0> at offset <1> of size <2>. 44 | // 45 | static const std::map register_mappings 46 | { 47 | /* [Instance] [Base] [Offset] [Size] */ 48 | { X86_REG_RAX, { X86_REG_RAX, 0, 8 } }, 49 | { X86_REG_EAX, { X86_REG_RAX, 0, 4 } }, 50 | { X86_REG_AX, { X86_REG_RAX, 0, 2 } }, 51 | { X86_REG_AH, { X86_REG_RAX, 1, 1 } }, 52 | { X86_REG_AL, { X86_REG_RAX, 0, 1 } }, 53 | 54 | { X86_REG_RBX, { X86_REG_RBX, 0, 8 } }, 55 | { X86_REG_EBX, { X86_REG_RBX, 0, 4 } }, 56 | { X86_REG_BX, { X86_REG_RBX, 0, 2 } }, 57 | { X86_REG_BH, { X86_REG_RBX, 1, 1 } }, 58 | { X86_REG_BL, { X86_REG_RBX, 0, 1 } }, 59 | 60 | { X86_REG_RCX, { X86_REG_RCX, 0, 8 } }, 61 | { X86_REG_ECX, { X86_REG_RCX, 0, 4 } }, 62 | { X86_REG_CX, { X86_REG_RCX, 0, 2 } }, 63 | { X86_REG_CH, { X86_REG_RCX, 1, 1 } }, 64 | { X86_REG_CL, { X86_REG_RCX, 0, 1 } }, 65 | 66 | { X86_REG_RDX, { X86_REG_RDX, 0, 8 } }, 67 | { X86_REG_EDX, { X86_REG_RDX, 0, 4 } }, 68 | { X86_REG_DX, { X86_REG_RDX, 0, 2 } }, 69 | { X86_REG_DH, { X86_REG_RDX, 1, 1 } }, 70 | { X86_REG_DL, { X86_REG_RDX, 0, 1 } }, 71 | 72 | { X86_REG_RDI, { X86_REG_RDI, 0, 8 } }, 73 | { X86_REG_EDI, { X86_REG_RDI, 0, 4 } }, 74 | { X86_REG_DI, { X86_REG_RDI, 0, 2 } }, 75 | { X86_REG_DIL, { X86_REG_RDI, 0, 1 } }, 76 | 77 | { X86_REG_RSI, { X86_REG_RSI, 0, 8 } }, 78 | { X86_REG_ESI, { X86_REG_RSI, 0, 4 } }, 79 | { X86_REG_SI, { X86_REG_RSI, 0, 2 } }, 80 | { X86_REG_SIL, { X86_REG_RSI, 0, 1 } }, 81 | 82 | { X86_REG_RBP, { X86_REG_RBP, 0, 8 } }, 83 | { X86_REG_EBP, { X86_REG_RBP, 0, 4 } }, 84 | { X86_REG_BP, { X86_REG_RBP, 0, 2 } }, 85 | { X86_REG_BPL, { X86_REG_RBP, 0, 1 } }, 86 | 87 | { X86_REG_RSP, { X86_REG_RSP, 0, 8 } }, 88 | { X86_REG_ESP, { X86_REG_RSP, 0, 4 } }, 89 | { X86_REG_SP, { X86_REG_RSP, 0, 2 } }, 90 | { X86_REG_SPL, { X86_REG_RSP, 0, 1 } }, 91 | 92 | { X86_REG_R8, { X86_REG_R8, 0, 8 } }, 93 | { X86_REG_R8D, { X86_REG_R8, 0, 4 } }, 94 | { X86_REG_R8W, { X86_REG_R8, 0, 2 } }, 95 | { X86_REG_R8B, { X86_REG_R8, 0, 1 } }, 96 | 97 | { X86_REG_R9, { X86_REG_R9, 0, 8 } }, 98 | { X86_REG_R9D, { X86_REG_R9, 0, 4 } }, 99 | { X86_REG_R9W, { X86_REG_R9, 0, 2 } }, 100 | { X86_REG_R9B, { X86_REG_R9, 0, 1 } }, 101 | 102 | { X86_REG_R10, { X86_REG_R10, 0, 8 } }, 103 | { X86_REG_R10D, { X86_REG_R10, 0, 4 } }, 104 | { X86_REG_R10W, { X86_REG_R10, 0, 2 } }, 105 | { X86_REG_R10B, { X86_REG_R10, 0, 1 } }, 106 | 107 | { X86_REG_R11, { X86_REG_R11, 0, 8 } }, 108 | { X86_REG_R11D, { X86_REG_R11, 0, 4 } }, 109 | { X86_REG_R11W, { X86_REG_R11, 0, 2 } }, 110 | { X86_REG_R11B, { X86_REG_R11, 0, 1 } }, 111 | 112 | { X86_REG_R12, { X86_REG_R12, 0, 8 } }, 113 | { X86_REG_R12D, { X86_REG_R12, 0, 4 } }, 114 | { X86_REG_R12W, { X86_REG_R12, 0, 2 } }, 115 | { X86_REG_R12B, { X86_REG_R12, 0, 1 } }, 116 | 117 | { X86_REG_R13, { X86_REG_R13, 0, 8 } }, 118 | { X86_REG_R13D, { X86_REG_R13, 0, 4 } }, 119 | { X86_REG_R13W, { X86_REG_R13, 0, 2 } }, 120 | { X86_REG_R13B, { X86_REG_R13, 0, 1 } }, 121 | 122 | { X86_REG_R14, { X86_REG_R14, 0, 8 } }, 123 | { X86_REG_R14D, { X86_REG_R14, 0, 4 } }, 124 | { X86_REG_R14W, { X86_REG_R14, 0, 2 } }, 125 | { X86_REG_R14B, { X86_REG_R14, 0, 1 } }, 126 | 127 | { X86_REG_R15, { X86_REG_R15, 0, 8 } }, 128 | { X86_REG_R15D, { X86_REG_R15, 0, 4 } }, 129 | { X86_REG_R15W, { X86_REG_R15, 0, 2 } }, 130 | { X86_REG_R15B, { X86_REG_R15, 0, 1 } }, 131 | 132 | { X86_REG_EFLAGS, { X86_REG_EFLAGS, 0, 8 } }, 133 | }; 134 | 135 | // Gets the offset<0> and size<1> of the mapping for the given register. 136 | // 137 | register_mapping resolve_mapping( uint8_t _reg ) 138 | { 139 | // Try to find the register mapping, if successful return if 140 | // 141 | auto it = register_mappings.find( ( x86_reg ) _reg ); 142 | if ( it != register_mappings.end() ) 143 | return it->second; 144 | 145 | // Otherwise return default mapping after making sure it's valid. 146 | // 147 | fassert( _reg != X86_REG_INVALID ); 148 | return { ( x86_reg ) _reg, 0, 8 }; 149 | } 150 | 151 | // Gets the base register for the given register. 152 | // 153 | x86_reg extend( uint8_t _reg ) 154 | { 155 | // Try to find the register mapping, 156 | // return as is if we fail to do so. 157 | // 158 | auto it = register_mappings.find( ( x86_reg ) _reg ); 159 | if ( it == register_mappings.end() ) 160 | return ( x86_reg ) _reg; 161 | 162 | // Otherwise return the base register. 163 | // 164 | return it->second.base_register; 165 | } 166 | 167 | // Converts the enum into human-readable format. 168 | // 169 | std::string name( uint8_t _reg ) 170 | { 171 | // Else lookup the name from capstone. 172 | // 173 | return cs_reg_name( capstone::get_handle(), ( x86_reg ) _reg ); 174 | } 175 | 176 | // Remaps the given register at given specifications. 177 | // 178 | x86_reg remap( uint8_t _reg, uint8_t offset, uint8_t size ) 179 | { 180 | // Extend passed register 181 | // 182 | x86_reg base_register = extend( _reg ); 183 | 184 | // For each mapping described: 185 | // 186 | for ( auto& pair : register_mappings ) 187 | { 188 | // If matches the specifications, return. 189 | // 190 | if ( pair.second.base_register == base_register && 191 | pair.second.offset == offset && 192 | pair.second.size == size ) 193 | return pair.first; 194 | } 195 | 196 | // If we fail to find, and we're strictly 197 | // remapping to a full register, return as is. 198 | // 199 | fassert( offset == 0 ); 200 | return base_register; 201 | } 202 | }; -------------------------------------------------------------------------------- /amd64/register_details.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | 29 | // Furthermore, the following pieces of software have additional copyrights 30 | // licenses, and/or restrictions: 31 | // 32 | // |--------------------------------------------------------------------------| 33 | // | File name | Link for further information | 34 | // |-------------------------|------------------------------------------------| 35 | // | amd64/* | https://github.com/aquynh/capstone/ | 36 | // | | https://github.com/keystone-engine/keystone/ | 37 | // |--------------------------------------------------------------------------| 38 | // 39 | #pragma once 40 | #include 41 | #include 42 | #include 43 | #include "disassembly.hpp" 44 | #include "..\io\asserts.hpp" 45 | 46 | namespace vtil::amd64 47 | { 48 | // Structure describing how a register maps to another register. 49 | // 50 | struct register_mapping 51 | { 52 | // Base register of full size, e.g. X86_REG_RAX. 53 | // 54 | x86_reg base_register; 55 | 56 | // Offset of the current register from the base register. 57 | // 58 | uint8_t offset; 59 | 60 | // Size of the current register in bytes. 61 | // 62 | uint8_t size; 63 | 64 | inline operator std::tuple() { return { base_register, offset, size }; } 65 | }; 66 | 67 | // Gets the offset<0> and size<1> of the mapping for the given register. 68 | // 69 | register_mapping resolve_mapping( uint8_t _reg ); 70 | 71 | // Gets the base register for the given register. 72 | // 73 | x86_reg extend( uint8_t _reg ); 74 | 75 | // Converts the enum into human-readable format. 76 | // 77 | std::string name( uint8_t _reg ); 78 | 79 | // Remaps the given register at given specifications. 80 | // 81 | x86_reg remap( uint8_t _reg, uint8_t offset, uint8_t size ); 82 | }; -------------------------------------------------------------------------------- /includes/vtil/amd64: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "..\..\amd64\assembler.hpp" 3 | #include "..\..\amd64\disassembly.hpp" 4 | #include "..\..\amd64\register_details.hpp" -------------------------------------------------------------------------------- /includes/vtil/common: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include -------------------------------------------------------------------------------- /includes/vtil/io: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "..\..\io\asserts.hpp" 3 | #include "..\..\io\formatting.hpp" 4 | #include "..\..\io\logger.hpp" -------------------------------------------------------------------------------- /includes/vtil/math: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "..\..\math\bitwise.hpp" 3 | #include "..\..\math\operators.hpp" 4 | #include "..\..\math\operable.hpp" -------------------------------------------------------------------------------- /includes/vtil/query: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "..\..\query\range_iterator_contract.hpp" 3 | #include "..\..\query\range_iterator.hpp" 4 | #include "..\..\query\query_descriptor.hpp" 5 | #include "..\..\query\view.hpp" 6 | #include "..\..\query\recursive_view.hpp" 7 | #include "..\..\query\fixed_iterator.hpp" -------------------------------------------------------------------------------- /includes/vtil/utility: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "..\..\util\priority_list.hpp" 3 | #include "..\..\util\critical_section.hpp" 4 | #include "..\..\util\copy_on_write.hpp" 5 | #include "..\..\util\concept.hpp" 6 | #include "..\..\util\variant.hpp" 7 | #include "..\..\util\hashable.hpp" 8 | #include "..\..\util\stack_container.hpp" 9 | #include "..\..\util\reducable.hpp" -------------------------------------------------------------------------------- /includes/vtil/vtil: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include -------------------------------------------------------------------------------- /io/asserts.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include "logger.hpp" 31 | 32 | namespace vtil::assert 33 | { 34 | namespace impl { __declspec( noreturn ) __forceinline static void noreturn_helper() { __debugbreak(); } }; 35 | 36 | static void or_die( bool condition, const char* file_name, const char* condition_str, uint32_t line_number ) 37 | { 38 | if ( condition ) return; 39 | logger::error 40 | ( 41 | "Assertion failure at %s:%d (%s)", 42 | file_name, 43 | line_number, 44 | condition_str 45 | ); 46 | } 47 | }; 48 | 49 | #ifdef _DEBUG 50 | #define fassert__stringify(x) #x 51 | #define fassert(x) vtil::assert::or_die( (x), __FILE__, fassert__stringify(x), __LINE__ ) 52 | #else 53 | #define fassert(...) 54 | #endif 55 | #define unreachable() vtil::assert::impl::noreturn_helper() -------------------------------------------------------------------------------- /io/formatting.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | 32 | // [Configuration] 33 | // Determine the way we format the instructions. 34 | // 35 | #ifndef VTIL_FMT_DEFINED 36 | #define VTIL_FMT_INS_MNM "%-8s" 37 | #define VTIL_FMT_INS_OPR "%-12s" 38 | #define VTIL_FMT_INS_MNM_S 8 39 | #define VTIL_FMT_INS_OPR_S 12 40 | #define VTIL_FMT_SUFFIX_1 'b' 41 | #define VTIL_FMT_SUFFIX_2 'w' 42 | #define VTIL_FMT_SUFFIX_4 'd' 43 | #define VTIL_FMT_SUFFIX_8 'q' 44 | #define VTIL_FMT_DEFINED 45 | #endif 46 | 47 | namespace vtil::format 48 | { 49 | // Suffixes used to indicate registers of N bytes. 50 | // 51 | static constexpr char suffix_map[] = { 0, VTIL_FMT_SUFFIX_1, VTIL_FMT_SUFFIX_2, 0, VTIL_FMT_SUFFIX_4, 0, 0, 0, VTIL_FMT_SUFFIX_8 }; 52 | 53 | // Used to fix std::string usage in combination with "%s". 54 | // 55 | #ifdef __INTEL_COMPILER 56 | #pragma warning (supress:1011) // Billion dollar company yes? #2 57 | #endif 58 | template 59 | __forceinline static auto fix_parameter( T&& x ) 60 | { 61 | if constexpr ( std::is_same_v, std::string> || std::is_same_v, std::wstring> ) 62 | return x.data(); 63 | else 64 | return std::forward( x ); 65 | } 66 | 67 | // Returns formatted string according to . 68 | // 69 | template 70 | static std::string str( const char* fmt, params&&... ps ) 71 | { 72 | std::string buffer; 73 | buffer.resize( snprintf( nullptr, 0, fmt, fix_parameter( std::forward( ps ) )... ) ); 74 | sprintf_s( buffer.data(), buffer.size() + 1, fmt, fix_parameter( std::forward( ps ) )... ); 75 | return buffer; 76 | } 77 | 78 | // Formats the integer into a signed hexadecimal. 79 | // 80 | template>, int> = 0> 81 | static std::string hex( T&& value ) 82 | { 83 | if ( !std::is_signed_v> || value >= 0 ) 84 | return str( "0x%llx", value ); 85 | else 86 | return str( "-0x%llx", -value ); 87 | } 88 | 89 | // Formats the integer into a signed hexadecimal with explicit + if positive. 90 | // 91 | static std::string offset( int64_t value ) 92 | { 93 | if ( value >= 0 ) 94 | return str( "+ 0x%llx", value ); 95 | else 96 | return str( "- 0x%llx", -value ); 97 | } 98 | }; -------------------------------------------------------------------------------- /io/logger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #include "logger.hpp" 29 | 30 | #if _WIN64 31 | #define WIN32_LEAN_AND_MEAN 32 | #define NOMINMAX 33 | #include 34 | #endif 35 | 36 | namespace vtil::logger 37 | { 38 | // State of the logging engine. 39 | // 40 | critical_section log_cs; 41 | volatile bool log_disable = false; 42 | volatile int log_padding = -1; 43 | volatile int log_padding_carry = 0; 44 | 45 | namespace impl 46 | { 47 | // Internally used to change the console if possible. 48 | // 49 | void set_color( console_color color ) 50 | { 51 | #if _WIN64 52 | SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), color ); 53 | #endif 54 | } 55 | 56 | // Internally used to initialize the logger. 57 | // 58 | void initialize() 59 | { 60 | static bool log_init = false; 61 | if ( log_init ) return; 62 | #if _WIN64 63 | SetConsoleOutputCP( CP_UTF8 ); 64 | #endif 65 | log_init = true; 66 | } 67 | }; 68 | }; -------------------------------------------------------------------------------- /io/logger.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "formatting.hpp" 35 | #include "..\util\critical_section.hpp" 36 | 37 | namespace vtil::logger 38 | { 39 | // Console colors, only used on Windows platform. 40 | // 41 | enum console_color 42 | { 43 | CON_BRG = 15, 44 | CON_YLW = 14, 45 | CON_PRP = 13, 46 | CON_RED = 12, 47 | CON_CYN = 11, 48 | CON_GRN = 10, 49 | CON_BLU = 9, 50 | CON_DEF = 7, 51 | }; 52 | 53 | // State of the logging engine. 54 | // 55 | extern critical_section log_cs; 56 | extern volatile bool log_disable; 57 | extern volatile int log_padding; 58 | extern volatile int log_padding_carry; 59 | 60 | // Padding customization for logger. 61 | // 62 | static constexpr char log_padding_c = '|'; 63 | static constexpr uint32_t log_padding_step = 2; 64 | 65 | // RAII hack for incrementing the padding until routine ends. 66 | // Can be used with the argument u=0 to act as a lock guard. 67 | // - Will wait for the critical section ownership and hold it 68 | // until the scope ends. 69 | // 70 | struct scope_padding 71 | { 72 | int prev = log_padding; 73 | bool holds_lock = false; 74 | scope_padding( unsigned u ) { log_padding += u; log_cs.lock(); holds_lock = true; } 75 | void end() { if ( holds_lock ) log_cs.unlock(), holds_lock = false; log_padding = prev; } 76 | ~scope_padding() { end(); } 77 | }; 78 | 79 | // RAII hack for changing verbosity of logs within the scope. 80 | // - Will wait for the critical section ownership and hold it 81 | // until the scope ends. 82 | // 83 | struct scope_verbosity 84 | { 85 | bool prev = log_disable; 86 | bool holds_lock = false; 87 | scope_verbosity( bool verbose_output ) { log_disable |= !verbose_output; log_cs.lock(); holds_lock = true; } 88 | void end() { if ( holds_lock ) log_cs.unlock(), holds_lock = false; log_disable = prev; } 89 | ~scope_verbosity() { end(); } 90 | }; 91 | 92 | // Implementation details. 93 | // 94 | namespace impl 95 | { 96 | // Internally used to change the console if possible. 97 | // 98 | void set_color( console_color color ); 99 | 100 | // Internally used to initialize the logger. 101 | // 102 | void initialize(); 103 | 104 | // Used to mark functions noreturn. 105 | // 106 | __declspec( noreturn ) __forceinline static void noreturn_helper() { __debugbreak(); } 107 | }; 108 | 109 | // Main function used when logging. 110 | // 111 | template 112 | static int log( const char* fmt, params&&... ps ) 113 | { 114 | // Do not execute if logs are disabled. 115 | // 116 | if ( log_disable ) return 0; 117 | 118 | // Hold the lock for the critical section guarding ::log. 119 | // 120 | std::lock_guard g( log_cs ); 121 | 122 | // Initialize logger if not done already. 123 | // 124 | impl::initialize(); 125 | 126 | // Set to defualt color. 127 | // 128 | impl::set_color( CON_DEF ); 129 | int out_cnt = 0; 130 | 131 | // If we should pad this output: 132 | // 133 | if ( log_padding > 0 ) 134 | { 135 | // If it was not carried from previous: 136 | // 137 | if( int pad_by = log_padding - log_padding_carry ) 138 | { 139 | for ( int i = 0; i < pad_by; i++ ) 140 | { 141 | if ( ( i + 1 ) == pad_by ) 142 | { 143 | out_cnt += printf( "%*c", log_padding_step - 1, ' ' ); 144 | if ( fmt[ 0 ] == ' ' ) putchar( log_padding_c ); 145 | } 146 | else 147 | { 148 | out_cnt += printf( "%*c%c", log_padding_step - 1, ' ', log_padding_c ); 149 | } 150 | } 151 | } 152 | 153 | // Set or clear the carry for next. 154 | // 155 | if ( fmt[ strlen( fmt ) - 1 ] == '\n' ) 156 | log_padding_carry = 0; 157 | else 158 | log_padding_carry = log_padding; 159 | } 160 | 161 | // Set to requested color and redirect to printf. 162 | // 163 | impl::set_color( color ); 164 | return out_cnt + printf( fmt, format::fix_parameter( std::forward( ps ) )... ); 165 | } 166 | 167 | // Prints an error message and breaks the execution. 168 | // 169 | template 170 | __declspec( noreturn ) static void error( const char* fmt, params&&... ps ) 171 | { 172 | // Error will stop any execution so feel free to ignore any locks. 173 | // 174 | new ( &log_cs ) critical_section(); 175 | 176 | // Print the erorr message. 177 | // 178 | log( fmt, std::forward( ps )... ); 179 | 180 | // Break the program. 181 | // 182 | #ifdef _DEBUG 183 | __debugbreak(); 184 | #else 185 | exit( 1 ); 186 | #endif 187 | impl::noreturn_helper(); 188 | } 189 | }; -------------------------------------------------------------------------------- /math/bitwise.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "..\util\reducable.hpp" 34 | #include "..\io\asserts.hpp" 35 | 36 | // Declare the type we will used for bit lenghts of data. 37 | // - We are using int instead of char since most operations will end up casting 38 | // this value to an integer anyway and since char does not provide us any intrinsic 39 | // safety either this only hurts us in terms of performance. 40 | // 41 | using bitcnt_t = int; 42 | 43 | namespace vtil::math 44 | { 45 | // Sizeof equivalent in bits. 46 | // 47 | template 48 | static constexpr bitcnt_t bitcnt = sizeof( T ) * 8; 49 | 50 | // Extracts the sign bit from the given value. 51 | // 52 | template, int> = 0> 53 | static constexpr bool sgn( T type ) { return bool( type >> ( bitcnt - 1 ) ); } 54 | 55 | // Implement platform-indepdenent popcnt and bit_test/set/clear/flip. 56 | // 57 | static constexpr bitcnt_t popcnt( uint64_t x ) 58 | { 59 | // https://www.chessprogramming.org/Population_Count#The_PopCount_routine 60 | // 61 | x = x - ( x >> 1 ) & 0x5555555555555555; 62 | x = ( x & 0x3333333333333333 ) + ( ( x >> 2 ) & 0x3333333333333333 ); 63 | x = ( x + ( x >> 4 ) ) & 0x0f0f0f0f0f0f0f0f; 64 | x = ( x * 0x0101010101010101 ) >> 56; 65 | return bitcnt_t( x ); 66 | } 67 | 68 | // Generate a mask for the given variable size and offset. 69 | // 70 | static constexpr uint64_t fill( bitcnt_t bit_count, bitcnt_t bit_offset = 0 ) 71 | { 72 | if ( bit_offset >= 64 ) return 0; 73 | return ( ( ~0ull ) >> ( 64 - bit_count ) ) << bit_offset; 74 | } 75 | 76 | // Fills the bits of the uint64_t type after the given offset with the sign bit. 77 | // - We accept an [uint64_t] as the sign "bit" instead of a for 78 | // the sake of a further trick we use to avoid branches. 79 | // 80 | static constexpr uint64_t fill_sign( uint64_t sign, bitcnt_t bit_offset = 0 ) 81 | { 82 | // The XOR operation with 0b1 flips the sign bit, after which when we subtract 83 | // one to create 0xFF... for (1) and 0x00... for (0). 84 | // - We could have also done [s *= ~0ull], but it's slower since: 85 | // 1) XOR ~= [#μop: 1, latency: 1] 86 | // 2) SUB ~= [#μop: 1, latency: 1] 87 | // vs 88 | // 1) MUL ~= [#μop: 3, latency: 3] 89 | // 90 | return ( ( sign ^ 1 ) - 1 ) << bit_offset; 91 | } 92 | 93 | // Zero extends the given integer. 94 | // 95 | static uint64_t __zx( uint64_t value, bitcnt_t bcnt_src ) 96 | { 97 | // Use simple casts where possible. 98 | // 99 | switch ( bcnt_src ) 100 | { 101 | case 1: return value & 1; 102 | case 8: return *( uint8_t* ) &value; 103 | case 16: return *( uint16_t* ) &value; 104 | case 32: return *( uint32_t* ) &value; 105 | case 64: return *( uint64_t* ) &value; 106 | } 107 | 108 | // Make sure source size is non-zero. 109 | // 110 | fassert( bcnt_src != 0 ); 111 | 112 | // Mask the value. 113 | // 114 | value &= fill( bcnt_src ); 115 | return value; 116 | } 117 | 118 | // Sign extends the given integer. 119 | // 120 | static int64_t __sx( uint64_t value, bitcnt_t bcnt_src ) 121 | { 122 | // Use simple casts where possible. 123 | // 124 | switch ( bcnt_src ) 125 | { 126 | case 1: return value & 1; // Booleans cannot have sign bits by definition. 127 | case 8: return *( int8_t* ) &value; 128 | case 16: return *( int16_t* ) &value; 129 | case 32: return *( int32_t* ) &value; 130 | case 64: return *( int64_t* ) &value; 131 | } 132 | 133 | // Make sure source size is non-zero. 134 | // 135 | fassert( bcnt_src != 0 ); 136 | 137 | // Extract sign bit. 138 | // 139 | uint64_t sign = ( value >> ( bcnt_src - 1 ) ) & 1; 140 | 141 | // Mask the value. 142 | // 143 | value &= fill( bcnt_src ); 144 | 145 | // Extend the sign bit. 146 | // - Small trick is used here to avoid branches. 147 | // 148 | sign = ( ( sign ^ 1 ) - 1 ) << bcnt_src; 149 | return value | sign; 150 | } 151 | 152 | // Return value from bit-vector lookup where the result can be either unknown or constant 0/1. 153 | // 154 | enum class bit_state : int8_t 155 | { 156 | zero = -1, 157 | unknown = 0, 158 | one = +1, 159 | }; 160 | 161 | // Bit-vector holding 0 to 64 bits of value with optional unknowns. 162 | // 163 | class bit_vector : public reducable 164 | { 165 | // Value of the known bits, mask of it can be found by [::known_mask()] 166 | // - Guaranteed to hold 0 for unknown bits. 167 | // 168 | uint64_t known_bits = 0; 169 | 170 | // Mask for the bit that we do not know. 171 | // - Guaranteed to hold 0 for known bits and for all bits above bit_count. 172 | // 173 | uint64_t unknown_bits = 0; 174 | 175 | // Number of bits this vector contains. 176 | // 177 | bitcnt_t bit_count = 0; 178 | 179 | public: 180 | // Default constructor, will result in invalid bit-vector. 181 | // 182 | bit_vector() = default; 183 | 184 | // Constructs a bit-vector where all bits are set according to the state. 185 | // - Declared explicit to avoid construction from integers. 186 | // 187 | explicit bit_vector( bitcnt_t bit_count ) : 188 | bit_count( bit_count ), unknown_bits( fill( bit_count ) ), known_bits( 0 ) {} 189 | 190 | // Constructs a bit-vector where all bits are known. 191 | // 192 | bit_vector( uint64_t value, bitcnt_t bit_count ) : 193 | bit_count( bit_count ), unknown_bits( 0 ), known_bits( value & fill( bit_count ) ) {} 194 | 195 | // Constructs a bit-vector where bits are partially known. 196 | // 197 | bit_vector( uint64_t known_bits, uint64_t unknown_bits, bitcnt_t bit_count ) : 198 | bit_count( bit_count ), unknown_bits( unknown_bits & fill( bit_count ) ), known_bits( known_bits & ~( unknown_bits & fill( bit_count ) ) ) {} 199 | 200 | // Some helpers to access the internal state. 201 | // 202 | inline uint64_t value_mask() const { return fill( bit_count ); } 203 | inline uint64_t unknown_mask() const { return unknown_bits; } 204 | inline uint64_t known_mask() const { return fill( bit_count ) & ~unknown_bits; } 205 | inline uint64_t known_one() const { return known_bits; } 206 | inline uint64_t known_zero() const { return ~( unknown_bits | known_bits ); } 207 | inline bool all_zero() const { return unknown_bits == 0 && !known_bits; } 208 | inline bool all_one() const { return unknown_bits == 0 && ( known_bits == fill( bit_count ) ); } 209 | inline bool is_valid() const { return bit_count != 0; } 210 | inline bool is_known() const { return bit_count && unknown_bits == 0; } 211 | inline bool is_unknown() const { return !bit_count || unknown_bits != 0; } 212 | inline bitcnt_t size() const { return bit_count; } 213 | 214 | // Gets the value represented, and nullopt if vector has unknown bits. 215 | // 216 | template 217 | std::optional get() const 218 | { 219 | if ( is_known() ) 220 | { 221 | if constexpr ( std::is_signed_v ) 222 | return ( type ) __sx( known_bits, bit_count ); 223 | else 224 | return ( type ) __zx( known_bits, bit_count ); 225 | } 226 | return std::nullopt; 227 | } 228 | template> 229 | inline std::optional get() const { return get(); } 230 | 231 | // Extends or shrinks the the vector. 232 | // 233 | bit_vector& resize( bitcnt_t new_size, bool signed_cast = false ) 234 | { 235 | fassert( 0 < new_size && new_size <= 64 ); 236 | 237 | if( signed_cast && new_size > bit_count ) 238 | { 239 | bit_state sign_bit = at( bit_count - 1 ); 240 | bool sign_bit_unk = at( bit_count - 1 ) == bit_state::unknown; 241 | 242 | if ( sign_bit == bit_state::unknown ) 243 | unknown_bits |= fill( 64, bit_count ); 244 | else if ( sign_bit == bit_state::one ) 245 | known_bits |= fill( 64, bit_count ); 246 | } 247 | 248 | bit_count = new_size; 249 | known_bits &= fill( new_size ); 250 | unknown_bits &= fill( new_size ); 251 | return *this; 252 | } 253 | 254 | // Gets the state of the bit at the index given. 255 | // 256 | bit_state at( bitcnt_t n ) const 257 | { 258 | if ( unknown_bits & ( 1ull << n ) ) return bit_state::unknown; 259 | return bit_state( ( ( ( known_bits >> n ) & 1 ) << 1 ) - 1 ); 260 | } 261 | inline bit_state operator[]( bitcnt_t n ) const { return at( n ); } 262 | 263 | // Conversion to human-readable format. 264 | // 265 | std::string to_string() const 266 | { 267 | std::string out; 268 | for ( int n = bit_count - 1; n >= 0; n-- ) 269 | { 270 | uint64_t mask = 1ull << n; 271 | out += ( unknown_bits & mask ) ? '?' : ( known_bits & mask ) ? '1' : '0'; 272 | } 273 | return out; 274 | } 275 | 276 | // Declare reduction. 277 | // - Note: Relative comparison operators should not be used for actual comparison 278 | // but are there for the use of sorted containers. 279 | // 280 | auto reduce() { return std::tie( bit_count, known_bits, unknown_bits ); } 281 | }; 282 | }; -------------------------------------------------------------------------------- /math/operable.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include "operators.hpp" 33 | #include "bitwise.hpp" 34 | 35 | // Operables provide a very easy way to generate lazy math operators for all 36 | // [Class x Integer], [Integer x Class], [Class x Class] posibilities as 37 | // long as the base class provides 2 constructors by contract. 38 | // 39 | // template, int> = 0> 40 | // - base_class( T value ) 41 | // => operable( value ) 42 | // 43 | // - base_class( operator_desc, const base_class& ) // For unary operators (Optionaly T&&) 44 | // - base_class( const base_class&, operator_desc, const base_class& ) // For binary operators (Optionaly T&&) 45 | // => operable(), operable::bit_count must be set at constructor. 46 | // 47 | // 48 | namespace vtil::math 49 | { 50 | // Declare base operable type. 51 | // 52 | template 53 | struct operable 54 | { 55 | // Value of the operand. 56 | // 57 | math::bit_vector value = {}; 58 | 59 | // Default constructor and the constructor for constant values. 60 | // 61 | operable() = default; 62 | template, int> = 0> 63 | operable( T value, bitcnt_t bit_count = sizeof( T ) * 8 ) : value( uint64_t( value ), bit_count ) {} 64 | 65 | // Gets the value represented, and nullopt if value has unknown bits. 66 | // 67 | template 68 | std::optional get() const { return value.get(); } 69 | template> 70 | inline std::optional get() const { return value.get(); } 71 | 72 | // Redirect certain helpers to bit_vector. 73 | // 74 | inline bitcnt_t size() const { return value.size(); } 75 | inline uint64_t known_mask() const { return value.known_mask(); } 76 | inline uint64_t unknown_mask() const { return value.unknown_mask(); } 77 | inline uint64_t known_one() const { return value.known_one(); } 78 | inline uint64_t known_zero() const { return value.known_zero(); } 79 | inline bool is_constant() const { return value.is_known(); } 80 | 81 | // Resizes the constant, must be overriden by the base type to handle unknowns. 82 | // 83 | void resize( bitcnt_t new_size, bool sign_extend = false ) 84 | { 85 | fassert( value.is_known() ); 86 | value.resize( new_size, sign_extend ); 87 | } 88 | }; 89 | 90 | // Whether the type is a operable instance or not. 91 | // 92 | template 93 | static constexpr bool is_custom_operable_v = std::is_base_of_v, T>; 94 | 95 | // Whether the type is operable in combination with an operable instance or not. 96 | // 97 | template 98 | static constexpr bool is_operable_v = std::is_integral_v || is_custom_operable_v; 99 | 100 | // Whether given types are cross-operable or not. 101 | // 102 | template 103 | static constexpr bool is_xoperable() 104 | { 105 | // If T1 is a custom operable, T2 needs to be either an integral type or same type as T1. 106 | // 107 | if constexpr ( is_custom_operable_v ) 108 | return std::is_integral_v || std::is_same_v; 109 | 110 | // If only T2 is a custom operable, T1 needs to be an integral type. 111 | // 112 | else if constexpr ( is_custom_operable_v ) 113 | return std::is_integral_v; 114 | return false; 115 | } 116 | 117 | // Can be overriden externally to allow aliases. 118 | // 119 | template 120 | struct resolve_alias { using type = typename T1; }; 121 | 122 | // Removes all qualifiers and resolves the base if aliased. 123 | // 124 | template 125 | using strip_operable_t = typename resolve_alias>::type; 126 | 127 | // Returns the result of the cross-operation between two types, void if not cross-operable. 128 | // 129 | template, 131 | typename base_type_2 = strip_operable_t, 132 | std::enable_if_t(), int> = 0 133 | > 134 | struct xop_result 135 | { 136 | using type = std::conditional_t< 137 | is_custom_operable_v, 138 | base_type_1, 139 | base_type_2 140 | >; 141 | }; 142 | }; 143 | 144 | // Operations with operable types 145 | // 146 | #define DEFINE_OPERATION(...) \ 147 | template::type> \ 148 | static result_t __VA_ARGS__ 149 | 150 | #undef __max // Seriously stdlib? 151 | #undef __min 152 | 153 | DEFINE_OPERATION( operator~( T1&& a ) { return { vtil::math::operator_id::bitwise_not, std::forward( a ) }; } ); 154 | DEFINE_OPERATION( operator&( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::bitwise_and, std::forward( b ) }; } ); 155 | DEFINE_OPERATION( operator|( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::bitwise_or, std::forward( b ) }; } ); 156 | DEFINE_OPERATION( operator^( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::bitwise_xor, std::forward( b ) }; } ); 157 | DEFINE_OPERATION( operator>>( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::shift_right, std::forward( b ) }; } ); 158 | DEFINE_OPERATION( operator<<( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::shift_left, std::forward( b ) }; } ); 159 | DEFINE_OPERATION( __rotr( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::rotate_right, std::forward( b ) }; } ); 160 | DEFINE_OPERATION( __rotl( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::rotate_left, std::forward( b ) }; } ); 161 | DEFINE_OPERATION( operator-( T1&& a ) { return { vtil::math::operator_id::negate, std::forward( a ) }; } ); 162 | DEFINE_OPERATION( operator+( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::add, std::forward( b ) }; } ); 163 | DEFINE_OPERATION( operator-( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::subtract, std::forward( b ) }; } ); 164 | DEFINE_OPERATION( imulhi( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::multiply_high, std::forward( b ) }; } ); 165 | DEFINE_OPERATION( operator*( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::multiply, std::forward( b ) }; } ); 166 | DEFINE_OPERATION( operator/( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::divide, std::forward( b ) }; } ); 167 | DEFINE_OPERATION( operator%( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::remainder, std::forward( b ) }; } ); 168 | DEFINE_OPERATION( umulhi( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::umultiply_high, std::forward( b ) }; } ); 169 | DEFINE_OPERATION( umul( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::umultiply, std::forward( b ) }; } ); 170 | DEFINE_OPERATION( udiv( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::udivide, std::forward( b ) }; } ); 171 | DEFINE_OPERATION( urem( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::uremainder, std::forward( b ) }; } ); 172 | DEFINE_OPERATION( __ucast( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::ucast, std::forward( b ) }; } ); 173 | DEFINE_OPERATION( __cast( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::cast, std::forward( b ) }; } ); 174 | DEFINE_OPERATION( __popcnt( T1&& a ) { return { vtil::math::operator_id::popcnt, std::forward( a ) }; } ); 175 | DEFINE_OPERATION( __bt( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::bit_test, std::forward( b ) }; } ); 176 | DEFINE_OPERATION( __mask( T1&& a ) { return { vtil::math::operator_id::mask, std::forward( a ) }; } ); 177 | DEFINE_OPERATION( __bcnt( T1&& a ) { return { vtil::math::operator_id::bit_count, std::forward( a ) }; } ); 178 | DEFINE_OPERATION( __if( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::value_if, std::forward( b ) }; } ); 179 | DEFINE_OPERATION( __max( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::max_value, std::forward( b ) }; } ); 180 | DEFINE_OPERATION( __min( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::min_value, std::forward( b ) }; } ); 181 | DEFINE_OPERATION( __umax( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::umax_value, std::forward( b ) }; } ); 182 | DEFINE_OPERATION( __umin( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::umin_value, std::forward( b ) }; } ); 183 | DEFINE_OPERATION( operator>( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::greater, std::forward( b ) }; } ); 184 | DEFINE_OPERATION( operator>=( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::greater_eq, std::forward( b ) }; } ); 185 | DEFINE_OPERATION( operator==( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::equal, std::forward( b ) }; } ); 186 | DEFINE_OPERATION( operator!=( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::not_equal, std::forward( b ) }; } ); 187 | DEFINE_OPERATION( operator<=( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::less_eq, std::forward( b ) }; } ); 188 | DEFINE_OPERATION( operator<( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::less, std::forward( b ) }; } ); 189 | DEFINE_OPERATION( __ugreat( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::ugreater, std::forward( b ) }; } ); 190 | DEFINE_OPERATION( __ugreat_eq( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::ugreater_eq, std::forward( b ) }; } ); 191 | DEFINE_OPERATION( __uless_eq( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::uless_eq, std::forward( b ) }; } ); 192 | DEFINE_OPERATION( __uless( T1&& a, T2&& b ) { return { std::forward( a ), vtil::math::operator_id::uless, std::forward( b ) }; } ); 193 | #undef DEFINE_OPERATION -------------------------------------------------------------------------------- /math/operators.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "bitwise.hpp" 35 | 36 | namespace vtil::math 37 | { 38 | enum class operator_id : uint8_t 39 | { 40 | invalid, // = 41 | 42 | // ------------------ Bitwise Operators ------------------ // 43 | 44 | // Bitwise modifiers: 45 | // 46 | bitwise_not, // ~RHS 47 | 48 | // Basic bitwise operations: 49 | // 50 | bitwise_and, // LHS&(RHS&...) 51 | bitwise_or, // LHS|(RHS|...) 52 | bitwise_xor, // LHS^(RHS^...) 53 | 54 | // Distributing bitwise operations: 55 | // 56 | shift_right, // LHS>>(RHS+...) 57 | shift_left, // LHS<<(RHS+...) 58 | rotate_right, // LHS>](RHS+...) 59 | rotate_left, // LHS[<(RHS+...) 60 | 61 | // ---------------- Arithmetic Operators ----------------- // 62 | 63 | // Arithmetic modifiers: 64 | // 65 | negate, // -RHS 66 | 67 | // Basic arithmetic operations: 68 | // 69 | add, // LHS+(RHS+...) 70 | subtract, // LHS-(RHS+...) 71 | 72 | // Distributing arithmetic operations: 73 | // 74 | multiply_high, // HI(LHS*RHS) 75 | multiply, // LHS*(RHS*...) 76 | divide, // LHS/(RHS*...) 77 | remainder, // LHS%RHS 78 | 79 | umultiply_high, // < Unsigned variants of above > 80 | umultiply, // 81 | udivide, // 82 | uremainder, // 83 | 84 | // ----------------- Special Operators ----------------- // 85 | ucast, // uintRHS_t(LHS, RHS) 86 | cast, // intRHS_t(LHS, RHS) 87 | popcnt, // POPCNT(RHS) 88 | bit_test, // [LHS>>RHS]&1 89 | mask, // RHS.mask() 90 | bit_count, // RHS.bitcount() 91 | value_if, // LHS&1 ? RHS : 0 92 | 93 | max_value, // LHS>=RHS ? LHS : RHS 94 | min_value, // LHS<=RHS ? LHS : RHS 95 | 96 | umax_value, // < Unsigned variants of above > 97 | umin_value, // 98 | 99 | greater, // LHS > RHS 100 | greater_eq, // LHS >= RHS 101 | equal, // LHS == RHS 102 | not_equal, // LHS != RHS 103 | less_eq, // LHS <= RHS 104 | less, // LHS < RHS 105 | 106 | ugreater, // < Unsigned variants of above > [Note: equal and not_equal are always unsigned.] 107 | ugreater_eq, // 108 | uless_eq, // 109 | uless, // 110 | max, 111 | }; 112 | 113 | // Basic properties of each operator. 114 | // 115 | struct operator_desc 116 | { 117 | // >0 if bitwise operations are preferred as operands, <0 if arithmetic, ==0 if neutral. 118 | // 119 | int hint_bitwise; 120 | 121 | // Whether it expects signed operands or not. 122 | // 123 | bool is_signed; 124 | 125 | // Number of operands it takes. Either 1 or 2. 126 | // 127 | size_t operand_count; 128 | 129 | // Whether the operation is commutative or not. 130 | // 131 | bool is_commutative; 132 | 133 | // Symbol of the operation. 134 | // 135 | const char* symbol; 136 | 137 | // Name of the function associated with the operation. 138 | // 139 | const char* function_name; 140 | 141 | // Creates a string representation based on the operands passed. 142 | // 143 | inline std::string to_string( const std::string& lhs, const std::string& rhs ) const 144 | { 145 | // If unary function: 146 | // 147 | if ( operand_count == 1 ) 148 | { 149 | // If it has a symbol, use it, else return in function format. 150 | // 151 | if ( symbol ) return symbol + rhs; 152 | else return format::str( "%s(%s)", function_name, rhs ); 153 | } 154 | // If binary function: 155 | // 156 | else if ( operand_count == 2 ) 157 | { 158 | // If it has a symbol, use it, else return in function format. 159 | // 160 | if ( symbol ) return format::str( "(%s%s%s)", lhs, symbol, rhs ); 161 | else return format::str( "%s(%s, %s)", function_name, lhs, rhs ); 162 | } 163 | unreachable(); 164 | } 165 | }; 166 | static constexpr operator_desc descriptors[] = 167 | { 168 | // Skipping ::invalid. 169 | {}, 170 | 171 | /* [Bitwise] [Signed] [#Op] [Commutative] [Symbol] [Name] */ 172 | { +1, false, 1, false, "~", "not" }, 173 | { +1, false, 2, true, "&", "and" }, 174 | { +1, false, 2, true, "|", "or" }, 175 | { +1, false, 2, true, "^", "xor" }, 176 | { +1, false, 2, false, ">>", "shr" }, 177 | { +1, false, 2, false, "<<", "shl" }, 178 | { +1, false, 2, false, ">]", "rotr" }, 179 | { +1, false, 2, false, "[<", "rotl" }, 180 | { -1, true, 1, false, "-", "neg" }, 181 | { -1, true, 2, true, "+", "add" }, 182 | { -1, true, 2, false, "-", "sub" }, 183 | { -1, true, 2, true, "h*", "mulhi" }, 184 | { -1, true, 2, true, "*", "mul" }, 185 | { -1, true, 2, false, "/", "div" }, 186 | { -1, true, 2, false, "%", "rem" }, 187 | { -1, false, 2, true, "uh*", "umulhi" }, 188 | { -1, false, 2, true, "u*", "umul" }, 189 | { -1, false, 2, false, "u/", "udiv" }, 190 | { -1, false, 2, false, "u%", "urem" }, 191 | { 0, false, 2, false, nullptr, "__ucast" }, 192 | { -1, true, 2, false, nullptr, "__cast" }, 193 | { +1, false, 1, false, nullptr, "__popcnt" }, 194 | { +1, false, 2, false, nullptr, "__bt" }, 195 | { +1, false, 1, false, nullptr, "__mask" }, 196 | { 0, false, 1, false, nullptr, "__bcnt" }, 197 | { 0, false, 2, false, "?", "if" }, 198 | { 0, false, 2, false, nullptr, "max" }, 199 | { 0, false, 2, false, nullptr, "min" }, 200 | { 0, true, 2, false, nullptr, "umax" }, 201 | { 0, true, 2, false, nullptr, "umin" }, 202 | { -1, true, 2, false, ">", "greater" }, 203 | { -1, true, 2, false, ">=", "greater_eq" }, 204 | { 0, false, 2, false, "==", "equal" }, 205 | { 0, false, 2, false, "!=", "not_equal" }, 206 | { -1, true, 2, false, "<=", "less_eq" }, 207 | { -1, true, 2, false, "<", "less" }, 208 | { 0, false, 2, false, "u>", "ugreater" }, 209 | { 0, false, 2, false, "u>=", "ugreater_eq" }, 210 | { 0, false, 2, false, "u<=", "uless_eq" }, 211 | { 0, false, 2, false, "u<", "uless" }, 212 | }; 213 | static_assert( std::size( descriptors ) == size_t( operator_id::max ), "Operator descriptor table is invalid." ); 214 | inline static const operator_desc* descriptor_of( operator_id id ) { return ( operator_id::invalid < id && id < operator_id::max ) ? &descriptors[ ( size_t ) id ] : nullptr; } 215 | 216 | // Operators that return bit-indices, always use the following size. 217 | // 218 | static constexpr bitcnt_t bit_index_size = 8; 219 | 220 | // Before operators return their result, the result size is always 221 | // rounded up to either 1, 8, 16, 32 or 64 (where available). 222 | // 223 | inline static constexpr bitcnt_t round_bit_count( bitcnt_t n ) 224 | { 225 | if ( n > 32 ) return 64; 226 | else if ( n > 16 ) return 32; 227 | else if ( n > 8 ) return 16; 228 | else if ( n > 1 ) return 8; 229 | else return 1; 230 | } 231 | 232 | // Calculates the size of the result after after the application of the operator [id] on the operands. 233 | // 234 | bitcnt_t result_size( operator_id id, bitcnt_t bcnt_lhs, bitcnt_t bcnt_rhs ); 235 | 236 | // Applies the specified operator [id] on left hand side [lhs] and right hand side [rhs] 237 | // and returns the output as a masked unsigned 64-bit integer <0> and the final size <1>. 238 | // 239 | std::pair evaluate( operator_id id, bitcnt_t bcnt_lhs, uint64_t lhs, bitcnt_t bcnt_rhs, uint64_t rhs ); 240 | 241 | // Applies the specified operator [op] on left hand side [lhs] and right hand side [rhs] wher 242 | // input and output values are expressed in the format of bit-vectors with optional unknowns, 243 | // and no size constraints. 244 | // 245 | bit_vector evaluate_partial( operator_id op, const bit_vector& lhs, const bit_vector& rhs ); 246 | }; -------------------------------------------------------------------------------- /query/fixed_iterator.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "..\io\asserts.hpp" 34 | #include "range_iterator.hpp" 35 | 36 | // Fixed iterators are used to create simple iterators that are valid range iterators 37 | // from a structure that is not based on a container, but still benefits from a common 38 | // enumeration method and vtil::query system. 39 | // 40 | namespace vtil::query 41 | { 42 | // Implement a fixed iterator end that redirects comparison to .is_end(), 43 | // in a similar way to how nullptr and nullopt works 44 | // 45 | struct fixed_iterator_end_t 46 | { 47 | struct _tag {}; 48 | constexpr explicit fixed_iterator_end_t( _tag ) {} 49 | }; 50 | inline constexpr fixed_iterator_end_t fixed_iterator_end{ fixed_iterator_end_t::_tag{} }; 51 | 52 | // If vector entries are not pointers, they will be used to generate 53 | // fake const_iterators mapping to each entry. 54 | // 55 | template 56 | struct fixed_iterator 57 | { 58 | // Export the required traits of range iterators. 59 | // 60 | using container_type = _container_type; 61 | using value_type = _value_type; 62 | using iterator_type = const value_type*; 63 | using recurse_function = std::vector(*)( container_type* self, bool forward ); 64 | 65 | // Export the required traits of an standard iterator. 66 | // 67 | using iterator_category = std::random_access_iterator_tag; 68 | using difference_type = size_t; 69 | using pointer = const value_type*; 70 | using reference = const value_type&; 71 | 72 | // Fixed iterator consists of a container, a vector that 73 | // holds the values to be iterated, and a optional recursion 74 | // helper that describes what to do on a recursion attempt. 75 | // 76 | container_type* container = nullptr; 77 | std::vector fixed_range = {}; 78 | recurse_function recurse_helper = nullptr; 79 | size_t at = 0; 80 | 81 | // Implement basic requirements of range iterators and data access. 82 | // 83 | reference operator*() const { return fixed_range[ at ]; } 84 | pointer operator->() const { return &fixed_range[ at ]; } 85 | bool is_end() const { return at == fixed_range.size(); } 86 | bool is_begin() const { return at == 0; } 87 | bool is_valid() const { return container && at < fixed_range.size(); } 88 | 89 | // Redirect to helper where relevant, if not return empty vector. 90 | // 91 | std::vector recurse( bool forward ) const 92 | { 93 | if ( !container || !recurse_helper ) 94 | return {}; 95 | return recurse_helper( container, forward ); 96 | } 97 | }; 98 | 99 | // If vector entries are pointers, they will be used to generate 100 | // fake iterators mapping to the address pointed by each entry instead. 101 | // 102 | template 103 | struct fixed_iterator<_container_type, _value_type*> 104 | { 105 | // Export the required traits of range iterators. 106 | // 107 | using container_type = _container_type; 108 | using value_type = _value_type; 109 | using iterator_type = value_type*; 110 | using recurse_function = std::vector(*)(container_type* self, bool forward); 111 | 112 | // Export the required traits of an standard iterator. 113 | // 114 | using iterator_category = std::random_access_iterator_tag; 115 | using difference_type = size_t; 116 | using pointer = value_type*; 117 | using reference = value_type&; 118 | 119 | // Fixed iterator consists of a container, a vector that 120 | // holds the values to be iterated, and a optional recursion 121 | // helper that describes what to do on a recursion attempt. 122 | // 123 | container_type* container = nullptr; 124 | std::vector fixed_range = {}; 125 | recurse_function recurse_helper = nullptr; 126 | size_t at = 0; 127 | 128 | // Implement basic requirements of range iterators and data access. 129 | // 130 | reference operator*() const { return *fixed_range[ at ]; } 131 | value_type operator->() const { return fixed_range[ at ]; } 132 | bool is_end() const { return at == fixed_range.size(); } 133 | bool is_begin() const { return at == 0; } 134 | bool is_valid() const { return container && at < fixed_range.size(); } 135 | 136 | // Redirect to helper where relevant, if not return empty vector. 137 | // 138 | std::vector recurse( bool forward ) const 139 | { 140 | if ( !container || !recurse_helper ) 141 | return {}; 142 | return recurse_helper( container, forward ); 143 | } 144 | }; 145 | }; 146 | 147 | // Implement random-access iterator properties, by redirecting to size_t ::at. 148 | // 149 | template 150 | static auto& operator+=( vtil::query::fixed_iterator& a, size_t i ) { a.at += i; return a; } 151 | template 152 | static auto& operator++( vtil::query::fixed_iterator& a ) { a.at++; return a; } 153 | template 154 | static auto& operator--( vtil::query::fixed_iterator& a ) { a.at--; return a; } 155 | template 156 | static size_t operator-( const vtil::query::fixed_iterator& a, 157 | const vtil::query::fixed_iterator& b ) 158 | { 159 | fassert( a.container == b.container && a.fixed_range == b.fixed_range ); 160 | return a.at - b.at; 161 | } 162 | 163 | // Implement equality comparison between same type and the end type. 164 | // 165 | template 166 | static bool operator==( const vtil::query::fixed_iterator& a, compared_type&& b ) 167 | { 168 | // Assert sanity of the comparison. 169 | // 170 | constexpr bool compare_w_fixed_end = std::is_same_v, vtil::query::fixed_iterator_end_t>; 171 | constexpr bool compare_w_iterator = std::is_same_v, vtil::query::fixed_iterator>; 172 | static_assert( compare_w_fixed_end || compare_w_iterator, "Invalid fixed type comparison." ); 173 | 174 | // If comparing against fixed_iteartor_end, just check if a is at the end. 175 | // 176 | if constexpr ( compare_w_fixed_end ) 177 | return a.is_end(); 178 | 179 | // Othewrise compare every property. 180 | // 181 | if constexpr( compare_w_iterator ) 182 | return a.container == b.container && a.fixed_range == b.fixed_range && a.at == b.at; 183 | 184 | } 185 | template 186 | static bool operator!=( const vtil::query::fixed_iterator& a, compared_type&& b ) { return !operator==( a, std::forward( b ) ); } -------------------------------------------------------------------------------- /query/query_descriptor.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "range_iterator.hpp" 34 | 35 | namespace vtil::query 36 | { 37 | // Query descriptor is a structure describing the iteration state of any 38 | // query object in dependent to the currently projected type. 39 | // 40 | template 41 | struct query_desc 42 | { 43 | using iterator_type = _iterator_type; 44 | using reference_type = decltype( *std::declval() ); 45 | 46 | // Current range iterator. 47 | // 48 | iterator_type iterator = {}; 49 | 50 | // Direction of iteration: 51 | // => +1 for forward 52 | // => -1 for backwar 53 | // 54 | int8_t direction = 0; 55 | 56 | // Iteration function let's us define a generic iteration logic. 57 | // 58 | // Returns: 59 | // - 1 if there's a valid result 60 | // - 0 if reached end of the stream 61 | // - -1 if terminated due to until(...) 62 | // 63 | using fn_controller = std::function; 64 | fn_controller controller = [ ] ( query_desc&, iterator_type ) { return 1; }; 65 | 66 | // Queries can be simply constructed from an iterator and an 67 | // optional direction value, where it defaults to forward iteration 68 | // if not .end(), backwards otherwise. 69 | // 70 | query_desc() = default; 71 | inline query_desc( iterator_type it, int8_t dir = 0 ) : iterator( it ) 72 | { 73 | if ( it.is_end() && !it.is_begin() ) 74 | direction = dir != 0 ? dir : -1; 75 | else 76 | direction = dir != 0 ? dir : +1; 77 | } 78 | 79 | // Wraps ::recurse(...) of range iterators, returning query descriptors. 80 | // 81 | std::vector recurse() const 82 | { 83 | // Return an empty list if direction is invalid. 84 | // 85 | if ( direction == 0 ) return {}; 86 | 87 | // Get the list of possible iterators we could continue from. 88 | // 89 | std::vector iterators = iterator.recurse( direction == +1 ); 90 | 91 | // Convert into query descriptors. 92 | // 93 | std::vector query_descriptors; 94 | for ( iterator_type& it : iterators ) 95 | { 96 | // Create a default descriptor with the iterator and the direction, 97 | // afterwards propagate the iteration logic. 98 | // 99 | query_desc qd = { it, direction }; 100 | qd.controller = controller; 101 | query_descriptors.push_back( qd ); 102 | } 103 | return query_descriptors; 104 | } 105 | 106 | // Invalidates current query. 107 | // 108 | inline void stop() { iterator = prev(); direction = 0; } 109 | 110 | // Value that next() processed previously. 111 | // 112 | inline iterator_type prev() const { return direction != +1 ? iterator : ( iterator.is_begin() ? iterator_type{} : std::prev( iterator ) ); } 113 | 114 | // Value that next() will process next. 115 | // 116 | inline iterator_type next() const { return direction != -1 ? iterator : ( iterator.is_begin() ? iterator_type{} : std::prev( iterator ) ); } 117 | 118 | // Reverses the current query direction 119 | // 120 | void reverse() 121 | { 122 | // We have to fix the iterators since 123 | // .end() is valid for [-1] but not [+1] 124 | // and .begin() is valid for [+1] but not [-1]. 125 | // 126 | if ( direction == -1 ) 127 | { 128 | if ( !iterator.is_begin() ) --iterator; 129 | direction = +1; 130 | } 131 | else if ( direction == +1 ) 132 | { 133 | if ( !iterator.is_end() ) ++iterator; 134 | direction = -1; 135 | } 136 | } 137 | 138 | // Forwards the iterator in the specified direction [n] times. 139 | // 140 | int forward( int n = 1 ) 141 | { 142 | // Until we exhaust the item counter: 143 | // 144 | while ( n > 0 ) 145 | { 146 | // If direction is backwards: 147 | // 148 | if ( direction == -1 ) 149 | { 150 | // If we've reached .begin(), break. 151 | // 152 | if ( iterator.is_begin() ) 153 | break; 154 | 155 | // Point the iterator at the current item. 156 | // 157 | --iterator; 158 | 159 | // If invalid, break. 160 | // 161 | if ( !iterator.is_valid() ) 162 | break; 163 | } 164 | // If direction is forwards: 165 | // 166 | else if ( direction == +1 ) 167 | { 168 | // If we've reached .end(), break. 169 | // 170 | if ( iterator.is_end() ) 171 | break; 172 | } 173 | // If no direction specified, break. 174 | // 175 | else 176 | { 177 | break; 178 | } 179 | 180 | // Invoke the iteration logic. 181 | // 182 | int res = controller( *this, iterator ); 183 | 184 | // If direction was forward, increment the iterator now. 185 | // 186 | if ( direction == +1 ) 187 | ++iterator; 188 | 189 | // If a breaking condition was satisfied, report so. 190 | // 191 | if ( res == -1 ) 192 | return -1; 193 | 194 | // If filters were passed and we've exhausted the 195 | // item counter, report success. 196 | // 197 | if ( res == 1 && --n <= 0 ) 198 | return 1; 199 | } 200 | 201 | // Report end-of-stream. 202 | // 203 | return 0; 204 | } 205 | }; 206 | }; -------------------------------------------------------------------------------- /query/range_iterator.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include "range_iterator_contract.hpp" 32 | 33 | // Range iterators are cross-container iterators used by the VTIL queries in order to make the 34 | // iteration of trees, in our specific use case the basic blocks, easier. 35 | // 36 | namespace vtil::query 37 | { 38 | // Basic range iterators provide a simple range iterator implementation for default STL container and pretty much 39 | // any other container adhereing to their standards to be used with VTIL queries. 40 | // 41 | template, typename _container_type::const_iterator, typename _container_type::iterator>> 43 | struct basic_range_iterator : _iterator_type 44 | { 45 | using container_type = _container_type; 46 | using iterator_type = _iterator_type; 47 | 48 | // Reference to the container. 49 | // 50 | container_type* container = nullptr; 51 | 52 | // Default constructor and the container-bound constructor. 53 | // 54 | basic_range_iterator() = default; 55 | basic_range_iterator( container_type* container, iterator_type i ) : iterator_type( i ), container( container ) {} 56 | template basic_range_iterator( const basic_range_iterator& o ) : container( o.container ), iterator_type( Y( o ) ) {} 57 | 58 | // Override equality operators to check container first. 59 | // 60 | bool operator==( const basic_range_iterator& o ) const { return container == o.container && iterator_type::operator==( o ); } 61 | bool operator!=( const basic_range_iterator& o ) const { return container != o.container || iterator_type::operator!=( o ); } 62 | 63 | // Simple position/validity checks. 64 | // 65 | bool is_end() const { return !container || iterator_type::operator==( ( iterator_type ) container->end() ); } 66 | bool is_begin() const { return !container || iterator_type::operator==( ( iterator_type ) container->begin() ); } 67 | bool is_valid() const { return !is_begin() || !is_end(); } 68 | 69 | // No default implementation for recursion since STL has no default tree-based container. 70 | // 71 | std::vector recurse( bool forward ) const { return {}; } 72 | }; 73 | 74 | // Makes range iterator from any container and iterator combination based on basic_range_iterator. 75 | // 76 | template 77 | static auto bind( container_type& container, iterator_type iterator ) { return basic_range_iterator{ &container, iterator }; } 78 | 79 | // Make sure contract is being abided. 80 | // 81 | static_assert 82 | ( 83 | is_range_iterator_v, std::vector::iterator>>&& 84 | is_range_iterator_v, std::vector::const_iterator>>&& 85 | "Basic range iterator does not abide by the range iterator contract." 86 | ); 87 | }; -------------------------------------------------------------------------------- /query/range_iterator_contract.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | 32 | // We implement a simple type-traits like template to check if an arbitrary type 33 | // abides by the range iterator contract. 34 | // 35 | namespace vtil::query 36 | { 37 | namespace impl 38 | { 39 | template 40 | static constexpr bool _is_range_iterator( bool ) 41 | { 42 | using T = typename std::remove_cvref_t<_T>; 43 | 44 | /// Must a valid standard random-access iterator. 45 | // - T::operator+=(); 46 | // - T::operator++(); 47 | // - T::operator--(); 48 | // 49 | using dist_type = decltype( std::declval() - std::declval() ); 50 | using next_type = decltype( std::next( std::declval() ) ); 51 | using prev_type = decltype( std::prev( std::declval() ) ); 52 | using dec_type = decltype( --std::declval() ); 53 | using inc_type = decltype( ++std::declval() ); 54 | using ref_type = decltype( std::declval().operator*() ); 55 | using ptr_type = decltype( std::declval().operator->() ); 56 | 57 | // Must have a public member called .container that is of type T::container_type*. 58 | // - T::container_type* T::container; 59 | // 60 | using container_type_a = std::remove_pointer_t().container )>; 61 | using container_type_b = typename T::container_type; 62 | if ( !std::is_same_v ) 63 | return false; 64 | 65 | // Must have the basic comparison checks implemented. 66 | // - bool T::operator==(const T& o); 67 | // - bool T::operator!=(const T& o); 68 | // 69 | using comparison_assertation_1 = decltype( bool( std::declval() == std::declval() ) ); 70 | using comparison_assertation_2 = decltype( bool( std::declval() != std::declval() ) ); 71 | 72 | // Must have the container-abstracting range checks implemented. 73 | // - bool T::is_begin(); 74 | // - bool T::is_end(); 75 | // - bool T::is_valid(); 76 | // 77 | using range_check_1 = decltype( bool( std::declval().is_begin() ) ); 78 | using range_check_2 = decltype( bool( std::declval().is_end() ) ); 79 | using range_check_3 = decltype( bool( std::declval().is_valid() ) ); 80 | 81 | // Must have recursion helpers implemented, even if no-op. 82 | // 83 | // - container_type T::recurse(bool) 84 | // - container_iterator_type container_type::begin() 85 | // - container_iterator_type container_type::end() 86 | // 87 | using recurse_retval = decltype( std::declval().recurse( false ) ); 88 | using container_iterator_begin = decltype( std::declval().begin() ); 89 | using container_iterator_end = decltype( std::declval().end() ); 90 | using container_iterator_value = std::remove_cvref_t() )>; 91 | using container_iterator_pointer = std::remove_cvref_t().operator->() )>; 92 | 93 | return std::is_same_v, T> && 94 | std::is_same_v, T*>; 95 | } 96 | 97 | template 98 | static constexpr bool _is_range_iterator( ... ) { return false; } 99 | }; 100 | template 101 | static constexpr bool is_range_iterator_v = impl::_is_range_iterator( true ); 102 | }; -------------------------------------------------------------------------------- /query/recursive_view.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include "view.hpp" 32 | #include "query_descriptor.hpp" 33 | #include "..\io\asserts.hpp" 34 | 35 | namespace vtil::query 36 | { 37 | // Recursive results are used to collect results 38 | // in a way that clearly indicates the path taken 39 | // to get the result, and the source container. 40 | // 41 | template 42 | struct recursive_result 43 | { 44 | // Whether we've visited this container before or not. 45 | // 46 | bool is_looping = false; 47 | 48 | // The container that the result belong to. 49 | // 50 | const container_type* source = nullptr; 51 | 52 | // Result of the collection. 53 | // 54 | result_type result; 55 | 56 | // Results of deeper recursions. 57 | // 58 | std::vector paths; 59 | 60 | // Merges the results of all or extended basic-blocks. 61 | // 62 | template 63 | struct is_std_vector : std::false_type {}; 64 | template 65 | struct is_std_vector> : std::true_type {}; 66 | recursive_result& flatten( bool force = false ) 67 | { 68 | // Apply to each path recursively. 69 | // 70 | for ( auto& path : paths ) 71 | path = path.flatten(); 72 | 73 | // If single possible path or force mode: 74 | // 75 | if ( paths.size() == 1 || force ) 76 | { 77 | std::vector paths_p = paths; 78 | paths.clear(); 79 | 80 | for ( auto& r : paths_p ) 81 | { 82 | // Merge basic result. 83 | // 84 | is_looping |= r.is_looping; 85 | if( !r.paths.empty() ) 86 | paths.insert( paths.end(), r.paths.begin(), r.paths.end() ); 87 | 88 | // Either combine vectors or use the addition operator. 89 | // 90 | if constexpr ( is_std_vector::value ) 91 | result.insert( result.end(), r.result.begin(), r.result.end() ); 92 | else 93 | result += r.result; 94 | } 95 | } 96 | 97 | // Return as is. 98 | // 99 | return *this; 100 | } 101 | }; 102 | 103 | template 104 | struct recursive_view 105 | { 106 | // Base view and its typedefs. 107 | // 108 | using iterator_type = typename view_type::iterator_type; 109 | using projected_type = typename view_type::projected_type; 110 | using container_type = typename iterator_type::container_type; 111 | view_type view; 112 | 113 | // Container filters determine whether we should 114 | // recurse into the passed container or not. 115 | // 116 | using fn_container_filter = std::function; 117 | fn_container_filter filter = {}; 118 | 119 | // Special iterator saved by the root to mark the 120 | // beginning of it's iteration so loops can properly 121 | // lead to it. 122 | // 123 | iterator_type it0 = {}; 124 | bool it0_oob = false; 125 | 126 | // List of containers that we've recursively visited. 127 | // The second argument of the container filter, first_time, 128 | // is determined by whether the container we're trying to 129 | // visit is in this list or not. 130 | // 131 | std::set visited = {}; 132 | 133 | // Constructs a recursive view from the view structure passed. 134 | // 135 | // - If partial visits are allowed, in case of an infinite loop, 136 | // still iterates up to the starting point of view, first_time will 137 | // be set to true in this case when we reach the root container. 138 | // 139 | // - Filter is a function that takes the pointer to the next container 140 | // and whether it's being visit for the first time or not and returns 141 | // whether we should visit it or not. 142 | // 143 | // 144 | recursive_view() = default; 145 | recursive_view( const view_type& view, bool partial_visits, fn_container_filter filter ) : view( view ), filter( filter ) 146 | { 147 | // If partial visits are allowed: 148 | // 149 | if ( partial_visits ) 150 | { 151 | // Set it0 as the next iterator, if invalid (meaning we'll skip to next 152 | // container right away) set the container property. 153 | // 154 | it0 = view.query.next(); 155 | if ( !it0.is_valid() ) 156 | { 157 | it0.container = view.query.iterator.container; 158 | it0_oob = true; 159 | } 160 | } 161 | else 162 | { 163 | // Assing an invalid iterator to it0 and mark current 164 | // iterator's container visited. 165 | // 166 | it0 = {}; 167 | visited.insert( view.query.iterator.container ); 168 | } 169 | } 170 | 171 | // Simply clones the current state. 172 | // 173 | recursive_view clone() 174 | { 175 | return *this; 176 | } 177 | 178 | // Simple wrappers around the real view. 179 | // - If body only contains unreachable(), call is not valid for recursive view. 180 | // - Collection must not have started yet, otherwise calls are invalid. 181 | // 182 | void prev() { unreachable(); } 183 | void next() { unreachable(); } 184 | void skip( int n = 1 ) { unreachable(); } 185 | void last() { unreachable(); } 186 | 187 | auto& reverse() { view.reverse(); return *this; } 188 | template auto& run( T next ) { view.run( next ); return *this; } 189 | template auto& with( T next ) { view.with( next ); return *this; } 190 | template auto& where( T next ) { view.where( next ); return *this; } 191 | template auto& until( T next ) { view.until( next ); return *this; } 192 | template auto& whilst( T next ) { view.whilst( next ); return *this; } 193 | 194 | template 195 | auto project( projector_type next ) { return recursive_view{ view.project( next ), it0.container != nullptr, filter }; } 196 | template 197 | auto reproject( projector_type next ) { return recursive_view{ view.reproject( next ), it0.container != nullptr, filter }; } 198 | auto unproject() { return recursive_view{ view.unproject(), it0.container != nullptr, filter }; } 199 | 200 | // [Collection method] 201 | // Invokes the enumerator for each entry, if enumerator returns void/bool 202 | // saves the number of (?=true) entries, otherwise collects the return value 203 | // in std::vector<> and saves that in the recursive_result structure. 204 | // Continues appending paths and results in that structure until 205 | // stream is finished. 206 | // 207 | template()( std::declval() ) ), 209 | typename result_type = std::conditional_t || std::is_same_v, size_t, std::vector> 210 | > 211 | recursive_result for_each( const enumerator_type& enumerator ) 212 | { 213 | // Begin the iteration loop. 214 | // 215 | recursive_result output = { false, view.query.iterator.container, {}, {} }; 216 | while ( true ) 217 | { 218 | int r = view.query.forward(); 219 | 220 | // If a breaking condition was satisfied, end the loop. 221 | // 222 | if ( r == -1 ) 223 | { 224 | break; 225 | } 226 | // If entry passed the filters, append the result and continue. 227 | // 228 | else if ( r == 1 ) 229 | { 230 | if constexpr ( std::is_same_v ) 231 | output.result++, enumerator( view.prev() ); 232 | else if constexpr ( std::is_same_v ) 233 | output.result += enumerator( view.prev() ); 234 | else 235 | output.result.push_back( enumerator( view.prev() ) ); 236 | } 237 | // If we've reached the end of the stream, try recursing. 238 | // 239 | else 240 | { 241 | // For each plausible path: 242 | // 243 | std::vector desc_list = view.query.recurse(); 244 | for ( auto& desc : desc_list ) 245 | { 246 | auto visited_copy = visited; 247 | 248 | // If we did not already visit it: 249 | // 250 | bool first_visit = visited_copy.find( desc.iterator.container ) == visited_copy.end(); 251 | if ( filter( view.query.iterator.container, desc.iterator.container, first_visit ) ) 252 | { 253 | // Mark the container visited. 254 | // 255 | visited_copy.insert( desc.iterator.container ); 256 | 257 | // Create another recursive view with the new query. 258 | // 259 | recursive_view view_new = clone(); 260 | view_new.view.query = desc; 261 | view_new.visited = visited_copy; 262 | 263 | // If iterator belongs to the same container as the root: 264 | // 265 | bool partial_loop = desc.iterator.container == it0.container; 266 | if ( partial_loop ) 267 | { 268 | // Append an additional rule to the iteration. 269 | // 270 | if( !it0_oob ) 271 | view_new.view = view_new.view.until( it0 ); 272 | } 273 | 274 | // Invoke the same enumerator and append as a path. 275 | // 276 | recursive_result result = view_new.for_each( enumerator ); 277 | result.is_looping = partial_loop; 278 | output.paths.push_back( result ); 279 | } 280 | // Otherwise: 281 | // 282 | else 283 | { 284 | // Append an empty path marked as a loop. 285 | // 286 | output.paths.push_back( { true, view.query.iterator.container, {}, {} } ); 287 | } 288 | } 289 | 290 | // Break out. 291 | // 292 | break; 293 | } 294 | } 295 | 296 | // Return the final result. 297 | // 298 | return output; 299 | } 300 | 301 | // [Collection method] 302 | // Collects each entry in std::vector<> and saves that in the 303 | // recursive_result structure. Continues appending paths and 304 | // results in that structure until stream is finished. 305 | // 306 | auto collect() 307 | { 308 | return for_each( [ ] ( projected_type r ) { return r; } ); 309 | } 310 | 311 | // [Collection method] 312 | // Evaluates the iteration logic and returns the number of hits 313 | // in terms of recursive_result. 314 | // 315 | auto evaluate() 316 | { 317 | return for_each( [ ] ( projected_type r ) {} ); 318 | } 319 | 320 | // [Collection method] 321 | // Collects first entry in std::vector<>, saves that in the 322 | // recursive_result structure and stops if applicable. 323 | // Otherwise continues appending paths in that structure 324 | // until a valid entry is hit. 325 | // 326 | auto first() 327 | { 328 | auto prev = view.query.controller; 329 | view.query.controller = [ prev ] ( auto& self, iterator_type i ) -> int 330 | { 331 | // If current iterator reports end or filtered-out, 332 | // return as is. 333 | // 334 | int res = prev( self, i ); 335 | if ( res <= 0 ) 336 | return res; 337 | 338 | // Else, stop the query and return it to be processed. 339 | // 340 | self.stop(); 341 | return 1; 342 | }; 343 | return collect(); 344 | } 345 | }; 346 | 347 | // Converts the view to a recursive view. Since recursive view only contains collection 348 | // methods, all filtering/projection/iteration-logic should be processed at the base view. 349 | // 350 | template 351 | static auto recurse( view_type view, typename recursive_view::fn_container_filter filter = {}, bool partial_visits = true, bool safe = true ) 352 | { 353 | return recursive_view( view, partial_visits, [ filter, safe ] ( auto* src, auto* dst, bool first_time ) 354 | { 355 | return ( !safe || first_time ) && ( !filter || filter( src, dst, true ) ); 356 | } ); 357 | } 358 | 359 | // Creates a reference-view query for the given query base. 360 | // 361 | template::reference_type, query_desc> 363 | > 364 | static auto create_recursive( query_desc q, typename recursive_view::fn_container_filter filter = {}, bool partial_visits = true, bool safe = true ) 365 | { 366 | return recurse 367 | ( 368 | view_type( q ), 369 | filter, 370 | partial_visits, 371 | safe 372 | ); 373 | } 374 | 375 | // Creates a reference-view query for the given range iterator. 376 | // 377 | template::reference_type, query_desc> 379 | > 380 | static auto create_recursive( iterator_type r, int8_t dir = 0, typename recursive_view::fn_container_filter filter = {}, bool partial_visits = true, bool safe = true ) 381 | { 382 | return create_recursive( query_desc{ r, dir }, filter, partial_visits, safe ); 383 | } 384 | }; -------------------------------------------------------------------------------- /util/concept.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | 33 | // Poor man's concept, until C++20 hits widespread. 34 | // 35 | namespace vtil 36 | { 37 | // Abuse SFINAE to detect feasability. 38 | // 39 | template 40 | static constexpr auto test_concept( ... ) -> int; 41 | template 42 | static constexpr auto test_concept( bool x )->std::void_t()... ) )>; 43 | template 44 | static constexpr bool test_concept_v = std::is_same_v( false ) ), void>; 45 | 46 | // Define a base type to generalize the checks. 47 | // 48 | template typename C, typename... T> 49 | struct concept_base 50 | { 51 | static constexpr bool apply() { return test_concept_v, T...>; } 52 | }; 53 | }; -------------------------------------------------------------------------------- /util/copy_on_write.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include "..\io\asserts.hpp" 33 | 34 | // Define _AddressOfReturnAddress() for compilers that do not have it. 35 | // 36 | #if not defined(_MSC_VER) and not defined(__INTELLISENSE__) 37 | #define _AddressOfReturnAddress() __builtin_frame_address(0) 38 | #endif 39 | 40 | // The copy-on-write interface defined here is used to avoid deep duplications of 41 | // containers such as trees when a VTIL routine is working with them. 42 | // 43 | namespace vtil 44 | { 45 | namespace impl 46 | { 47 | template struct param_pack_first { using type = std::tuple_element_t<0, std::tuple>; }; 48 | template<> struct param_pack_first<> { using type = void; }; 49 | 50 | template 51 | static constexpr bool should_invoke_constructor() 52 | { 53 | // Constructor should be always invoked if we have more than one parameter and 54 | // never if we have zero parameters. 55 | // 56 | if constexpr ( sizeof...( params ) != 1 ) 57 | { 58 | return sizeof...( params ) != 0; 59 | } 60 | else 61 | { 62 | // Extract first parameter. 63 | // 64 | using first_param_t = typename param_pack_first::type; 65 | 66 | // Invoke if not equal to the reference type. 67 | // 68 | return !std::is_same_v, T>; 69 | } 70 | } 71 | 72 | template 73 | using enable_if_constructor = typename std::enable_if_t()>; 74 | 75 | 76 | template 77 | inline static std::shared_ptr make_shared( params&&... args ) 78 | { 79 | std::shared_ptr out = std::make_shared( std::forward( args )... ); 80 | 81 | // Billion dollar company yes? 82 | // 83 | #ifdef __INTEL_COMPILER 84 | { 85 | std::weak_ptr __tmp = out; 86 | new ( &__tmp ) std::weak_ptr{}; 87 | } 88 | #endif 89 | return out; 90 | } 91 | 92 | template 93 | inline static T* reloc_const( const T* ptr, const void* src, void* dst ) 94 | { 95 | int64_t reloc_delta = ( int64_t ) dst - ( int64_t ) src; 96 | return ( T* ) ( ( size_t ) ptr + reloc_delta ); 97 | } 98 | 99 | template 100 | inline static T& reloc_const( const T& ref, const void* src, void* dst ) 101 | { 102 | int64_t reloc_delta = ( int64_t ) dst - ( int64_t ) src; 103 | return *( T* ) ( ( size_t ) &ref + reloc_delta ); 104 | } 105 | }; 106 | 107 | // This structure is used to describe copy-on-write references. 108 | // 109 | template 110 | struct shared_reference 111 | { 112 | // The original reference and current state. 113 | // 114 | std::shared_ptr reference; 115 | bool is_owning = false; 116 | bool is_locked = false; 117 | 118 | // Null reference construction. 119 | // 120 | shared_reference() : reference( nullptr ) {} 121 | shared_reference( std::nullptr_t ) : reference( nullptr ) {} 122 | 123 | // Owning reference constructor. 124 | // 125 | template, params...>> 126 | shared_reference( params&&... p ) : reference( impl::make_shared( std::forward( p )... ) ), is_owning( true ) {} 127 | 128 | // Copy-on-write reference construction and assignment. 129 | // 130 | shared_reference( const shared_reference& ref ) : reference( ref.reference ), is_locked( ref.is_locked ) {} 131 | shared_reference& operator=( const shared_reference& o ) { reference = o.reference; is_locked = o.is_locked; is_owning = false; return *this; } 132 | 133 | // Construction and assignment operator for rvalue references. 134 | // 135 | shared_reference( shared_reference&& ref ) = default; 136 | shared_reference& operator=( shared_reference&& o ) = default; 137 | 138 | // Simple validity checks. 139 | // 140 | bool is_valid() const { return ( bool ) reference; } 141 | operator bool() const { return is_valid(); } 142 | 143 | // Locks the current reference, a locked reference cannot be upgraded 144 | // to a copy-on-write reference as is. 145 | // 146 | shared_reference& lock() { is_locked = true; is_owning = false; return *this; } 147 | 148 | // Unlocks the current reference, should be called before storing the reference. 149 | // 150 | shared_reference& unlock() 151 | { 152 | // If reference is locked, we need to copy it. 153 | // 154 | if ( is_locked ) 155 | { 156 | // Create a copy and change reference to point at it. 157 | // 158 | reference = impl::make_shared( *reference ); 159 | 160 | // Mark as unlocked and owning. 161 | // 162 | is_locked = true; 163 | is_owning = true; 164 | } 165 | return *this; 166 | } 167 | 168 | // Converts this reference to an owning one if it is not one already and 169 | // returns the pointer to the base type with no const-qualifiers. 170 | // 171 | T* own() 172 | { 173 | fassert( is_valid() ); 174 | 175 | // If copy-on-write, convert to owning first. 176 | // 177 | if ( !is_owning ) 178 | { 179 | // If use counter is above 1 or reference is locked, we need 180 | // to make a copy before modifying the reference. 181 | // 182 | if ( reference.use_count() > 1 || is_locked ) 183 | { 184 | T v = *reference; 185 | reference = {}; 186 | reference = impl::make_shared( std::move( v ) ); 187 | } 188 | 189 | // Mark as unlocked and owning. 190 | // 191 | is_owning = true; 192 | is_locked = false; 193 | } 194 | 195 | // Redirect the operator to the reference. 196 | // 197 | return ( T* ) get(); 198 | } 199 | 200 | // Wrapper around ::own that can be called with arguments that are const-qualified 201 | // pointers or references which we will relocate to the new object as non-const qualified 202 | // owned instances of them. 203 | // 204 | template 205 | auto own( X... params ) 206 | { 207 | const T* prev = get(); 208 | T* owned = own(); 209 | return std::make_tuple( owned, impl::reloc_const( std::forward( params ), prev, owned )... ); 210 | } 211 | 212 | // Basic comparison operators are redirected to the pointer type. 213 | // 214 | bool operator==( const shared_reference& o ) const { return reference == o.reference; } 215 | bool operator<( const shared_reference& o ) const { return reference < o.reference; } 216 | 217 | // Redirect pointer and dereferencing operator to the reference and cast to const-qualified equivalent. 218 | // 219 | const T* get() const { return reference.operator->(); } 220 | const T* operator->() const { return get(); } 221 | const T& operator*() const { return *reference; } 222 | 223 | // Syntax sugar for ::own() using add operator. 224 | // 225 | T* operator+() { return own(); } 226 | }; 227 | 228 | // Local references are used to create copy-on-write references to values on stack, 229 | // note that they should not be stored under any condition. 230 | // 231 | template 232 | __forceinline shared_reference make_local_reference( T* variable_pointer ) 233 | { 234 | // Save current frame address. 235 | // 236 | void* creation_frame = _AddressOfReturnAddress(); 237 | 238 | // Create a shared_reference from a custom std::shared_ptr. 239 | // 240 | shared_reference output; 241 | output.reference = std::shared_ptr{ variable_pointer, [ creation_frame ] ( T* ptr ) 242 | { 243 | // Should not be destructed above current frame. 244 | // 245 | fassert( creation_frame > _AddressOfReturnAddress() ); 246 | } }; 247 | 248 | // Mark as locked and return. 249 | // 250 | output.is_locked = true; 251 | return output; 252 | } 253 | }; -------------------------------------------------------------------------------- /util/critical_section.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #include "critical_section.hpp" 29 | 30 | #if _WIN64 31 | #include 32 | #else 33 | #include 34 | #endif 35 | #include "..\io\asserts.hpp" 36 | 37 | namespace vtil 38 | { 39 | // Returns the thread identifier in a platform independent way, 40 | // used instead of std::thread::get_id() as conversion to an integer 41 | // requires std::hash... 42 | // 43 | tid_t get_thread_id() 44 | { 45 | #if _WIN64 46 | static_assert( sizeof( tid_t ) == 8, "Thread identifier must be defined as a quadword." ); 47 | return __readgsqword( 0x48 ); 48 | #else 49 | return ( tid_t ) gettid(); 50 | #endif 51 | } 52 | 53 | // Tries locking the mutex, returns true on success and false on failure. 54 | // 55 | bool critical_section::try_lock() 56 | { 57 | // If we could not acquire the mutex ownership: 58 | // 59 | if ( !mtx.try_lock() ) 60 | { 61 | // If thread identifier does not match, report failure. 62 | // 63 | if ( owner.load() != get_thread_id() ) 64 | return false; 65 | 66 | // Increment lock count. 67 | // 68 | lock_count++; 69 | return true; 70 | } 71 | 72 | // This thread now owns this mutex, report success. 73 | // 74 | owner.store( get_thread_id() ); 75 | return true; 76 | } 77 | 78 | // Continously attempts to lock the mutex until it succeeds, returns only 79 | // when the mutex is acquired. 80 | // 81 | void critical_section::lock() 82 | { 83 | // If owner is not the current thread, spin until we acquire the mutex. 84 | // 85 | if ( owner != get_thread_id() ) 86 | mtx.lock(); 87 | 88 | // Increment the lock counter, if it was zero, store the current threads 89 | // identifier as the owning thread identifier and return. 90 | // 91 | if ( lock_count++ == 0 ) 92 | owner.store( get_thread_id() ); 93 | 94 | // Validate sanity. 95 | // 96 | fassert( owner.load() == get_thread_id() ); 97 | } 98 | 99 | // Unlocks the mutex with the assumption that caller currently owns it. 100 | // 101 | void critical_section::unlock() 102 | { 103 | // Validate sanity. 104 | // 105 | fassert( owner.load() == get_thread_id() ); 106 | 107 | // If lock count reached zero: 108 | // 109 | if ( --lock_count == 0 ) 110 | { 111 | // Zero-out the owning thread-id and unlock the mutex. 112 | // 113 | owner.store( 0 ); 114 | mtx.unlock(); 115 | } 116 | } 117 | }; 118 | -------------------------------------------------------------------------------- /util/critical_section.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | 33 | namespace vtil 34 | { 35 | // Returns the thread identifier in a platform independent way, 36 | // used instead of std::thread::get_id() as conversion to an integer 37 | // requires std::hash... 38 | // 39 | using tid_t = size_t; 40 | tid_t get_thread_id(); 41 | 42 | // Implements a structure that mimics the way Win32 CRITICAL_SECTION objects work. 43 | // As long as it's the same thread, this lock can be acquired multiple times. 44 | // 45 | // - Note that this object assumes thread identifier cannot change 46 | // if it is equivalent to the current thread identifier, which holds 47 | // since owner having the same thread identifier implies it was locked 48 | // by a routine that directly or indirectly called the current one 49 | // and since user-mode has no "yes but..."s to this, such as interrupts, 50 | // we can conclude that we don't need to checkin again and unroll on mismatch. 51 | // 52 | struct critical_section 53 | { 54 | // The mutex that we wrap around. 55 | // 56 | std::mutex mtx; 57 | 58 | // Number of times the mutex was acquired by this thread. 59 | // 60 | std::atomic lock_count; 61 | 62 | // Identifier of the thread that currently owns this mutex. 63 | // 64 | std::atomic owner; 65 | 66 | // Default constructor, copying or moving this object is not allowed. 67 | // 68 | critical_section() = default; 69 | critical_section( critical_section&& ) = delete; 70 | critical_section( const critical_section& ) = delete; 71 | critical_section& operator=( critical_section&& ) = delete; 72 | critical_section& operator=( const critical_section& ) = delete; 73 | 74 | // Tries locking the mutex, returns true on success and false on failure. 75 | // 76 | bool try_lock(); 77 | 78 | // Continously attempts to lock the mutex until it succeeds, returns only 79 | // when the mutex is acquired. 80 | // 81 | void lock(); 82 | 83 | // Unlocks the mutex with the assumption that caller currently owns it. 84 | // 85 | void unlock(); 86 | }; 87 | }; -------------------------------------------------------------------------------- /util/fnv128.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include "..\io\formatting.hpp" 33 | 34 | namespace vtil 35 | { 36 | // Defines a 128-bit hash type based on FNV-1. 37 | // 38 | struct fnv128_hash_t 39 | { 40 | // Magic constants for 128-bit FNV-1 . 41 | // 42 | static constexpr size_t default_seed[] = { 0x62B821756295C58D, 0x6C62272E07BB0142 }; 43 | static constexpr size_t prime[] = { 0x000000000000013B, 0x0000000001000000 }; 44 | 45 | // Current value of the hash. 46 | // 47 | size_t value[ 2 ]; 48 | 49 | // Construct a new hash from an optional seed of either 64-bit or 128-bit value. 50 | // 51 | fnv128_hash_t( size_t seed64 ) { value[ 1 ] = ~0ull; value[ 0 ] = seed64; } 52 | fnv128_hash_t( const size_t( &seed128 )[ 2 ] = default_seed ) { std::copy( seed128, std::end( seed128 ), value ); } 53 | 54 | // Appends the given array of bytes into the hash value. 55 | // 56 | template 57 | void add_bytes( const T& data ) 58 | { 59 | const uint8_t* bytes = ( const uint8_t* ) &data; 60 | 61 | for ( size_t i = 0; i != sizeof( T ); i++ ) 62 | { 63 | // Apply XOR over the low byte. 64 | // 65 | value[ 0 ] ^= bytes[ i ]; 66 | 67 | // Calculate [value * prime]. 68 | // 69 | // A: 0x???????????????? 0x???????????????? 70 | // HA LA 71 | uint64_t ha = value[ 1 ], la = value[ 0 ]; 72 | // B: 0x0000000001000000 0x000000000000013B 73 | // HB LB 74 | uint64_t hb = prime[ 1 ], lb = prime[ 0 ]; 75 | // x 76 | // ---------------------------------------- 77 | // = (HA<<64 + LA) * (HB<<64 + LB) 78 | // 79 | // = LA * LB (Has both low and high parts) 80 | // 81 | value[ 0 ] = _umul128( la, lb, &value[ 1 ] ); 82 | // 83 | // HA<<64 * HB<<64 + (Discarded) 84 | // HA<<64 * LB + (Will have no low part) 85 | // 86 | value[ 1 ] += ha * lb; 87 | // 88 | // LA * HB<<64 + (Will have no low part) 89 | // 90 | value[ 1 ] += la * hb; 91 | } 92 | } 93 | 94 | // Implicit conversion to 64-bit and 128-bit values. 95 | // 96 | size_t as64() const { return value[ 0 ] + value[ 1 ]; } 97 | operator size_t() const { return as64(); } 98 | 99 | // Conversion to human-readable format. 100 | // 101 | std::string to_string() const 102 | { 103 | return format::str( "0x%p%p", value[ 1 ], value[ 0 ] ); 104 | } 105 | 106 | // Basic comparison operators. 107 | // 108 | bool operator<( const fnv128_hash_t& o ) const { return memcmp( value, o.value, sizeof( value ) ) < 0; } 109 | bool operator==( const fnv128_hash_t& o ) const { return memcmp( value, o.value, sizeof( value ) ) == 0; } 110 | bool operator!=( const fnv128_hash_t& o ) const { return memcmp( value, o.value, sizeof( value ) ) != 0; } 111 | }; 112 | }; 113 | 114 | // Make it std::hashable. 115 | // 116 | namespace std 117 | { 118 | template<> 119 | struct hash 120 | { 121 | size_t operator()( const vtil::fnv128_hash_t& value ) const { return value.as64(); } 122 | }; 123 | }; -------------------------------------------------------------------------------- /util/fnv64.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include "..\io\formatting.hpp" 33 | 34 | namespace vtil 35 | { 36 | // Defines a 64-bit hash type based on FNV-1. 37 | // 38 | struct fnv64_hash_t 39 | { 40 | // Magic constants for 64-bit FNV-1 . 41 | // 42 | using value_t = size_t; 43 | static constexpr size_t default_seed = { 0xCBF29CE484222325 }; 44 | static constexpr size_t prime = { 0x00000100000001B3 }; 45 | 46 | // Current value of the hash. 47 | // 48 | value_t value[ 1 ]; 49 | 50 | // Construct a new hash from an optional seed of 64-bit value. 51 | // 52 | fnv64_hash_t( value_t seed64 = default_seed ) { value[ 0 ] = seed64; } 53 | 54 | // Appends the given array of bytes into the hash value. 55 | // 56 | template 57 | void add_bytes( const T& data ) 58 | { 59 | const uint8_t* bytes = ( const uint8_t* ) &data; 60 | 61 | for ( size_t i = 0; i != sizeof( T ); i++ ) 62 | { 63 | // Apply XOR over the low byte. 64 | // 65 | value[ 0 ] ^= bytes[ i ]; 66 | 67 | // Calculate [value * prime]. 68 | // 69 | value[ 0 ] *= prime; 70 | } 71 | } 72 | 73 | // Implicit conversion to 64-bit values. 74 | // 75 | size_t as64() const { return value[ 0 ]; } 76 | operator size_t() const { return as64(); } 77 | 78 | // Conversion to human-readable format. 79 | // 80 | std::string to_string() const 81 | { 82 | return format::str( "0x%p", value[ 0 ] ); 83 | } 84 | 85 | // Basic comparison operators. 86 | // 87 | bool operator<( const fnv64_hash_t& o ) const { return memcmp( value, o.value, sizeof( value ) ) < 0; } 88 | bool operator==( const fnv64_hash_t& o ) const { return memcmp( value, o.value, sizeof( value ) ) == 0; } 89 | bool operator!=( const fnv64_hash_t& o ) const { return memcmp( value, o.value, sizeof( value ) ) != 0; } 90 | }; 91 | }; 92 | 93 | // Make it std::hashable. 94 | // 95 | namespace std 96 | { 97 | template<> 98 | struct hash 99 | { 100 | size_t operator()( const vtil::fnv64_hash_t& value ) const { return value.as64(); } 101 | }; 102 | }; -------------------------------------------------------------------------------- /util/hashable.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include "concept.hpp" 32 | #include "..\io\formatting.hpp" 33 | 34 | // Determine the size of vtil::hash_t. 35 | // 36 | #ifndef VTIL_HASH_SIZE 37 | #ifdef _DEBUG 38 | #define VTIL_HASH_SIZE 64 39 | #else 40 | #define VTIL_HASH_SIZE 128 41 | #endif 42 | #endif 43 | 44 | // Include the hash header file for the hash type we use 45 | // and redirect the definition of hash_t to it. 46 | // 47 | #if VTIL_HASH_SIZE == 128 48 | #include "fnv128.hpp" 49 | namespace vtil { using hash_t = vtil::fnv128_hash_t; }; 50 | #elif VTIL_HASH_SIZE == 64 51 | #include "fnv64.hpp" 52 | namespace vtil { using hash_t = vtil::fnv64_hash_t; }; 53 | #else 54 | #error FNV-1 Algorithm for the FNV algorithm is not defined for the given bit count. 55 | #endif 56 | 57 | namespace vtil 58 | { 59 | namespace impl 60 | { 61 | // Check if type is hashable using std::hash<>. 62 | // 63 | template 64 | struct is_std_hashable : concept_base 65 | { 66 | template 67 | static auto f( T v ) -> decltype( std::hash{}( v ) ); 68 | }; 69 | 70 | // Check if type is hashable using ::hash() 71 | // 72 | template 73 | struct is_vtil_hashable : concept_base 74 | { 75 | template 76 | static auto f( const T v ) -> decltype( v.hash() ); 77 | }; 78 | 79 | // Check if type is iterable using std::begin / std::end. 80 | // 81 | template 82 | struct is_iterable : concept_base 83 | { 84 | template 85 | static auto f( T v ) -> decltype( std::begin( v ), std::end( v ) ); 86 | }; 87 | 88 | // Used to combine two hashes of arbitrary size. 89 | // 90 | static void combine_hash( hash_t& a, const hash_t& b ) 91 | { 92 | static constexpr uint64_t hash_combination_keys[] = 93 | { 94 | 0x0c214449f2ced59a, 0x63799bb9f17566b6, 0xbccb2d46778c06d1, 0x4570d058141eca81, 95 | 0xca967987832ab9dd, 0xff85a956b704b02e, 0xc3544dd4f91272e0, 0xc2f4185a6b5da2fa, 96 | 0x0d2c48be2a8b2eac, 0x10373db6d8fbf237, 0x8c5bbed2074d19a6, 0x4bbf4451b13375dc, 97 | 0xe2bdd40325aee12c, 0x562ed25209bbaabd, 0x8659a830869a89ff, 0x015db8396e1ec55a, 98 | 0xf12189b01704f5a5, 0xf86540ef4910fbbe, 0x482cf76fa1fef848, 0x6e1ba3ffe21ff90d, 99 | 0x870d91d376936b1c, 0x68ad6b317bf548d3, 0x25956f8cf8f61f1e, 0xd1034eeae30b3cff, 100 | 0xf1901e9f69d6b183, 0xc74f6acbc520c43f, 0x4baab0a89021b9e6, 0x432bacb35143cd01, 101 | 0xe2c254956ea60865, 0xc7f7a5570d61009d, 0x05094efaaf889e3b, 0xc118676c1d7b78f7, 102 | 0x0ca0c965b0fd34ef, 0x6dcb98d623b7defc, 0x2edd0e86860ed35a, 0x93785fa8424ec7ce, 103 | 0xa421dd7a455cad94, 0x334d5c6bf23c41a9, 0x101fb5a20dabc5b8, 0xc8dd9d4da0103025, 104 | 0x75c3870304c0b9f6, 0xbd83825458b55edc, 0x730bdb30ebfcf0c2, 0xc52ffe66afbec22b, 105 | 0x9b1581590b90d484, 0xad2698ca617f4940, 0x1f823ccbc35bda50, 0x92717153a167439e, 106 | 0x2e1770b9d19bbdee, 0xc54c7c30a19075a1, 0x4aa6fc19e3b16881, 0x2a76777dfe6ee009, 107 | 0x8ab2f6f54d6f0f3c, 0x252d923185ff895a, 0xc6cf709908708bd5, 0x3d164624c483ff88, 108 | 0x2271b75f2a889123, 0x0b892f4ae4e5f9f5, 0x0095bb746454d0b7, 0xc0e948fe1a9dc9eb, 109 | 0x96b1d69df03265c6, 0xbeac9571cabb01c1, 0x7d9ef1d2fde07fc1, 0x3217c6c2c98498c1, 110 | }; 111 | 112 | static constexpr int N = VTIL_HASH_SIZE / 64; 113 | for ( int i = 0; i < N; i++ ) 114 | { 115 | // Key rotating A is decided by the element of B on 116 | // the opposite index and vice versa. 117 | // 118 | uint8_t ka = ( i + b.value[ N - i - 1 ] ) & 0x3F; 119 | uint8_t kb = ( N - i - 1 + a.value[ i ] ) & 0x3F; 120 | 121 | // Rotate both hashes, add together and combine with the combination key. 122 | // 123 | a.value[ i ] = ( _rotl64( a.value[ i ], ka ) + _rotl64( b.value[ i ], kb ) ) ^ hash_combination_keys[ ka ]; 124 | } 125 | } 126 | 127 | // This tag is used to simplify the use of hasher struct when passing to 128 | // classic STL templates that take a type-tagged hasher, will redirect all 129 | // instances of operator() to the default hasher as decided by make_hash(...). 130 | // 131 | struct hasher_default_tag_t {}; 132 | }; 133 | 134 | // Define basic hasher. 135 | // 136 | template 137 | struct hasher 138 | { 139 | hash_t operator()( const T& value ) const noexcept 140 | { 141 | // If object is hashable via ::hash(), use as is. 142 | // 143 | if constexpr ( impl::is_vtil_hashable::apply() ) 144 | { 145 | return value.hash(); 146 | } 147 | // If STL container or array, hash each element and add container information. 148 | // 149 | else if constexpr ( impl::is_iterable::apply() || std::extent_v ) 150 | { 151 | hash_t hash = {}; 152 | size_t i = 0; 153 | for ( const auto& entry : value ) 154 | impl::combine_hash( hash, hasher>{}( entry ) ), i++; 155 | hash.add_bytes( sizeof( T ) + i ); 156 | return hash; 157 | } 158 | // If hash, combine with default seed. 159 | // 160 | else if constexpr ( std::is_same_v ) 161 | { 162 | hash_t hash = value; 163 | impl::combine_hash( hash, {} ); 164 | return hash; 165 | } 166 | // If trivial type, hash each byte. 167 | // 168 | else if constexpr ( std::is_trivial_v ) 169 | { 170 | hash_t hash = {}; 171 | hash.add_bytes( value ); 172 | return hash; 173 | } 174 | // If hashable using std::hash<>, redirect. 175 | // 176 | else if constexpr ( impl::is_std_hashable::apply() ) 177 | { 178 | return std::hash{}( value ); 179 | } 180 | // Throw assert fail. 181 | // 182 | else 183 | { 184 | static_assert( sizeof( T ) == -1, "Type not hashable." ); 185 | } 186 | } 187 | }; 188 | 189 | // Vararg hasher wrapper that should be used to create hashes from N values. 190 | // 191 | template 192 | static hash_t make_hash( const T& value ) { return hasher{}( value ); } 193 | template 194 | static hash_t make_hash( const C& current, T&&... rest ) 195 | { 196 | hash_t res = make_hash( std::forward( rest )... ); 197 | impl::combine_hash( res, make_hash( current ) ); 198 | return res; 199 | } 200 | 201 | // Overload for std::optional. 202 | // 203 | template 204 | struct hasher> 205 | { 206 | hash_t operator()( const std::optional& value ) const noexcept 207 | { 208 | if ( value ) 209 | return make_hash( *value, sizeof( T ), true ); 210 | else 211 | return make_hash( -int( sizeof( T ) ), false ); 212 | } 213 | }; 214 | 215 | // Overload for std::variant. 216 | // 217 | template 218 | struct hasher> 219 | { 220 | hash_t operator()( const std::variant& value ) const noexcept 221 | { 222 | hash_t res = std::visit( [ ] ( auto&& arg ) { return make_hash( arg ); }, value ); 223 | res.add_bytes( value.index() ); 224 | return res; 225 | } 226 | }; 227 | 228 | // Overload for std::pair. 229 | // 230 | template 231 | struct hasher> 232 | { 233 | hash_t operator()( const std::pair& obj ) const noexcept 234 | { 235 | return make_hash( obj.first, obj.second ); 236 | } 237 | }; 238 | 239 | // Overload for std::tuple. 240 | // 241 | template 242 | struct hasher> 243 | { 244 | template 245 | auto hash_all( const T& obj, std::index_sequence ) const noexcept 246 | { 247 | return make_hash( std::get( obj )... ); 248 | } 249 | 250 | hash_t operator()( const std::tuple& obj ) const noexcept 251 | { 252 | return hash_all( obj, std::index_sequence_for{} );; 253 | } 254 | }; 255 | 256 | // Overload default instance. 257 | // 258 | template<> 259 | struct hasher 260 | { 261 | template 262 | hash_t operator()( const T& obj ) const noexcept 263 | { 264 | return make_hash( obj ); 265 | } 266 | }; 267 | }; -------------------------------------------------------------------------------- /util/priority_list.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | namespace vtil 39 | { 40 | // Provides a (optionally atomic) list where entries are sorted by "points". 41 | // 42 | template 43 | struct priority_list 44 | { 45 | // Entry type wrapping the actual object. 46 | // 47 | struct entry_type 48 | { 49 | T value = {}; 50 | std::atomic points = 0; 51 | 52 | entry_type() = default; 53 | entry_type( entry_type&& entry ) : points( entry.points.load() ), value( std::move( entry.value ) ) {} 54 | entry_type( const entry_type& entry ) : points( entry.points.load() ), value( entry.value ) {} 55 | entry_type( const T & value ) : value( value ) {} 56 | entry_type( T&& value ) : value( std::move( value ) ) {} 57 | }; 58 | 59 | // Remapped iterator hiding the base entry_type from the user. 60 | // 61 | template 62 | struct remapped_iterator : T 63 | { 64 | using base_type = T; 65 | 66 | remapped_iterator() = default; 67 | remapped_iterator( const T & v ) : T( v ) {} 68 | remapped_iterator( T&& v ) : T( std::move( v ) ) {} 69 | 70 | auto* operator->() const { return &T::operator->()->value; } 71 | auto& operator*() const { return T::operator->()->value; } 72 | 73 | template =0> auto& inc_priority( int64_t n ) { T::operator->()->points += n; return *this; } 74 | template =0> auto& dec_priority( int64_t n ) { T::operator->()->points -= n; return *this; } 75 | 76 | auto operator==( remapped_iterator& o ) const { return T::operator==( o ); } 77 | auto operator!=( remapped_iterator& o ) const { return T::operator!=( o ); } 78 | }; 79 | 80 | // Use std::list for atomic priority lists, and std::vector otherwise. 81 | // 82 | using container_type = typename std::conditional_t, std::vector>; 83 | 84 | // Define standard container properties. 85 | // 86 | using value_type = entry_type; 87 | using pointer = value_type*; 88 | using const_pointer = const value_type*; 89 | using reference = value_type&; 90 | using const_reference = const value_type&; 91 | using iterator = remapped_iterator; 92 | using const_iterator = remapped_iterator; 93 | using reverse_iterator = remapped_iterator; 94 | using const_reverse_iterator = remapped_iterator; 95 | 96 | // Mutex for atomic lists, which may be unused, and the container itself. 97 | // 98 | std::shared_mutex mutex; 99 | container_type container; 100 | 101 | // Mutex ignores const-qualifiers. 102 | // 103 | std::shared_mutex* get_mutex() const 104 | { 105 | return ( std::shared_mutex* ) &mutex; 106 | } 107 | 108 | // Default constructors and copy/move. 109 | // 110 | priority_list() = default; 111 | priority_list( const priority_list& ) = delete; 112 | priority_list& operator=( const priority_list& ) = delete; 113 | priority_list( priority_list&& ) = default; 114 | priority_list& operator=( priority_list&& ) = default; 115 | 116 | // Construct from a list of initial values. 117 | // 118 | priority_list( const std::initializer_list& init_list ) 119 | { 120 | for ( T& entry : init_list ) 121 | push_back( entry ); 122 | } 123 | priority_list( std::initializer_list&& init_list ) 124 | { 125 | for ( T& entry : init_list ) 126 | push_back( std::move( entry ) ); 127 | } 128 | 129 | // Priority based iteration is done using these wrappers. 130 | // 131 | template 132 | std::optional for_each( const std::function( iterator )>& enumerator ) 133 | { 134 | if ( auto m = get_mutex() ) m->lock_shared(); 135 | 136 | // Take a snapshot of current points. 137 | // 138 | std::vector> snapshot; 139 | snapshot.reserve( container.size() ); 140 | for ( auto it = begin(); it != end(); it++ ) 141 | snapshot.push_back( { iterator::base_type( it )->points, it } ); 142 | 143 | // Sort the snapshot, descending in terms of points. 144 | // 145 | std::sort( snapshot.begin(), snapshot.end(), [ ] ( auto& a, auto& b ) { return a.first > b.first; } ); 146 | 147 | // Iterate according to the guide and invoke enumerator. 148 | // 149 | std::optional result; 150 | for ( auto& guide : snapshot ) 151 | { 152 | if ( auto r = enumerator( guide.second ) ) 153 | { 154 | result = r; 155 | break; 156 | } 157 | } 158 | 159 | if ( auto m = get_mutex() ) m->unlock_shared(); 160 | return result; 161 | } 162 | 163 | template 164 | std::optional for_each( const std::function( const_iterator )>& enumerator ) const 165 | { 166 | if ( auto m = get_mutex() ) m->lock_shared(); 167 | 168 | // Take a snapshot of current points. 169 | // 170 | std::vector> snapshot; 171 | snapshot.reserve( container.size() ); 172 | for ( auto it = begin(); it != end(); it++ ) 173 | snapshot.push_back( { const_iterator::base_type( it )->points, it } ); 174 | 175 | // Sort the snapshot, descending in terms of points. 176 | // 177 | std::sort( snapshot.begin(), snapshot.end(), [ ] ( auto& a, auto& b ) { return a.first > b.first; } ); 178 | 179 | // Iterate according to the guide and invoke enumerator. 180 | // 181 | std::optional result; 182 | for ( auto& guide : snapshot ) 183 | { 184 | if ( auto r = enumerator( guide.second ) ) 185 | { 186 | result = r; 187 | break; 188 | } 189 | } 190 | 191 | if ( auto m = get_mutex() ) m->unlock_shared(); 192 | return result; 193 | } 194 | 195 | // Redirect size related std::list functions. 196 | // 197 | size_t size() const { if ( auto m = get_mutex() ) m->lock_shared(); auto r = container.size(); if ( auto m = get_mutex() ) m->unlock_shared(); return r; } 198 | bool empty() const { if ( auto m = get_mutex() ) m->lock_shared(); auto r = container.empty(); if ( auto m = get_mutex() ) m->unlock_shared(); return r; } 199 | 200 | // Redirect iteration related std::list functions. 201 | // - Note: Unsafe, if lock not aquired. 202 | // 203 | iterator begin() { return container.begin(); } 204 | iterator end() { return container.end(); } 205 | reverse_iterator rbegin() { return container.rbegin(); } 206 | reverse_iterator rend() { return container.rend(); } 207 | const_iterator begin() const { return container.begin(); } 208 | const_iterator end() const { return container.end(); } 209 | const_reverse_iterator rbegin() const { return container.rbegin(); } 210 | const_reverse_iterator rend() const { return container.rend(); } 211 | 212 | // Redirect data related std::list functions. 213 | // 214 | iterator at( size_t n ) { if ( auto m = get_mutex() ) m->lock_shared(); auto r = std::next( container.begin(), n ); if ( auto m = get_mutex() ) m->unlock_shared(); return r; } 215 | iterator operator[]( size_t n ) { return at( n ); } 216 | const_iterator at( size_t n ) const { if ( auto m = get_mutex() ) m->lock_shared(); auto r = std::next( container.begin(), n ); if ( auto m = get_mutex() ) m->unlock_shared(); return r; } 217 | const_iterator operator[]( size_t n ) const { return at( n ); } 218 | void push_back( T&& v ) { if ( auto m = get_mutex() ) m->lock(); container.push_back( std::move( v ) ); if ( auto m = get_mutex() ) m->unlock(); } 219 | void push_back( const T& v ) { if ( auto m = get_mutex() ) m->lock(); container.push_back( v ); if ( auto m = get_mutex() ) m->unlock(); } 220 | void resize( size_t v ) { if ( auto m = get_mutex() ) m->lock(); container.resize( v ); if ( auto m = get_mutex() ) m->unlock(); } 221 | }; 222 | }; -------------------------------------------------------------------------------- /util/reducable.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include "hashable.hpp" 31 | 32 | // Reducable types essentially let us do member-type reflection 33 | // which we use to auto generate useful but repetetive methods 34 | // like ::hash() or comparison operators. Base type has to define 35 | // the following routine where [...] should be replaced by members 36 | // that should be constributing to the comparison/hash operations. 37 | // 38 | // - auto reduce() { return std::tie( ... ); } 39 | // 40 | #pragma warning(push) 41 | #pragma warning(disable: 4305) 42 | namespace vtil 43 | { 44 | namespace impl 45 | { 46 | // Applies type modifier over each element in pair/tuple. 47 | // 48 | template typename F, typename T> 49 | struct apply_each { using type = F; }; 50 | template typename F, typename... T> 51 | struct apply_each> { using type = std::pair...>; }; 52 | template typename F, typename... T> 53 | struct apply_each> { using type = std::tuple...>; }; 54 | 55 | template typename F, typename T> 56 | using apply_each_t = typename apply_each::type; 57 | 58 | // Appends a deep const qualifier to each referenced type. 59 | // 60 | template 61 | using add_dconst_t = typename std::conditional_t 62 | , 63 | std::add_lvalue_reference_t>>, 64 | std::add_const_t>; 65 | }; 66 | 67 | // Mask of requested reducable auto declerations. 68 | // 69 | enum reducable_auto_decl_id : uint8_t 70 | { 71 | reducable_none = 0x00, 72 | reducable_equ = 1 << 0, 73 | reducable_nequ = 1 << 1, 74 | reducable_leq = 1 << 2, 75 | reducable_greq = 1 << 3, 76 | reducable_less = 1 << 4, 77 | reducable_greater = 1 << 5, 78 | reducable_hash = 1 << 6, 79 | reducable_all = 0xFF, 80 | }; 81 | 82 | // Reducable tag let's us check if a type is reducable without having 83 | // to template for proxied type or the auto-decl flags. 84 | // 85 | struct reducable_tag_t {}; 86 | 87 | template 88 | static constexpr bool is_reducable_v = std::is_base_of_v; 89 | 90 | // The main definition of the helper: 91 | // 92 | template 93 | struct reducable : reducable_tag_t 94 | { 95 | protected: 96 | // Invoking T::reduce() in a member function will create problems 97 | // due to the type not being defined yet, however we can proxy it. 98 | // 99 | template 100 | static auto reduce_proxy( Tx& p ) { return p.reduce(); } 101 | 102 | template().reduce() )> 103 | static auto reduce_proxy( const Tx& p ) 104 | { 105 | return ( typename impl::apply_each_t ) reduce_proxy( ( Tx& ) p ); 106 | } 107 | 108 | // Only the base type can construct/copy/move this type. 109 | // 110 | reducable() = default; 111 | reducable( reducable&& ) = default; 112 | reducable( const reducable& ) = default; 113 | reducable& operator=( reducable&& ) = default; 114 | reducable& operator=( const reducable& ) = default; 115 | 116 | public: 117 | // Define basic comparison operators using std::tuple. 118 | // 119 | template = 0> 120 | auto operator==( const T& other ) const { return reduce_proxy( ( T& ) *this ) == reduce_proxy( other ); } 121 | template = 0> 122 | auto operator!=( const T& other ) const { return reduce_proxy( ( T& ) *this ) != reduce_proxy( other ); } 123 | template = 0> 124 | auto operator<=( const T& other ) const { return reduce_proxy( ( T& ) *this ) <= reduce_proxy( other ); } 125 | template = 0> 126 | auto operator>=( const T& other ) const { return reduce_proxy( ( T& ) *this ) >= reduce_proxy( other ); } 127 | template = 0> 128 | auto operator< ( const T& other ) const { return reduce_proxy( ( T& ) *this ) < reduce_proxy( other ); } 129 | template = 0> 130 | auto operator> ( const T& other ) const { return reduce_proxy( ( T& ) *this ) > reduce_proxy( other ); } 131 | 132 | // Define VTIL hash using a simple VTIL tuple hasher. 133 | // 134 | template = 0> 135 | hash_t hash() const { return make_hash( reduce_proxy( ( const T& ) *this ) ); } 136 | 137 | // Define the [const T::reduce()] for the base type just for convinience. 138 | // 139 | auto reduce() const { return reduce_proxy( ( const T& ) *this ); } 140 | }; 141 | }; 142 | #pragma warning(pop) -------------------------------------------------------------------------------- /util/stack_container.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "concept.hpp" 40 | #include "..\io\asserts.hpp" 41 | 42 | // [Configuration] 43 | // Determine stack buffer clamping range. 44 | // 45 | #ifndef VTIL_STACK_BUFFER_SIZE_RANGE 46 | #define VTIL_STACK_BUFFER_SIZE_RANGE 0x100, 0x1000 47 | #else 48 | static_assert( std::initializer_list{ VTIL_STACK_BUFFER_SIZE_RANGE }.size() == 2, "Invalid buffer range specification." ); 49 | #endif 50 | 51 | namespace vtil 52 | { 53 | namespace impl 54 | { 55 | // Swaps the given container's allocator with [A]. 56 | // 57 | template 58 | struct swap_allocator { using type = void; }; 59 | template typename C, typename... T, typename A> 60 | struct swap_allocator, A> { using type = typename C::allocator_type>, A, T>...>; }; 61 | 62 | template 63 | using swap_allocator_t = typename swap_allocator::type; 64 | }; 65 | 66 | 67 | // Stack buffer state with iterators enforcing equivalent alignment for any type. 68 | // 69 | template 70 | struct stack_buffer_state 71 | { 72 | // Align [T] as if it was the original type of the buffer. 73 | // 74 | static constexpr size_t alignment_mask = alignof( real_type ) - 1; 75 | struct alignas( real_type ) realigned_type { T value; }; 76 | 77 | // Declare 3-pointer iterators based on this type. 78 | // 79 | realigned_type* base; 80 | realigned_type* limit; 81 | realigned_type* it; 82 | 83 | // Default constructor. 84 | // 85 | stack_buffer_state() = default; 86 | 87 | // Construct state from any Tx(&)[N]. 88 | // 89 | template 90 | stack_buffer_state( buffer_type& buffer ) 91 | { 92 | // Calculate the beginning of the aligned array, and set base, limit and it based on it. 93 | // 94 | uint64_t mem_begin = ( uint64_t( std::begin( buffer ) ) + alignment_mask ) & ~alignment_mask; 95 | base = it = ( realigned_type* ) mem_begin; 96 | limit = ( realigned_type* ) std::end( buffer ); 97 | } 98 | }; 99 | 100 | // This allocator is constructed from a stack buffer state. The first 101 | // allocations that can be allocated directly from this buffer will use 102 | // the buffer and frees of those allocations will be ignored unless done 103 | // so in order. Rest of the allocations will invoke the default allocator. 104 | // It could be more efficient in terms of actually processing the deallocations 105 | // but might as well use the already implemented heap in that case. 106 | // 107 | template 108 | struct stack_buffered_allocator 109 | { 110 | // Allocator traits. 111 | // 112 | using value_type = T; 113 | using pointer = T*; 114 | using const_pointer = const T*; 115 | using void_pointer = void*; 116 | using const_void_pointer = const void*; 117 | using size_type = size_t; 118 | using difference_type = int64_t; 119 | using is_always_equal = std::false_type; 120 | 121 | template 122 | struct rebind { using other = stack_buffered_allocator; }; 123 | 124 | // State of the original buffer. 125 | // 126 | stack_buffer_state* state; 127 | 128 | // Construct from buffer state. 129 | // 130 | stack_buffered_allocator( stack_buffer_state<>* state ) 131 | : state( ( stack_buffer_state* )state ) {} 132 | 133 | // Construct from any buffered allocator of same [real_type]. 134 | // 135 | template 136 | stack_buffered_allocator( const stack_buffered_allocator& o ) 137 | : state( ( stack_buffer_state* ) o.state ) {} 138 | 139 | // Allocators are only equivalent if the internal state references 140 | // the same stack buffer. 141 | // 142 | template 143 | bool operator==( const stack_buffered_allocator& o ) const 144 | { 145 | return ( void* ) state == ( void* ) o.state; 146 | } 147 | 148 | // Allocation routine. 149 | // 150 | T* allocate( size_t n, void* hint = 0 ) 151 | { 152 | // If it can be allocated from the buffer: 153 | // 154 | if ( ( state->it + n ) <= state->limit ) 155 | { 156 | // Forward the iterator ahead [n] times, return the original iterator. 157 | // 158 | T* ptr = &state->it->value; 159 | state->it += n; 160 | return ptr; 161 | } 162 | 163 | // Otherwise redirect to default allocator. 164 | // 165 | std::allocator default_allocator; 166 | return std::allocator_traits>::allocate( default_allocator, n, hint ); 167 | } 168 | 169 | // Deallocation routine. 170 | // 171 | void deallocate( T* ptr, size_t n ) 172 | { 173 | // If deallocating from the buffer: 174 | // 175 | if ( &state->base->value <= ptr && ptr < &state->limit->value ) 176 | { 177 | // If deallocating previous allocation, free buffer. 178 | // 179 | if ( &( state->it - n )->value == ptr ) 180 | state->it -= n; 181 | 182 | // Return to the caller. 183 | // 184 | return; 185 | } 186 | 187 | // Otherwise redirect to default allocator. 188 | // 189 | std::allocator default_allocator; 190 | return std::allocator_traits>::deallocate( default_allocator, ptr, n ); 191 | } 192 | }; 193 | 194 | // Define generic stack-buffered container. 195 | // 196 | template, 198 | typename container_t = impl::swap_allocator_t> 199 | struct stack_buffered_container : public container_t 200 | { 201 | static constexpr size_t _size_range[] = { VTIL_STACK_BUFFER_SIZE_RANGE }; 202 | 203 | // Clamp the number of bytes in the buffer to [0x100, 0x1000]. 204 | // - Append 0x20 bytes for _DEBUG binaries to compensate for std::_Container_proxy; 205 | // 206 | static constexpr size_t align_mask = alignof( T ) - 1; 207 | #ifdef _DEBUG 208 | static constexpr size_t buffer_size = std::clamp( N * sizeof( typename T::value_type ), _size_range[ 0 ], _size_range[ 1 ] ) + ( 0x20 + sizeof( T ) + align_mask ) * 2; 209 | #else 210 | static constexpr size_t buffer_size = std::clamp( N * sizeof( typename T::value_type ), _size_range[ 0 ], _size_range[ 1 ] ); 211 | #endif 212 | 213 | // Buffer aligned to match the alignment of T. 214 | // 215 | uint8_t buffer[ buffer_size + align_mask ]; 216 | stack_buffer_state<> state; 217 | 218 | // Constructor forwards as is, ideally should be initially constructed 219 | // with no parameters to make sure the buffer is utilized as much as possible. 220 | // 221 | template 222 | stack_buffered_container( T&&... args ) 223 | : container_t( std::forward( args )..., allocator_t{ &( state = buffer, state ) } ) 224 | { 225 | if constexpr( do_reserve ) 226 | container_t::reserve( N ); 227 | } 228 | }; 229 | 230 | // Wrap basic string derivatives: 231 | // - Note: Strings might be unnecessary as the internal implementation already does SSO 232 | // optimization but might be useful for large strings, so will define anyway. 233 | // 234 | template 235 | using stack_string = stack_buffered_container; 236 | template 237 | using stack_wstring = stack_buffered_container; 238 | template, size_t N = 512> 239 | using basic_stack_string = stack_buffered_container, N, true>; 240 | 241 | // Wrap vector: 242 | // 243 | template 244 | using stack_vector = stack_buffered_container, N, true>; 245 | 246 | // Wrap set derivatives: 247 | // 248 | template, size_t N = 16> 249 | using stack_set = stack_buffered_container, N, false>; 250 | template, size_t N = 16> 251 | using unordered_stack_set = stack_buffered_container, N, false>; 252 | 253 | // Wrap map derivatives: 254 | // 255 | template, size_t N = 16> 256 | using stack_map = stack_buffered_container, N, false>; 257 | template, size_t N = 16> 258 | using unordered_stack_map = stack_buffered_container, N, false>; 259 | 260 | // Wrap multimap derivatives: 261 | // 262 | template, size_t N = 16> 263 | using stack_multimap = stack_buffered_container, N, false>; 264 | template, size_t N = 16> 265 | using unordered_stack_multimap = stack_buffered_container, N, false>; 266 | }; 267 | -------------------------------------------------------------------------------- /util/variant.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #include "variant.hpp" 29 | 30 | namespace vtil 31 | { 32 | // Calculates the address of an inline object within the region [begin-end] 33 | // with the given size and alignment properties. 34 | // 35 | static size_t calc_inline_address( const void* begin, const void* end, size_t size, size_t align ) 36 | { 37 | // Calculate inline boundaries. 38 | // 39 | size_t ptr = ( size_t ) begin; 40 | size_t ptr_lim = ( size_t ) end; 41 | 42 | // Align as required. 43 | // 44 | size_t align_mask = align - 1; 45 | size_t ptr_a = ( ptr + align_mask ) & ~align_mask; 46 | 47 | // If overflows, return null, else return the aligned address. 48 | // 49 | return ( ptr_a + size ) <= ptr_lim ? ptr_a : 0; 50 | } 51 | 52 | // Copy constructor. 53 | // 54 | variant::variant( const variant& src ) 55 | { 56 | // If source is storing a value: 57 | // 58 | if ( src.has_value() ) 59 | { 60 | // If source is trivially copyable, invoke memcpy. 61 | // 62 | if ( src.is_trivial_copy ) 63 | memcpy( allocate( copy_size, copy_align ), ( const void* ) src.get_address( copy_size, copy_align ), copy_size ); 64 | 65 | // Otherwise invoke the copy constructor 66 | // 67 | else 68 | src.copy_fn( src, *this ); 69 | 70 | // Inherit the copy/destruction traits from source. 71 | // 72 | is_trivial_copy = src.is_trivial_copy; 73 | copy_fn = src.copy_fn; 74 | destroy_fn = src.destroy_fn; 75 | 76 | // If debug mode, inherit type name. 77 | // 78 | #ifdef _DEBUG 79 | __typeid_name = src.__typeid_name; 80 | #endif 81 | } 82 | // If source is null, set to null and skip copying. 83 | // 84 | else 85 | { 86 | copy_fn = nullptr; 87 | } 88 | } 89 | // Move constructor. 90 | // 91 | variant::variant( variant&& src ) 92 | { 93 | // If source has no value, simply create a null variant. 94 | // 95 | if ( !src.has_value() ) 96 | { 97 | copy_fn = nullptr; 98 | return; 99 | } 100 | 101 | // If target stores inline value: 102 | // 103 | if ( src.is_inline ) 104 | { 105 | // If type is trivially copyable: 106 | // 107 | if ( src.is_trivial_copy ) 108 | { 109 | // Copy the stored inline value by bytes. 110 | // 111 | memcpy( allocate( src.copy_size, src.copy_align ), ( const void* ) src.get_address( src.copy_size, src.copy_align ), src.copy_size ); 112 | } 113 | // If type is not trivially copyable: 114 | // 115 | else 116 | { 117 | // Redirect to the copy constructor. 118 | // 119 | new ( this ) variant( ( variant& ) src ); 120 | 121 | // Free the object stored in source. 122 | // 123 | src.reset(); 124 | return; 125 | } 126 | } 127 | // If target stores an external pointer: 128 | // 129 | else 130 | { 131 | // Steal the stored external pointer. 132 | // 133 | is_inline = false; 134 | ext = std::move( src.ext ); 135 | } 136 | 137 | // Inherit the inline/copy/destruction traits from source. 138 | // 139 | is_trivial_copy = src.is_trivial_copy; 140 | copy_fn = src.copy_fn; 141 | destroy_fn = src.destroy_fn; 142 | 143 | // If debug mode, inherit type name. 144 | // 145 | #ifdef _DEBUG 146 | __typeid_name = src.__typeid_name; 147 | #endif 148 | 149 | // Mark the source object as freed. 150 | // 151 | src.copy_fn = nullptr; 152 | } 153 | // Gets the address of the object with the given properties. 154 | // - Will throw assert failure if the variant is empty. 155 | // 156 | size_t variant::get_address( size_t size, size_t align ) const 157 | { 158 | fassert( has_value() ); 159 | 160 | // If object is inline, calculate the inline address, otherwise return the external pointer. 161 | // 162 | return is_inline ? calc_inline_address( inl, std::end( inl ), size, align ) : ( size_t ) ext; 163 | } 164 | 165 | // Allocates the space for an object of the given properties and returns the pointer. 166 | // 167 | void* variant::allocate( size_t size, size_t align ) 168 | { 169 | // Calculate the inline address, if successful reference the inline object. 170 | // 171 | if ( size_t inline_adr = calc_inline_address( inl, std::end( inl ), size, align ) ) 172 | { 173 | is_inline = true; 174 | return ( void* ) inline_adr; 175 | } 176 | // Invoke aligned malloc. 177 | // 178 | else 179 | { 180 | is_inline = false; 181 | return ext = _aligned_malloc( size, align ); 182 | } 183 | } 184 | // Deletes the currently stored variant. 185 | // 186 | void variant::reset() 187 | { 188 | // If variant is storing any value: 189 | // 190 | if ( has_value() ) 191 | { 192 | // If there is a destructor callback, invoke it. 193 | // 194 | if ( destroy_fn ) destroy_fn( *this ); 195 | 196 | // If object was not inlined, invoke aligned free. 197 | // 198 | if ( !is_inline ) _aligned_free( ext ); 199 | 200 | // Null copy function to indicate null value. 201 | // 202 | copy_fn = nullptr; 203 | } 204 | } 205 | }; 206 | -------------------------------------------------------------------------------- /util/variant.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of mosquitto nor the names of its 13 | // contributors may be used to endorse or promote products derived from 14 | // this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "..\io\asserts.hpp" 34 | 35 | // [Configuration] 36 | // Determine whether we should use safe variants or not. 37 | // 38 | #if _DEBUG && not defined(VTIL_VARIANT_SAFE) 39 | #if defined(_CPPRTTI) 40 | #define VTIL_VARIANT_SAFE _CPPRTTI 41 | #elif defined(__GXX_RTTI) 42 | #define VTIL_VARIANT_SAFE __GXX_RTTI 43 | #elif defined(__has_feature) 44 | #define VTIL_VARIANT_SAFE __has_feature(cxx_rtti) 45 | #else 46 | #define VTIL_VARIANT_SAFE 0 47 | #endif 48 | #elif defined(VTIL_VARIANT_SAFE) && VTIL_VARIANT_SAFE 49 | #error Debug mode binaries cannot use RTTI based variant safety checks. 50 | #endif 51 | 52 | // [Configuration] 53 | // Determine the maximum size of types we should inline. 54 | // 55 | #ifndef VTIL_VARIANT_INLINE_LIMIT 56 | #define VTIL_VARIANT_INLINE_LIMIT 0x100 57 | #endif 58 | 59 | namespace vtil 60 | { 61 | // Variant can be used to store values of any type in a fast way. 62 | // 63 | #pragma pack(push, 1) 64 | struct variant 65 | { 66 | // Value is either stored in the [char inl[]] as an inline object, 67 | // or in [void* ext] as an external pointer. 68 | // 69 | union 70 | { 71 | char inl[ VTIL_VARIANT_INLINE_LIMIT ]; 72 | void* ext; 73 | }; 74 | 75 | // Set if object is inlined: 76 | // 77 | uint8_t is_inline : 1; 78 | 79 | // Set if object has a trivial copy constructor. 80 | // 81 | uint8_t is_trivial_copy : 1; 82 | 83 | // Details of copy constructor: 84 | // 85 | union 86 | { 87 | // If trivial, size and the alignment of the object. 88 | // 89 | struct 90 | { 91 | size_t copy_size : 32; 92 | size_t copy_align : 32; 93 | }; 94 | 95 | // Otherwise pointer to helper. 96 | // 97 | void( *copy_fn )( const variant&, variant& ); 98 | }; 99 | 100 | // Destructor callback. 101 | // 102 | void( *destroy_fn )( variant& ); 103 | 104 | // If debug mode, currently assigned typeid's name or undefined if RTTI is disabled. 105 | // 106 | #ifdef _DEBUG 107 | const char* __typeid_name; 108 | #endif 109 | 110 | // Null constructors. 111 | // 112 | variant() : copy_fn( nullptr ) {}; 113 | variant( std::nullptr_t ) : copy_fn( nullptr ) {}; 114 | variant( std::nullopt_t ) : copy_fn( nullptr ) {}; 115 | 116 | // Constructs variant from any type that is not variant, nullptr_t or nullopt_t. 117 | // 118 | template, variant> && 121 | !std::is_same_v, std::nullptr_t> && 122 | !std::is_same_v, std::nullopt_t>, int> = 0> 123 | variant( arg_type&& value ) 124 | { 125 | using T = std::remove_cvref_t; 126 | 127 | // Invoke copy constructor on allocated space. 128 | // 129 | T* out = new ( allocate( sizeof( T ), alignof( T ) ) ) T( std::forward( value ) ); 130 | 131 | // Assign destructor if not trivially destructible. 132 | // 133 | if constexpr ( !std::is_trivially_destructible_v ) 134 | destroy_fn = [ ] ( variant& v ) { v.get().~T(); }; 135 | // Otherwise null the destroy callback. 136 | // 137 | else 138 | destroy_fn = nullptr; 139 | 140 | // Assign copy constructor if not trivially copyable. 141 | // 142 | if constexpr ( !std::is_trivially_copyable_v ) 143 | { 144 | copy_fn = [ ] ( const variant& src, variant& dst ) 145 | { 146 | new ( dst.allocate( sizeof( T ), alignof( T ) ) ) T( src.get() ); 147 | }; 148 | is_trivial_copy = false; 149 | } 150 | // Otherwise indicate trivial copy. 151 | // 152 | else 153 | { 154 | copy_size = sizeof( T ); 155 | copy_align = alignof( T ); 156 | is_trivial_copy = true; 157 | } 158 | 159 | // If safe mode, assign type name. 160 | // 161 | #if VTIL_VARIANT_SAFE 162 | __typeid_name = typeid( T ).name(); 163 | #endif 164 | }; 165 | 166 | // Copy/move constructors. 167 | // 168 | variant( const variant& src ); 169 | variant( variant&& vo ); 170 | 171 | // Assignment by move/copy both reset current value and redirect to constructor. 172 | // 173 | variant& operator=( variant&& vo ) { reset(); return *new ( this ) variant( std::move( vo ) ); } 174 | variant& operator=( const variant& o ) { reset(); return *new ( this ) variant( o ); } 175 | 176 | // Variant does not have a value if the copy field is null. 177 | // 178 | inline bool has_value() const { return copy_fn != nullptr; } 179 | inline operator bool() const { return has_value(); } 180 | 181 | // Gets the address of the object with the given properties. 182 | // - Will throw assert failure if the variant is empty. 183 | // 184 | size_t get_address( size_t size, size_t align ) const; 185 | 186 | // Allocates the space for an object of the given properties and returns the pointer. 187 | // 188 | void* allocate( size_t size, size_t align ); 189 | 190 | // Simple wrappers around get_address. 191 | // - Will throw assert failure if the variant is empty. 192 | // 193 | template 194 | T& get() 195 | { 196 | // If safe mode, validate type name (We can compare pointers as it's a unique pointer in .rdata) 197 | // 198 | #if VTIL_VARIANT_SAFE 199 | fassert( __typeid_name == typeid( T ).name() ); 200 | #endif 201 | // Calculate the address and return a reference. 202 | // 203 | return *( T* ) get_address( sizeof( T ), alignof( T ) ); 204 | } 205 | template 206 | const T& get() const 207 | { 208 | // If safe mode, validate type name (We can compare pointers as it's a unique pointer in .rdata) 209 | // 210 | #if VTIL_VARIANT_SAFE 211 | fassert( __typeid_name == typeid( T ).name() ); 212 | #endif 213 | // Calculate the address and return a const qualified reference. 214 | // 215 | return *( const T* ) get_address( sizeof( T ), alignof( T ) ); 216 | } 217 | 218 | // Cast to optional. 219 | // - Unlike ::get, will not throw an assert failure if the variant 220 | // is empty and will return nullopt instead. 221 | // 222 | template 223 | inline std::optional as() const { return has_value() ? std::optional{ get() } : std::nullopt; } 224 | 225 | // Deletes the currently stored variant. 226 | // 227 | void reset(); 228 | ~variant() { reset(); } 229 | }; 230 | #pragma pack(pop) 231 | }; --------------------------------------------------------------------------------