├── .gitattributes ├── .gitignore ├── README.md ├── addressof.inc ├── addressof.md ├── addressof_jit.inc ├── addressof_jit.md ├── addressof_light.inc ├── amx.inc ├── amx.md ├── amx_base.inc ├── amx_header.inc ├── amx_jit.inc ├── amx_jit.md ├── amx_memory.inc ├── asm.inc ├── asm.md ├── asm_macros.inc ├── asm_macros.md ├── codescan.inc ├── codescan.md ├── disasm.inc ├── dynamic_call.inc ├── dynamic_call.md ├── frame_info.inc ├── frame_info.md ├── heap_alloc.inc ├── heap_alloc.md ├── jit.inc ├── opcode.inc ├── os.inc ├── pawn.json ├── phys_memory.inc ├── phys_memory.md ├── profiler.inc ├── shellcode.inc ├── shellcode.md ├── stack_dump.inc ├── stack_trace.inc ├── test ├── Makefile ├── all-tests.pwn ├── amx-test.pwn ├── asm-test.pwn ├── disasm-test.pwn ├── dynamic_call-test.pwn ├── jit-test.pwn ├── phys_memory-test.pwn ├── stack_trace-test.pwn └── windows │ ├── Makefile │ └── ShellExecute-test.pwn └── windows ├── ShellExecute.inc └── import_table.inc /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pwn linguist-language=Pawn 2 | *.inc linguist-language=Pawn 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Package only files 3 | # 4 | 5 | # Compiled Bytecode 6 | *.amx 7 | *.lst 8 | *.asm 9 | 10 | # Vendor directory for dependencies 11 | dependencies/ 12 | 13 | # Dependency versions lockfile 14 | pawn.lock 15 | 16 | 17 | # 18 | # Server/gamemode related files 19 | # 20 | 21 | # compiled settings file 22 | # keep `samp.json` file on version control 23 | # but make sure the `rcon_password` field is set externally 24 | # you can use the environment variable `SAMP_RCON_PASSWORD` to do this. 25 | server.cfg 26 | 27 | # Plugins directory 28 | plugins/ 29 | 30 | # binaries 31 | *.exe 32 | *.dll 33 | *.so 34 | announce 35 | samp03svr 36 | samp-npc 37 | 38 | # logs 39 | logs/ 40 | server_log.txt 41 | 42 | # 43 | # Common files 44 | # 45 | 46 | *.sublime-workspace 47 | *.sublime-project 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | * [`addressof.inc`](addressof.inc) - Provide `addressof` to get the compiled address of a function, plus runtime compilation to a simple constant, and a lightweight version with no *disasm* dependency. 4 | * [`amx.inc`](amx.inc) - Read/write contents of the AMX structure (`AMX` struct in C code). 5 | * [`amx_base.inc`](amx_base.inc) - Get base address of the AMX in memory (`amx->base`). 6 | * [`amx_header.inc`](amx_header.inc) - Read contents of the AMX header (`AMX_HEADER`) and query header tables such as publics, natives, tags, etc. 7 | * [`amx_memory.inc`](amx_memory.inc) - Read/write contents of variables using their AMX address (kind of like pointers in C). 8 | * [`asm.inc`](asm.inc) - `@emit()` for generating new assembly at runtime, similar to what `#emit` and `__emit` are for compile-time. 9 | * [`codescan.inc`](codescan.inc) - Find patterns in bytecode. 10 | * [`disasm.inc`](disasm.inc) - Example of how to disassemble AMX bytecode (i.e. self-disassembly). 11 | * [`dynamic_call.inc`](dynamic_call.inc) - Call any function by address or index. Can be very powerful in combination with [`amx_header.inc`](amx_header.inc). 12 | * [`frame_info.inc`](frame_info.inc) - Get information about call frames from the stack. 13 | * [`heap_alloc.inc`](heap_alloc.inc) - Allocate memory on the AMX heap. 14 | * [`jit.inc`](jit.inc) - Check if running under the [JIT](https://github.com/Zeex/samp-plugin-jit) plugin. 15 | * [`opcode.inc`](opcode.inc) - List of AMX opcodes and utility functions for (un-)relocating opcodes on Linux. 16 | * [`os.inc`](os.inc) - Detect operating system (Windows vs Linux). 17 | * [`phys_memory.inc`](phys_memory.inc) - Read/write memory of the host process (outside of AMX data). 18 | * [`profiler.inc`](profile.inc) - Simple profiler written purely in Pawn. It can measure execution time of public functions. 19 | * [`shellcode.inc`](shellcode.inc) - Execute arbitrary native code (doesn't work on Linux). 20 | * [`stack_dump.inc`](stack_dump.inc) - Print stack contents to the console. 21 | * [`stack_trace.inc`](stack_trace.inc) - Print stack trace. 22 | * [`windows/import_table.inc`](windows/import_table.inc) - Read the PE import table of the host process. 23 | * [`windows/ShellExecute.inc`](windows/ShellExecute.inc) - How to use `shellcode` and `import_table` to call a Win32 API function (in this case `ShellExecuteA`). 24 | 25 | ## Installation 26 | 27 | Simply install to your project: 28 | 29 | ```bash 30 | sampctl package install amx_assembly 31 | ``` 32 | 33 | Include in your code and begin using the library: 34 | 35 | ```pawn 36 | #include 37 | ``` 38 | 39 | ## Usage 40 | 41 | There are a few example scripts in the `test` directory that show how to use some of these includes. 42 | -------------------------------------------------------------------------------- /addressof.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016 Y_Less 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined ADDRESSOF_INC 22 | #endinput 23 | #endif 24 | #define ADDRESSOF_INC 25 | 26 | /** 27 | * 28 | * 29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include "frame_info" 40 | #include "disasm" 41 | #include "addressof_light" 42 | 43 | #if defined AMX_OLD_CALL 44 | #endinput 45 | #endif 46 | 47 | #define nativeidxof(%1) (_:((O@D_:O@B_())?(((CALL@%1()),O@V_)?1:2):(O@V_))) 48 | 49 | #define NativeIdxOfGetNextCall_ O@B_ 50 | #define CALL@NativeIdxOfGetNextCall_%8() CALL@O@B_%8() 51 | #define CALL@O@B_%8() O@B_%8() 52 | 53 | #define O@B_())?(((CALL@%1<%2>()),O@V_)?1:2):(O@V_) O@B_())?(((_ADDR@$%1()<%2>),O@V_)?(F@_@:1):(F@_@:2)):(F@_@:O@V_) 54 | 55 | // Convert `On` tests to string-based. 56 | #define O@D_:%0_())?(((CALL@On%1()),O@V_)?1:2):(O@V_)) O@G_:%0@("On"#%1))) 57 | #define O@G_:%0("On"#%1<%9>))) %0("On"#%1))) 58 | 59 | // Generate an example call, then extract the parameters from it. 60 | #define prototypeof(%1) (_:CALL@%1:M@:N@:O@C_:$()(F@_@:A@=F@_@:tagof(F@_@:))) 61 | 62 | // Strip internal spaces. 63 | #define M@:%8$(%0\32;%1)( M@:%8$(%0%1)( 64 | 65 | // No parameters (end). 66 | #define N@:%8$() 67 | 68 | // Give the call a trailing comma, and start the macros. 69 | #define O@C_:$(%0) O@E_:N@:$(%0,) 70 | 71 | // Detect the end of the parameter list. 72 | #define O@E_:%8$(%0,%7)( O@H_:(%0)O@E_:%8$(%7)( 73 | 74 | // Detect common parameter types. The extra `0` is to distinguish between `0` 75 | // and `0.0`. The first will have the macro name changed, the second won't. 76 | #define O@H_:(%0) P@%00: 77 | #define P@""0:%8$(%7)(%4:A@=%4:tagof(%4:)) %8$(%7)(%4s:A@=%4s:tagof(%4s:)) 78 | #define P@__REF0:%8$(%7)(%4:A@=%4:tagof(%4:)) %8$(%7)(%4v:A@=%4v:tagof(%4v:)) 79 | #define P@__ARR0:%8$(%7)(%4:A@=%4:tagof(%4:)) %8$(%7)(%4s:A@=%4s:tagof(%4s:)) 80 | #define P@0.00:%8$(%7)(%4:A@=%4:tagof(%4:)) %8$(%7)(%4f:A@=%4f:tagof(%4f:)) 81 | #define P@00:%8$(%7)(%4:A@=%4:tagof(%4:)) %8$(%7)(%4i:A@=%4i:tagof(%4i:)) 82 | #define P@true0:%8$(%7)(%4:A@=%4:tagof(%4:)) %8$(%7)(%4i:A@=%4i:tagof(%4i:)) 83 | #define P@false0:%8$(%7)(%4:A@=%4:tagof(%4:)) %8$(%7)(%4i:A@=%4i:tagof(%4i:)) 84 | #define P@__minus10:%8$(%7)(%4:A@=%4:tagof(%4:)) %8$(%7)(%4i:A@=%4i:tagof(%4i:)) 85 | 86 | #define P@Text: P@ 87 | #define P@PlayerText: P@ 88 | #define P@Float: P@ 89 | #define P@File: P@ 90 | #define P@bool: P@ 91 | 92 | // For internal tag creation. 93 | stock A@ = 0; 94 | 95 | /// amx_assembly addressof 96 | stock bool:NativeIdxOfGetNextCall_() { 97 | // Start reading code from the point to which this function returns, looking 98 | // for the next "CALL" op to signal the function call from the macro. 99 | new ctx[DisasmContext]; 100 | DisasmInit(ctx, GetCurrentFrameReturn()); 101 | new candidate = -1; 102 | while (DisasmNext(ctx)) { 103 | #if OPCODE_HAS_O2 104 | if (ctx[DisasmContext_opcode] == OP_SYSREQ_C || ctx[DisasmContext_opcode] == OP_SYSREQ_N) { 105 | #else 106 | if (ctx[DisasmContext_opcode] == OP_SYSREQ_C) { 107 | #endif 108 | // Return the data in a global, to be repassed from the conditional. 109 | candidate = DisasmGetOperand(ctx); 110 | } 111 | else if (ctx[DisasmContext_opcode] == OP_JZER && candidate != -1) { 112 | // Get the last `SYSREQ.C` before the branch. Because some calls 113 | // may invoke additional natives in their stubbing, most notably 114 | // `float(0)` in e.g. `nativeidxof(floatmul)`. 115 | gAddressOfReturnVar_ = candidate; 116 | return false; 117 | } 118 | } 119 | // ALWAYS returns false so that the function call within "OP(&func)" will 120 | // never be called thanks to the conditional. 121 | return false; 122 | } 123 | 124 | -------------------------------------------------------------------------------- /addressof.md: -------------------------------------------------------------------------------- 1 | amx_assembly addressof 2 | ========================================== 3 | AMX Assembly Library: `addressof` function address lookup. 4 | ------------------------------------------ 5 | 6 | This library provides the `addressof` operator to get the position of a function in code (i.e the "address of" a function). It also provides `nativeidxof` to get the index of a native function from the header (like `funcidx` does for public functions; which raises the question of why this has `of` in the name, for which there is no answer). 7 | 8 | ### `addressof` 9 | 10 | This function takes a function and returns its address relative to the start of `COD`: 11 | 12 | ```pawn 13 | MyFunc() 14 | { 15 | } 16 | 17 | main() 18 | { 19 | new addr = addressof(MyFunc); 20 | printf("`MyFunc` is at address 0x%08x", addr); 21 | } 22 | ``` 23 | 24 | Because of limitations in the way the macro is implemented you must tell `addressof` how to call the function in question. It *does not* call the function, so there are no unexpected side-effects, but a valid call to the function must appear in the generated code in order to get the address. This is an unfortunate implementation detail leaked to users. There are two ways to provide this information - either a `CALL@` macro for the function you're getting the address of: 25 | 26 | ```pawn 27 | MyFunc(a, b) 28 | { 29 | } 30 | #define CALL@MyFunc%8() MyFunc%8(0, 1) // Yes, the `%8`s are needed. 31 | 32 | main() 33 | { 34 | new addr = addressof(MyFunc); 35 | printf("`MyFunc` is at address 0x%08x", addr); 36 | } 37 | ``` 38 | 39 | Or giving a specifier directly to `addressof`: 40 | 41 | ```pawn 42 | MyFunc(a, b) 43 | { 44 | } 45 | 46 | main() 47 | { 48 | new addr = _:addressof(MyFunc); 49 | printf("`MyFunc` is at address 0x%08x", addr); 50 | } 51 | ``` 52 | 53 | In the latter case the return value will have a tag representing how to call the function, so without the `_:` tag override as seen in the example above you would need to declare `addr` as `Func:addr`. 54 | 55 | The *addressof-jit.inc* library will re-compile `addressof` calls to a single constant when `AddressofResolve()` is called. It should really be a constant, but there's no (known) way of doing that at compile-time with only macros. However, compiler version 3.10.11 introduced a native `__addressof` operator to do exactly this (but without the tag-type information, so still using `addressof` is better; with it using `__addressof` underneath where possible). The original implementation of `addressof` used *disasm.inc* to find the fake function call and get the address from that. The new version (ignoring any `__addressof` usage) does away with that fairly heavy dependency and is a single small function, defined in *addressof-light.inc*. 56 | 57 | ### `nativeidxof` 58 | 59 | This function returns the address of a native function. All natives are compiled to a number, this number is the index of the function name in the AMX header (basically an array), and calling a native becomes `SYSREQ.C `. The obvious implementation of `nativeidxof` would simply read this table from the header until the native was found, but there is no guarantee in that case that the native will have actually been used to appear in the table (`__nameof` *may* solve this). Instead, in the same way as `addressof` has a fake call, so does `nativeidxof`, thus has the same limitations in use - i.e. you either need to provide `CALL@Name%8()` or the `` specifier. However, unlike `addressof` the return value is never tagged. 60 | 61 | The index is currently read from the generated assembly, using `disasm` (thus bringing in that include when you use *addressof.inc* rather than *addressof-light.inc* directly). Searching the header is possible, but with some care due to how name offsets are clobbered when running a 32-bit script on a 64-bit server (the `SYSREQ.D` pointers take up two cells instead of one). This, however, is not information relevant to simply using the function. *addressof-jit.inc* does not currently optimise this lookup, but can do. 62 | 63 | ## Variables 64 | 65 | 66 | ### `O@V_`: 67 | 68 | 69 | ## Functions 70 | 71 | 72 | ### `O@A_`: 73 | 74 | 75 | #### Syntax 76 | 77 | 78 | ```pawn 79 | O@A_() 80 | ``` 81 | 82 | #### Tag 83 | `bool:` 84 | 85 | 86 | #### Depends on 87 | * [`DisasmContext`](#DisasmContext) 88 | * [`DisasmContext_opcode`](#DisasmContext_opcode) 89 | * [`DisasmGetOperandReloc`](#DisasmGetOperandReloc) 90 | * [`DisasmInit`](#DisasmInit) 91 | * [`DisasmNext`](#DisasmNext) 92 | * [`GetCurrentFrameReturn`](#GetCurrentFrameReturn) 93 | * [`O@V_`](#O@V_) 94 | * [`OP_CALL`](#OP_CALL) 95 | * [`false`](#false) 96 | #### Estimated stack usage 97 | 11 cells 98 | 99 | 100 | 101 | ### `O@B_`: 102 | 103 | 104 | #### Syntax 105 | 106 | 107 | ```pawn 108 | O@B_() 109 | ``` 110 | 111 | #### Tag 112 | `bool:` 113 | 114 | 115 | #### Depends on 116 | * [`DisasmContext`](#DisasmContext) 117 | * [`DisasmContext_opcode`](#DisasmContext_opcode) 118 | * [`DisasmGetOperand`](#DisasmGetOperand) 119 | * [`DisasmInit`](#DisasmInit) 120 | * [`DisasmNext`](#DisasmNext) 121 | * [`GetCurrentFrameReturn`](#GetCurrentFrameReturn) 122 | * [`O@V_`](#O@V_) 123 | * [`OP_SYSREQ_C`](#OP_SYSREQ_C) 124 | * [`false`](#false) 125 | #### Estimated stack usage 126 | 11 cells 127 | 128 | -------------------------------------------------------------------------------- /addressof_jit.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016 Y_Less 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined ADDRESSOF_JIT_INC 22 | #endinput 23 | #endif 24 | #define ADDRESSOF_JIT_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | * addressof works by reading data directly out of the stack to get 36 | * a return address, then reading information from that location in memory 37 | * to get the next CALL OpCode. This fails with the JIT because the 38 | * return address is in the JITed code, not in the original p-code. 39 | * Instead, when the JIT is in use, use codescan to convert 40 | * runtime addressof calls to startup-time resolutions. Actually, 41 | * there's no reason why this should be restricted to JIT only. 42 | * 43 | * 44 | */ 45 | 46 | ///

47 | 48 | #include "addressof_light" 49 | #include "codescan" 50 | #include "asm" 51 | 52 | /// amx_assembly addressof_jit 53 | static stock AddressofResolveFoundStart(const scanner[CodeScanner]) { 54 | // Skip over now superfluous code. 55 | new ctx[AsmContext]; 56 | CodeScanGetMatchAsm(scanner, ctx); 57 | new hdr[AMX_HDR]; 58 | GetAmxHeader(hdr); 59 | AsmEmitJump(ctx, CodeScanGetMatchHole(scanner, 0) + GetAmxBaseAddress() + hdr[AMX_HDR_COD]); 60 | new dctx[DisasmContext]; 61 | CodeScanGetMatchDisasm(scanner, dctx), 62 | // Skip the three matched instructions. 63 | DisasmNextInsn(dctx), 64 | DisasmNextInsn(dctx), 65 | DisasmNextInsn(dctx); 66 | // Find a `call`. 67 | while (DisasmNextInsn(dctx) != OP_CALL) 68 | { 69 | // Do nothing. 70 | } 71 | new ptr = DisasmGetOperandReloc(dctx); 72 | // Found the location. 73 | AsmInitPtr(ctx, CodeScanGetMatchHole(scanner, 0) - hdr[AMX_HDR_DAT] + hdr[AMX_HDR_COD], 2 * cellbytes); 74 | AsmEmitConstPri(ctx, ptr); 75 | } 76 | 77 | /// amx_assembly addressof_jit 78 | stock AddressofResolve() { 79 | static 80 | bool:gAddressofResolved = false; 81 | if (gAddressofResolved) 82 | return; 83 | gAddressofResolved = true; 84 | // Scan through the entire assembly and replace calls to `addressof` with 85 | // just a constant. The generated assembly is amazingly consistent between 86 | // different optimisation levels: 87 | // 88 | // push.c 0 // Make `jump l.185` 89 | // call .O@A_ 90 | // jzer 185 // Jump target. 91 | // push.c 0 // (tested function call). 92 | // call .MyFunc // (tested function call). 93 | // load.pri 2950 94 | // jzer 187 95 | // const.pri 1 96 | // jump 188 97 | // l.187 98 | // const.pri 2 99 | // l.188 100 | // jump 186 101 | // l.185 102 | // load.pri 2950 // Make `const.pri .MyFunc` 103 | // l.186 104 | // 105 | // We can actually use `addressof` (via `&`) in this code, since it should 106 | // NEVER be called after the JIT has started. We assert that. 107 | assert(!GetAmxJITBaseAddress()); 108 | new scanner[CodeScanner]; 109 | CodeScanInit(scanner); 110 | 111 | // Start. 112 | new csm1[CodeScanMatcher]; 113 | CodeScanMatcherInit(csm1, &AddressofResolveFoundStart); 114 | CodeScanMatcherPattern(csm1, 115 | OP(PUSH_C, 0) 116 | OP(CALL, &AddressOfGetNextCall_) 117 | OP(JZER, ???) 118 | ); 119 | CodeScanAddMatcher(scanner, csm1); 120 | 121 | // Run the scanner. 122 | CodeScanRunFast(scanner, &AddressOfGetNextCall_); 123 | } 124 | 125 | -------------------------------------------------------------------------------- /addressof_jit.md: -------------------------------------------------------------------------------- 1 | amx_assembly addressof_jit 2 | ========================================== 3 | AMX Assembly Library: `addressof` JIT code. 4 | ------------------------------------------ 5 | 6 | `addressof` works by reading data directly out of the stack to get a return address, then reading information from that location in memory to get the next `CALL` OpCode. This fails with the JIT because the return address is in the JITed code, not in the original p-code. Instead this library uses *codescan.inc* to convert runtime `addressof` calls to startup-time resolutions (i.e. constants). Despite being called "addressof-jit", this is useful in normal execution modes as well. See [addressof.md](addressof.md) for more details. 7 | 8 | 9 | ## Functions 10 | 11 | 12 | ### `AddressofResolve`: 13 | 14 | 15 | #### Syntax 16 | 17 | 18 | ```pawn 19 | AddressofResolve() 20 | ``` 21 | 22 | #### Depends on 23 | * [`AddressofResolveFoundEnd`](#AddressofResolveFoundEnd) 24 | * [`AddressofResolveFoundStart`](#AddressofResolveFoundStart) 25 | * [`CodeScanAddMatcher`](#CodeScanAddMatcher) 26 | * [`CodeScanInit`](#CodeScanInit) 27 | * [`CodeScanMatcher`](#CodeScanMatcher) 28 | * [`CodeScanMatcherInit_`](#CodeScanMatcherInit_) 29 | * [`CodeScanMatcherPattern_`](#CodeScanMatcherPattern_) 30 | * [`CodeScanRunFast`](#CodeScanRunFast) 31 | * [`CodeScanner`](#CodeScanner) 32 | * [`GetAmxJITBaseAddress`](#GetAmxJITBaseAddress) 33 | * [`O@A_`](#O@A_) 34 | * [`O@V_`](#O@V_) 35 | * [`OP_CALL`](#OP_CALL) 36 | * [`OP_CONST_PRI`](#OP_CONST_PRI) 37 | * [`OP_HEAP`](#OP_HEAP) 38 | * [`OP_JUMP`](#OP_JUMP) 39 | * [`OP_JZER`](#OP_JZER) 40 | * [`OP_LOAD_PRI`](#OP_LOAD_PRI) 41 | * [`OP_PUSH_C`](#OP_PUSH_C) 42 | * [`false`](#false) 43 | * [`gCodeScanCallback_match`](#gCodeScanCallback_match) 44 | * [`ref`](#ref) 45 | * [`true`](#true) 46 | #### Estimated stack usage 47 | 761 cells 48 | 49 | 50 | 51 | ### `AddressofResolveFoundEnd`: 52 | 53 | 54 | #### Syntax 55 | 56 | 57 | ```pawn 58 | AddressofResolveFoundEnd(scanner[]) 59 | ``` 60 | 61 | 62 | #### Parameters 63 | 64 | 65 | | **Name** | **Info** | 66 | | --- | --- | 67 | | `scanner` | ` [172] ` | 68 | 69 | #### Depends on 70 | * [`AsmContext`](#AsmContext) 71 | * [`AsmEmitConstPri`](#AsmEmitConstPri) 72 | * [`CodeScanGetMatchAsm`](#CodeScanGetMatchAsm) 73 | * [`CodeScanGetMatchHole`](#CodeScanGetMatchHole) 74 | * [`CodeScanGetMatchLength`](#CodeScanGetMatchLength) 75 | * [`cellbytes`](#cellbytes) 76 | #### Estimated stack usage 77 | 27 cells 78 | 79 | 80 | 81 | ### `AddressofResolveFoundStart`: 82 | 83 | 84 | #### Syntax 85 | 86 | 87 | ```pawn 88 | AddressofResolveFoundStart(scanner[]) 89 | ``` 90 | 91 | 92 | #### Parameters 93 | 94 | 95 | | **Name** | **Info** | 96 | | --- | --- | 97 | | `scanner` | ` [172] ` | 98 | 99 | #### Depends on 100 | * [`AMX_HDR`](#AMX_HDR) 101 | * [`AMX_HDR_COD`](#AMX_HDR_COD) 102 | * [`AsmContext`](#AsmContext) 103 | * [`AsmEmitJump`](#AsmEmitJump) 104 | * [`CodeScanGetMatchAsm`](#CodeScanGetMatchAsm) 105 | * [`CodeScanGetMatchHole`](#CodeScanGetMatchHole) 106 | * [`GetAmxBaseAddress`](#GetAmxBaseAddress) 107 | * [`GetAmxHeader`](#GetAmxHeader) 108 | #### Estimated stack usage 109 | 44 cells 110 | 111 | -------------------------------------------------------------------------------- /addressof_light.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2016 Y_Less 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined ADDRESSOF_LIGHT_INC 22 | #endinput 23 | #endif 24 | #define ADDRESSOF_LIGHT_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #if !defined cellbytes 40 | const cellbytes = cellbits / charbits; 41 | #endif 42 | 43 | // This is a horrible hack, I'm sorry for ruining your nice clean 44 | // library with references to YSI Zeex! It was (probably not) the only 45 | // way to detect some issues people were having and alert them to it, 46 | // plus it is completely optional. 47 | #if defined __YSI_CALL_UPGRADED 48 | // Check if YSI is updated to the new `CALL@` syntax. 49 | #if defined AMX_OLD_CALL 50 | #error amx_assembly and YSI are correctly matched; don't use `#define AMX_OLD_CALL`. 51 | #endif 52 | #elseif defined __COMPILER_PASS 53 | // Check if any version of YSI is included. This macro name is ancient. 54 | #if !defined AMX_OLD_CALL 55 | #error amx_assembly and YSI are mismatched. Update YSI, downgrade amx_assembly, or use `#define AMX_OLD_CALL`. 56 | #endif 57 | #endif 58 | 59 | #define __AMX_CALL_UPGRADED 60 | 61 | // For future compiler version. TODO: Figure out `` in this syntax. 62 | //#define addressof __addressof 63 | //#define __addressof(%0<%1>) (F@_@%1:__addressof(%0)) 64 | 65 | // This code uses two nested conditionals because of: 66 | // https://github.com/Zeex/pawn/issues/96 (this doesn't work): 67 | // 68 | // ((O@D_:O@A_()) ? ((CALL@%1()), 0) : (O@V_)) 69 | // 70 | // Even though it is the obvious solution when you don't want the result of 71 | // "CALL@%1()" to be used (as it may not exist), and you can't use a constant 72 | // instead of "O@V_" because then if becomes a constant in a 73 | // condition, which the compiler rightly complains about. Of course, because 74 | // "O@A_()" always returns "false", the entire other block of code 75 | // is jumped over. 76 | #if defined AMX_OLD_CALL 77 | #define addressof(%1) (O@A_()?(((CALL@%1),O@V_)?1:2):(O@V_)) 78 | #define CALL@%0\32; CALL@ 79 | 80 | #define gAddressOfReturnVar_ O@V_ 81 | #define AddressOfGetNextCall_ O@A_ 82 | #define CALL@AddressOfGetNextCall_ CALL@O@A_ 83 | #define CALL@O@A_ O@A_() 84 | 85 | #define O@A_()?(((CALL@%1<%2>),O@V_)?1:2):(O@V_) (O@A_()?(((_ADDR@$%1()<%2>),O@V_)?(F@_@:1):(F@_@:2)):(F@_@:O@V_)) 86 | #else 87 | #define addressof(%1) ((O@D_:O@A_())?(((CALL@%1()),O@V_)?1:2):(O@V_)) 88 | #define CALL@%0\32; CALL@ 89 | 90 | #define gAddressOfReturnVar_ O@V_ 91 | #define AddressOfGetNextCall_ O@A_ 92 | #define CALL@AddressOfGetNextCall_%8() CALL@O@A_%8() 93 | #define CALL@O@A_%8() O@A_%8() 94 | #define CALL@O@A@%8() O@A@%8("") 95 | 96 | #define O@A_())?(((CALL@%1<%2>()),O@V_)?1:2):(O@V_) O@A_())?(((_ADDR@$%1()<%2>),O@V_)?(F@_@:1):(F@_@:2)):(F@_@:O@V_) 97 | #endif 98 | 99 | // Enable parameter checks for `Func` types. 100 | #define _ADDR@ _ADDR@i:_ADDR@t:_ADDR@f:_ADDR@v:_ADDR@s:_ADDR@a:_ADDR@x:_ADDR@z 101 | 102 | #define _ADDR@z$%0(%1)<%2> _ADDR@y:%0(%1) 103 | #define _ADDR@y:%0(,%1) %0(%1) 104 | 105 | #define _ADDR@i:%8$%0(%1)),O@V_)?(%9:1):(%9:2)):(%9:O@V_)) _ADDR@$%0(%1,0)<%2>),O@V_)?(%9i:1):(%9i:2)):(%9i:O@V_)) 106 | #define _ADDR@t:%8$%0(%1)),O@V_)?(%9:1):(%9:2)):(%9:O@V_)) _ADDR@$%0(%1,%5:0)<%2>),O@V_)?(%9i:1):(%9i:2)):(%9i:O@V_)) 107 | #define _ADDR@f:%8$%0(%1)),O@V_)?(%9:1):(%9:2)):(%9:O@V_)) _ADDR@$%0(%1,0.0)<%2>),O@V_)?(%9f:1):(%9f:2)):(%9f:O@V_)) 108 | #define _ADDR@v:%8$%0(%1)),O@V_)?(%9:1):(%9:2)):(%9:O@V_)) _ADDR@$%0(%1,__REF)<%2>),O@V_)?(%9v:1):(%9v:2)):(%9v:O@V_)) 109 | #define _ADDR@s:%8$%0(%1)),O@V_)?(%9:1):(%9:2)):(%9:O@V_)) _ADDR@$%0(%1,__ARR)<%2>),O@V_)?(%9s:1):(%9s:2)):(%9s:O@V_)) 110 | #define _ADDR@a:%8$%0(%1)),O@V_)?(%9:1):(%9:2)):(%9:O@V_)) _ADDR@$%0(%1,__ARR)<%2>),O@V_)?(%9a:1):(%9a:2)):(%9a:O@V_)) 111 | #define _ADDR@x:%8$%0(%1)),O@V_)?(%9:1):(%9:2)):(%9:O@V_)) _ADDR@z$%0(%1)<>),O@V_)?(%9x:1):(%9x:2)):(%9x:O@V_)) 112 | 113 | /// amx_assembly addressof_light 114 | stock __ARR[1] = {}; 115 | 116 | /// amx_assembly addressof_light 117 | stock __REF = 0; 118 | 119 | /// amx_assembly addressof_light 120 | stock gAddressOfReturnVar_ = 0; 121 | 122 | /// amx_assembly addressof_light 123 | forward bool:AddressOfGetNextCall_(); 124 | 125 | //stock bool:AddressOfGetNextCall_() { 126 | // // Start reading code from the point to which this function returns, looking 127 | // // for the next "CALL" op to signal the function call from the macro. 128 | // new ctx[DisasmContext]; 129 | // DisasmInit(ctx, GetCurrentFrameReturn()); 130 | // while (DisasmNext(ctx)) { 131 | // if (ctx[DisasmContext_opcode] == OP_CALL) { 132 | // // Return the data in a global, to be repassed from the conditional. 133 | // gAddressOfReturnVar_ = DisasmGetOperandReloc(ctx); 134 | // return false; 135 | // } 136 | // } 137 | // // ALWAYS returns false so that the function call within "OP(&func)" will 138 | // // never be called thanks to the conditional. 139 | // return false; 140 | //} 141 | 142 | /// amx_assembly addressof_light 143 | stock bool:AddressOfGetNextCall_() { 144 | static OpCode:call = OpCode:0; 145 | static reloc = 0; 146 | static offset = 0; 147 | 148 | // Get the return address. 149 | new addr; 150 | const cellsRetn = 1 * cellbytes; 151 | 152 | // Get the `CALL` OpCode from two cells before this call. 153 | if (call) { 154 | #emit load.s.alt cellsRetn 155 | #emit load.pri offset 156 | #emit sub.alt 157 | #emit stor.s.pri addr 158 | } else { 159 | #emit lctrl 0 160 | #emit move.alt 161 | #emit lctrl 1 162 | #emit sub 163 | #emit stor.pri offset 164 | #emit load.s.alt cellsRetn 165 | #emit sub.alt 166 | #emit stor.s.pri addr 167 | 168 | // `CALL` is opcode 49, which is an odd number. So we don't need to 169 | // worry about conflicts with variable's addresses. Subtract two cells 170 | // from the return address to get the initial `CALL`. 171 | addr -= 2 * cellbytes; 172 | #emit lref.s.pri addr 173 | #emit stor.pri call 174 | 175 | addr += 1 * cellbytes; 176 | // Get the jump offset by using this function. 177 | #emit lref.s.pri addr 178 | #emit const.alt O@A_ // AddressOfGetNextCall_ 179 | #emit sub 180 | #emit stor.pri reloc 181 | } 182 | 183 | for ( ; ; ) { 184 | // We can skip the first cell after the return. It cannot ever be the 185 | // `CALL` op. 186 | addr += 1 * cellbytes; 187 | #emit lref.s.pri addr 188 | #emit stor.pri O@V_ // gAddressOfReturnVar_ 189 | if (OpCode:gAddressOfReturnVar_ == call) { 190 | addr += 1 * cellbytes; 191 | // TODO: Reloc. 192 | #emit lref.s.pri addr 193 | #emit load.alt reloc 194 | #emit sub 195 | #emit stor.pri O@V_ // gAddressOfReturnVar_ 196 | break; 197 | } 198 | } 199 | 200 | // ALWAYS returns false so that the function call within "OP(&func)" will 201 | // never be called thanks to the conditional. 202 | return false; 203 | } 204 | 205 | -------------------------------------------------------------------------------- /amx.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined AMX_INC 22 | #endinput 23 | #endif 24 | #define AMX_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include "phys_memory" 40 | #include "shellcode" 41 | 42 | /// amx_assembly amx 43 | const AMX_OFFSET_BASE = 0; 44 | 45 | /// amx_assembly amx 46 | const AMX_OFFSET_DATA = 4; 47 | 48 | /// amx_assembly amx 49 | const AMX_OFFSET_CALLBACK = 8; 50 | 51 | /// amx_assembly amx 52 | const AMX_OFFSET_DEBUG = 12; 53 | 54 | /// amx_assembly amx 55 | const AMX_OFFSET_CIP = 16; 56 | 57 | /// amx_assembly amx 58 | const AMX_OFFSET_FRM = 20; 59 | 60 | /// amx_assembly amx 61 | const AMX_OFFSET_HEA = 24; 62 | 63 | /// amx_assembly amx 64 | const AMX_OFFSET_HLW = 28; 65 | 66 | /// amx_assembly amx 67 | const AMX_OFFSET_STK = 32; 68 | 69 | /// amx_assembly amx 70 | const AMX_OFFSET_STP = 36; 71 | 72 | /// amx_assembly amx 73 | const AMX_OFFSET_FLAGS = 40; 74 | 75 | /// amx_assembly amx 76 | const AMX_OFFSET_USERTAGS = 44; 77 | 78 | /// amx_assembly amx 79 | const AMX_OFFSET_USERDATA = 60; 80 | 81 | /// amx_assembly amx 82 | const AMX_OFFSET_ERROR = 76; 83 | 84 | /// amx_assembly amx 85 | const AMX_OFFSET_PARAMCOUNT = 80; 86 | 87 | /// amx_assembly amx 88 | const AMX_OFFSET_PRI = 84; 89 | 90 | /// amx_assembly amx 91 | const AMX_OFFSET_ALT = 88; 92 | 93 | /// amx_assembly amx 94 | const AMX_OFFSET_RESET_STK = 92; 95 | 96 | /// amx_assembly amx 97 | const AMX_OFFSET_RESET_HEA = 96; 98 | 99 | /// amx_assembly amx 100 | const AMX_OFFSET_SYSREQ_D = 100; 101 | 102 | /// amx_assembly amx 103 | ///

104 | /// Returns the address of the AMX struct instance in memory that corresponds 105 | /// to this script. This function works only on Windows! 106 | /// 107 | stock GetAmxAddress() { 108 | static address = 0; 109 | if (address == 0) { 110 | #if cellbits == 32 111 | static const code[] = { 112 | 0x90909090, 0x90909090, 0x90909090, 0x90909090, // for alignment 113 | 0x0424448B, 0xC3C3C3C3 114 | // 8B 44 24 04 - MOV eax, [esp + arg_0] 115 | // C3 - RETN 116 | }; 117 | #elseif cellbits == 64 118 | static const code[] = { 119 | 0x9090909090909090, 0x9090909090909090, // for alignment 120 | 0xC3C3C3C30424448B 121 | // 8B 44 24 04 - MOV eax, [esp + arg_0] 122 | // C3 - RETN 123 | }; 124 | #else 125 | #error Unsupported `cellbits`. 126 | #endif 127 | address = RunShellcode(refabs(code)); 128 | } 129 | return address; 130 | } 131 | 132 | /// amx_assembly amx 133 | /// 134 | /// Reads a cell from the AMX struct. 135 | /// 136 | stock ReadAmxCell(offset) { 137 | new amx = GetAmxAddress(); 138 | return ReadPhysMemoryCell(amx + offset); 139 | } 140 | 141 | /// amx_assembly amx 142 | /// 143 | /// Writes a cell to the AMX struct. 144 | /// 145 | stock WriteAmxCell(offset, value) { 146 | new amx = GetAmxAddress(); 147 | WritePhysMemoryCell(amx + offset, value); 148 | } 149 | 150 | -------------------------------------------------------------------------------- /amx.md: -------------------------------------------------------------------------------- 1 | amx_assembly amx 2 | ========================================== 3 | AMX Assembly Library: `AMX*` struct pointer determination. 4 | ------------------------------------------ 5 | 6 | `struct AMX` is the structure in the server/VM that holds information about a loaded script - next instruction, segment addresses, etc. This library, through an expoit in certain versions of the VM, allows scripts to access their own server-internal data, to read and write to it. This makes it very easy to corrupt the currently running script. The functions provided only give very low-level access to reading and writing offsets. To find where in the structure you need to read certain data from check *amx.h* in the pawn runtime source. This include uses both *phys_memory.inc* and *shellcode.inc* to execute arbitrary *x86* machine code, which sounds scary, but that code can only be defined in a script explicitly executed. Furthermore the exploit in question only works on Windows, and is disabled by any VM without code relocation (notably, *open.mp*). 7 | 8 | ## Functions 9 | 10 | 11 | ### `GetAmxAddress`: 12 | 13 | Returns the address of the AMX struct instance in memory that corresponds to this script. This function works only on Windows! 14 | 15 | 16 | 17 | #### Syntax 18 | 19 | 20 | ```pawn 21 | GetAmxAddress() 22 | ``` 23 | 24 | #### Depends on 25 | * [`RunShellcode`](#RunShellcode) 26 | * [`cellbits`](#cellbits) 27 | * [`refabs`](#refabs) 28 | #### Estimated stack usage 29 | 5 cells 30 | 31 | 32 | 33 | ### `ReadAmxCell`: 34 | 35 | Reads a cell from the AMX struct. 36 | 37 | 38 | 39 | #### Syntax 40 | 41 | 42 | ```pawn 43 | ReadAmxCell(offset) 44 | ``` 45 | 46 | 47 | #### Parameters 48 | 49 | 50 | | **Name** | **Info** | 51 | | --- | --- | 52 | | `offset` | | 53 | 54 | #### Depends on 55 | * [`GetAmxAddress`](#GetAmxAddress) 56 | * [`ReadPhysMemoryCell`](#ReadPhysMemoryCell) 57 | #### Estimated stack usage 58 | 5 cells 59 | 60 | 61 | 62 | ### `WriteAmxCell`: 63 | 64 | Writes a cell to the AMX struct. 65 | 66 | 67 | 68 | #### Syntax 69 | 70 | 71 | ```pawn 72 | WriteAmxCell(offset, value) 73 | ``` 74 | 75 | 76 | #### Parameters 77 | 78 | 79 | | **Name** | **Info** | 80 | | --- | --- | 81 | | `offset` | | 82 | | `value` | | 83 | 84 | #### Depends on 85 | * [`GetAmxAddress`](#GetAmxAddress) 86 | * [`WritePhysMemoryCell`](#WritePhysMemoryCell) 87 | #### Estimated stack usage 88 | 6 cells 89 | 90 | -------------------------------------------------------------------------------- /amx_base.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011-2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined AMX_BASE_INC 22 | #endinput 23 | #endif 24 | #define AMX_BASE_INC 25 | 26 | /** 27 | * 28 | * 29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #if defined __PawnBuild 40 | #if __PawnBuild == 0 41 | #undef __PawnBuild 42 | #define __pawn_build 0 43 | #else 44 | #define __pawn_build __PawnBuild 45 | #endif 46 | #else 47 | #define __pawn_build 0 48 | #endif 49 | 50 | #if !defined cellbytes 51 | const cellbytes = cellbits / charbits; 52 | #endif 53 | 54 | #if !defined __register_names 55 | const __cod = 0; 56 | const __dat = 1; 57 | const __hea = 2; 58 | const __stp = 3; 59 | const __stk = 4; 60 | const __frm = 5; 61 | const __cip = 6; 62 | const __jit = 7; 63 | const __jmp = 8; 64 | const __flg = 9; 65 | #define __register_names 66 | #endif 67 | 68 | #if __pawn_build >= 10 69 | #define HasSysreqD() (bool:(__emit(zero.pri, lctrl __flg, eq.c.pri 0))) 70 | #define HasReloc() (bool:(__emit(zero.pri, lctrl __flg, const.alt 512, and, eq.c.pri 0))) 71 | #else 72 | forward bool:HasSysreqD(); 73 | forward bool:HasReloc(); 74 | #endif 75 | 76 | #if !defined cellbytes 77 | const cellbytes = cellbits / charbits; 78 | #endif 79 | 80 | /// amx_assembly amx_base 81 | static stock GetAmxBaseAddress_helper() { 82 | return 0; 83 | } 84 | 85 | /// amx_assembly amx_base 86 | ///

87 | /// Returns the AMX base address i.e. amx->base. 88 | /// 89 | stock GetAmxBaseAddressNow() { 90 | // Required for this trick to work. 91 | assert(HasReloc()); 92 | 93 | new cod = 0, dat = 0; 94 | #emit lctrl __cod 95 | #emit stor.s.pri cod 96 | #emit lctrl __dat 97 | #emit stor.s.pri dat 98 | 99 | // Get code section start address relative to data. 100 | new code_start = cod - dat; 101 | 102 | // Get address of GetAmxBaseAddress_helper(). 103 | new fn_addr = 0; 104 | #emit const.pri GetAmxBaseAddress_helper 105 | #emit stor.s.pri fn_addr 106 | 107 | // Get absolute address from the CALL instruction. 108 | new fn_addr_reloc = 0, call_addr = 0; 109 | GetAmxBaseAddress_helper(); 110 | #emit lctrl __cip 111 | #emit stor.s.pri call_addr 112 | call_addr = call_addr - 3 * cellbytes + code_start; 113 | #emit lref.s.pri call_addr 114 | #emit stor.s.pri fn_addr_reloc 115 | 116 | return fn_addr_reloc - fn_addr - cod; 117 | } 118 | 119 | /// amx_assembly amx_base 120 | stock GetAmxBaseAddress() { 121 | static amx_base = 0; 122 | if (amx_base == 0) { 123 | amx_base = GetAmxBaseAddressNow(); 124 | } 125 | return amx_base; 126 | } 127 | 128 | #if __pawn_build >= 10 129 | #endinput 130 | #endif 131 | 132 | /// amx_assembly amx_base 133 | stock bool:HasSysreqD() { 134 | // SA:MP doesn't have the AMX flags in register 9, open.mp does. Thus if 135 | // `pri` is 0 it wasn't set by the control register. 136 | #emit zero.pri 137 | #emit lctrl __flg // flags 138 | #emit eq.c.pri 0 139 | #emit retn 140 | 141 | return false; 142 | } 143 | 144 | /// amx_assembly amx_base 145 | stock bool:HasReloc() { 146 | // SA:MP doesn't have the AMX flags in register 9, open.mp does. Thus if 147 | // `pri` is 0 it wasn't set by the control register. 148 | #emit zero.pri 149 | #emit lctrl __flg // flags 150 | #emit const.alt 512 151 | #emit and 152 | #emit eq.c.pri 0 153 | #emit retn 154 | 155 | return false; 156 | } 157 | 158 | -------------------------------------------------------------------------------- /amx_jit.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined AMX_JIT_INC 22 | #endinput 23 | #endif 24 | #define AMX_JIT_INC 25 | 26 | /** 27 | * 28 | * 29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #if !defined __register_names 40 | const __cod = 0; 41 | const __dat = 1; 42 | const __hea = 2; 43 | const __stp = 3; 44 | const __stk = 4; 45 | const __frm = 5; 46 | const __cip = 6; 47 | const __jit = 7; 48 | const __jmp = 8; 49 | const __flg = 9; 50 | #define __register_names 51 | #endif 52 | 53 | /// amx_assembly amx_jit 54 | stock GetJITGeneratorVersion() { 55 | #emit zero.pri 56 | #emit lctrl __jit 57 | #emit retn 58 | return 0; 59 | } 60 | 61 | /// amx_assembly amx_jit 62 | stock GetAmxJITBaseAddress() { 63 | // Only works when the jit is installed, otherwise returns 0. The code is 64 | // so short, checking if it had been done before would have been longer than 65 | // just repeating the calculation. 66 | #emit zero.pri 67 | #emit lctrl __jmp 68 | #emit retn 69 | return 0; 70 | } 71 | 72 | /// amx_assembly amx_jit 73 | stock ResolveJITAddress(addr) { 74 | // Converts an address to a JIT address, if there is a JIT. Otherwise does 75 | // nothing. Could make it try detect if this is already JIT resolved, i.e. 76 | // is a return address. 77 | #emit load.s.pri addr 78 | #emit lctrl __jmp 79 | #emit retn 80 | return 0; 81 | } 82 | 83 | -------------------------------------------------------------------------------- /amx_jit.md: -------------------------------------------------------------------------------- 1 | amx_assembly amx_jit 2 | ========================================== 3 | AMX Assembly Library: JIT plugin address relocations. 4 | ------------------------------------------ 5 | 6 | 7 | 8 | ## Functions 9 | 10 | 11 | ### `GetAmxJITBaseAddress`: 12 | 13 | 14 | #### Syntax 15 | 16 | 17 | ```pawn 18 | GetAmxJITBaseAddress() 19 | ``` 20 | 21 | #### Estimated stack usage 22 | 1 cells 23 | 24 | 25 | 26 | ### `GetJITGeneratorVersion`: 27 | 28 | 29 | #### Syntax 30 | 31 | 32 | ```pawn 33 | GetJITGeneratorVersion() 34 | ``` 35 | 36 | #### Estimated stack usage 37 | 1 cells 38 | 39 | 40 | 41 | ### `ResolveJITAddress`: 42 | 43 | 44 | #### Syntax 45 | 46 | 47 | ```pawn 48 | ResolveJITAddress(addr) 49 | ``` 50 | 51 | 52 | #### Parameters 53 | 54 | 55 | | **Name** | **Info** | 56 | | --- | --- | 57 | | `addr` | | 58 | 59 | #### Estimated stack usage 60 | 1 cells 61 | 62 | -------------------------------------------------------------------------------- /amx_memory.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined AMX_MEMORY_INC 22 | #endinput 23 | #endif 24 | #define AMX_MEMORY_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include 40 | 41 | #if defined __PawnBuild 42 | #if __PawnBuild == 0 43 | #undef __PawnBuild 44 | #define __pawn_build 0 45 | #else 46 | #define __pawn_build __PawnBuild 47 | #endif 48 | #else 49 | #define __pawn_build 0 50 | #endif 51 | 52 | #if !defined cellbytes 53 | const cellbytes = cellbits / charbits; 54 | #endif 55 | 56 | #if !defined __register_names 57 | const __cod = 0; 58 | const __dat = 1; 59 | const __hea = 2; 60 | const __stp = 3; 61 | const __stk = 4; 62 | const __frm = 5; 63 | const __cip = 6; 64 | const __jit = 7; 65 | const __jmp = 8; 66 | const __flg = 9; 67 | #define __register_names 68 | #endif 69 | 70 | /// amx_assembly amx_memory 71 | ///

72 | /// Returns the address of a variable/array. 73 | /// 74 | stock ref(...) { 75 | const cells0 = 3 * cellbytes; 76 | assert(numargs() == 1); 77 | #emit load.s.pri cells0 // first argument 78 | #emit retn 79 | return 0; // make compiler happy 80 | } 81 | 82 | /// amx_assembly amx_memory 83 | /// 84 | /// Returns an array from an address. 85 | /// 86 | stock deref(v) { 87 | const cells0 = 4 * cellbytes; 88 | static gFake[1]; 89 | #emit load.s.pri v // first argument 90 | #emit stor.s.pri cells0 // secret argument 91 | #emit retn 92 | return gFake; // make compiler happy 93 | } 94 | 95 | /// amx_assembly amx_memory 96 | /// 97 | /// Returns the address of a variable parameter. 98 | /// 99 | stock argref(n) { 100 | #emit load.s.alt 0 101 | #emit load.s.pri n 102 | #emit add.c 3 103 | #emit lidx 104 | #emit retn 105 | return 0; // make compiler happy 106 | } 107 | 108 | /// amx_assembly amx_memory 109 | /// 110 | /// Returns an array from a variable parameter. 111 | /// 112 | stock argderef(n) { 113 | const cells0 = 4 * cellbytes; 114 | static gFake[1]; 115 | #emit load.s.alt 0 116 | #emit load.s.pri n 117 | #emit add.c 3 118 | #emit lidx 119 | #emit stor.s.pri cells0 // secret argument 120 | #emit retn 121 | return gFake; // make compiler happy 122 | } 123 | 124 | #if __pawn_build >= 10 125 | stock gAmxAssemblyAddress_; 126 | #define ReadAmxMemory(%0) __emit(load.u.pri %0, stor.pri gAmxAssemblyAddress_, lref.pri gAmxAssemblyAddress_) 127 | #define WriteAmxMemory(%0,%1) __emit(push.u %1, load.u.pri %0, stor.pri gAmxAssemblyAddress_, pop.pri, sref.pri gAmxAssemblyAddress_) 128 | #endif 129 | 130 | /// amx_assembly amx_memory 131 | stock ReadAmxMemoryArray(address, values[], size = sizeof(values)) { 132 | for (new i = 0; i < size; i++) { 133 | values[i] = ReadAmxMemory(address); 134 | address += cellbytes; 135 | } 136 | } 137 | 138 | /// amx_assembly amx_memory 139 | stock WriteAmxMemoryArray(address, const values[], size = sizeof(values)) { 140 | for (new i = 0; i < size; i++) { 141 | WriteAmxMemory(address, values[i]); 142 | address += cellbytes; 143 | } 144 | } 145 | 146 | /// amx_assembly amx_memory 147 | stock GetAmxNextInstructionPointer() 148 | { 149 | // Saying "get the current pointer" doesn't make a huge amount of sense - 150 | // getting the pointer will in itself take code, so exactly where is "here"? 151 | // This returns its own return address, which points to the instruction 152 | // directly after the call. This is at least well defined. 153 | const cells0 = 1 * cellbytes; 154 | #emit load.s.pri cells0 // Return address. 155 | #emit retn 156 | return 0; 157 | } 158 | 159 | /// amx_assembly amx_memory 160 | stock GetAmxHeapBase() { 161 | const cells0 = 5 * cellbytes; 162 | const cells1 = -1 * cellbytes; 163 | const cells2 = 1 * cellbytes; 164 | // Initial heap pointer. Not stored in an accessible register so read it 165 | // straight from the original header. 166 | #emit lctrl __dat // DAT 167 | #emit neg // -DAT 168 | #emit add.c cells0 // -DAT + 20 169 | #emit push.pri 170 | #emit lref.s.pri cells1 171 | #emit stack cells2 172 | #emit retn 173 | return 0; 174 | } 175 | 176 | /// amx_assembly amx_memory 177 | stock GetAmxHeapTop() 178 | { 179 | // Current heap pointer. 180 | #emit lctrl __hea 181 | #emit retn 182 | return 0; 183 | } 184 | 185 | /// amx_assembly amx_memory 186 | stock GetAmxStackBase() 187 | { 188 | #emit lctrl __stp 189 | #emit retn 190 | return 0; 191 | } 192 | 193 | /// amx_assembly amx_memory 194 | stock GetAmxStackBottom() { 195 | const cells0 = 3 * cellbytes; 196 | #emit lctrl __stk 197 | #emit add.c cells0 198 | #emit retn 199 | return 0; 200 | } 201 | 202 | /// amx_assembly amx_memory 203 | stock GetAmxFrame() 204 | { 205 | // Doesn't use "lctrl 5" because we want it to look like this function was 206 | // never called. Doing that would only return this function's frame. 207 | #emit load.s.pri 0 208 | #emit retn 209 | return 0; 210 | } 211 | 212 | /// amx_assembly amx_memory 213 | stock SetAmxHeapTop(ptr) 214 | { 215 | #emit load.s.pri ptr 216 | #emit sctrl __hea 217 | } 218 | 219 | /// amx_assembly amx_memory 220 | stock SetAmxStackBottom(ptr) { 221 | const cells0 = 1 * cellbytes; 222 | static cip = 0; 223 | // We need to be tricky here, because the return value is on the stack. 224 | #emit load.s.alt ptr 225 | // Store the return address. 226 | #emit load.s.pri cells0 227 | #emit stor.pri cip 228 | // Reset the frame. 229 | #emit load.s.pri 0 230 | #emit sctrl __frm 231 | // Modify the stack. 232 | #emit move.pri 233 | #emit sctrl __stk 234 | // Return. 235 | #emit load.pri cip 236 | #emit sctrl __jmp 237 | #emit sctrl __cip 238 | } 239 | 240 | /// amx_assembly amx_memory 241 | stock SetAmxFrame(ptr) { 242 | const cells0 = 1 * cellbytes; 243 | const cells1 = 4 * cellbytes; 244 | // We need to be tricky here, because the return value is in our frame. 245 | // Store the return address. 246 | #emit load.s.alt cells0 247 | // Modify the frame. 248 | #emit load.s.pri ptr 249 | #emit sctrl __frm 250 | // Return. 251 | #emit move.pri 252 | #emit stack cells1 253 | #emit sctrl __jmp 254 | #emit sctrl __cip 255 | } 256 | 257 | /// amx_assembly amx_memory 258 | stock SetAmxNextInstructionPointer(ptr) { 259 | const cells0 = 4 * cellbytes; 260 | #emit load.s.alt ptr 261 | // Reset the frame. 262 | #emit load.s.pri 0 263 | #emit sctrl __frm 264 | // Return. 265 | #emit move.pri 266 | #emit stack cells0 267 | #emit sctrl __jmp 268 | #emit sctrl __cip 269 | } 270 | 271 | #if __pawn_build >= 10 272 | #endinput 273 | #endif 274 | 275 | // After `#endif` because `#if` doesn't elide `#emit`. 276 | /// amx_assembly amx_memory 277 | stock WriteAmxMemory(address, value) { 278 | #emit load.s.pri value 279 | #emit sref.s.pri address 280 | #emit retn 281 | return 0; // make compiler happy 282 | } 283 | 284 | /// amx_assembly amx_memory 285 | stock ReadAmxMemory(address) { 286 | #emit lref.s.pri address 287 | #emit retn 288 | return 0; // make compiler happy 289 | } 290 | 291 | -------------------------------------------------------------------------------- /asm_macros.md: -------------------------------------------------------------------------------- 1 | amx_assembly asm_macros 2 | ========================================== 3 | AMX Assembly Library: `@emit` macros. 4 | ------------------------------------------ 5 | 6 | -------------------------------------------------------------------------------- /codescan.md: -------------------------------------------------------------------------------- 1 | amx_assembly codescan 2 | ========================================== 3 | AMX Assembly Library: Compiled code scanning and pattern matching. 4 | ------------------------------------------ 5 | 6 | 7 | 8 | ## Enums 9 | 10 | 11 | ### `CodeScanMatcher`: 12 | 13 | #### Members 14 | 15 | * `CodeScanMatcher_func = 0` 16 | * `CodeScanMatcher_user_data = 1` 17 | * `CodeScanMatcher_type = 2` 18 | * `CodeScanMatcher_code = 66` 19 | * `CodeScanMatcher_len = 130` 20 | * `CodeScanMatcher_offset = 131` 21 | * `CodeScanMatcher_start = 133` 22 | * `CodeScanMatcher_holeidx = 135` 23 | * `CodeScanMatcher_holes = 137` 24 | * `CodeScanMatcher_next = 169` 25 | * `CodeScanMatcher_flags = 170` 26 | 27 | ### `CodeScanner`: 28 | 29 | #### Members 30 | 31 | * `CodeScanMatch_func = 0` 32 | * `CodeScanMatch_size = 1` 33 | * `CodeScanMatch_type = 2` 34 | * `CodeScanMatch_heap = 3` 35 | * `CodeScanMatch_stack = 4` 36 | * `CodeScanMatch_params = 5` 37 | * `CodeScanMatch_cip = 6` 38 | * `CodeScanMatch_holes = 7` 39 | * `CodeScanMatch_hole_count = 23` 40 | * `CodeScanMatch_name = 24` 41 | * `CodeScanner_first = 40` 42 | * `CodeScanner_minn = 41` 43 | * `CodeScanner_jump_switch = 42` 44 | * `CodeScanner_jump_target = 74` 45 | * `CodeScanner_jump_stack = 106` 46 | * `CodeScanner_jump_heap = 138` 47 | * `CodeScanner_state = 170` 48 | * `CodeScanner_param = 171` 49 | 50 | ## Variables 51 | 52 | 53 | ### `gBase`: 54 | 55 | 56 | ### `gCodBase`: 57 | 58 | 59 | ### `gCodeScanCallback_match`: 60 | 61 | 62 | ### `gDat`: 63 | 64 | 65 | ### `gFakeMatcher`: 66 | 67 | 68 | ### `gHdr`: 69 | 70 | 71 | ### `gOP_CASETBL`: 72 | 73 | 74 | ### `gOP_NOP`: 75 | 76 | 77 | ### `gRelocateRequired`: 78 | 79 | #### Tag 80 | `bool:` 81 | 82 | 83 | 84 | ## Functions 85 | 86 | 87 | ### `CodeScanAddJumpTarget`: 88 | 89 | 90 | #### Syntax 91 | 92 | 93 | ```pawn 94 | CodeScanAddJumpTarget(cip, target, stk, hea, jumpTargets[], num) 95 | ``` 96 | 97 | 98 | #### Parameters 99 | 100 | 101 | | **Name** | **Info** | 102 | | --- | --- | 103 | | `cip` | | 104 | | `target` | | 105 | | `stk` | | 106 | | `hea` | | 107 | | `jumpTargets` | ` [172] ` | 108 | | `num` | | 109 | 110 | #### Depends on 111 | * [`CodeScanner_jump_heap`](#CodeScanner_jump_heap) 112 | * [`CodeScanner_jump_stack`](#CodeScanner_jump_stack) 113 | * [`CodeScanner_jump_switch`](#CodeScanner_jump_switch) 114 | * [`CodeScanner_jump_target`](#CodeScanner_jump_target) 115 | * [`CodeScanner_minn`](#CodeScanner_minn) 116 | * [`min`](#min) 117 | #### Estimated stack usage 118 | 4 cells 119 | 120 | 121 | 122 | ### `CodeScanAddMatcher`: 123 | 124 | 125 | #### Syntax 126 | 127 | 128 | ```pawn 129 | CodeScanAddMatcher(scanner[], searcher[]) 130 | ``` 131 | 132 | 133 | #### Parameters 134 | 135 | 136 | | **Name** | **Info** | 137 | | --- | --- | 138 | | `scanner` | ` [172] ` | 139 | | `searcher` | ` [171] ` | 140 | 141 | #### Depends on 142 | * [`CodeScanMatcher_next`](#CodeScanMatcher_next) 143 | * [`CodeScanner_first`](#CodeScanner_first) 144 | * [`ref`](#ref) 145 | #### Estimated stack usage 146 | 4 cells 147 | 148 | 149 | 150 | ### `CodeScanAddSwitchTarget`: 151 | 152 | 153 | #### Syntax 154 | 155 | 156 | ```pawn 157 | CodeScanAddSwitchTarget(cip, stk, hea, jumpTargets[], num) 158 | ``` 159 | 160 | 161 | #### Parameters 162 | 163 | 164 | | **Name** | **Info** | 165 | | --- | --- | 166 | | `cip` | | 167 | | `stk` | | 168 | | `hea` | | 169 | | `jumpTargets` | ` [172] ` | 170 | | `num` | | 171 | 172 | #### Depends on 173 | * [`AMX_HDR_COD`](#AMX_HDR_COD) 174 | * [`AMX_HDR_DAT`](#AMX_HDR_DAT) 175 | * [`CodeScanner_jump_heap`](#CodeScanner_jump_heap) 176 | * [`CodeScanner_jump_stack`](#CodeScanner_jump_stack) 177 | * [`CodeScanner_jump_switch`](#CodeScanner_jump_switch) 178 | * [`CodeScanner_jump_target`](#CodeScanner_jump_target) 179 | * [`CodeScanner_minn`](#CodeScanner_minn) 180 | * [`cellbytes`](#cellbytes) 181 | * [`cellmin`](#cellmin) 182 | * [`gAmxAssemblyAddress_`](#gAmxAssemblyAddress_) 183 | * [`gBase`](#gBase) 184 | * [`gHdr`](#gHdr) 185 | * [`gOP_CASETBL`](#gOP_CASETBL) 186 | * [`min`](#min) 187 | #### Estimated stack usage 188 | 8 cells 189 | 190 | 191 | 192 | ### `CodeScanCheck`: 193 | 194 | 195 | #### Syntax 196 | 197 | 198 | ```pawn 199 | CodeScanCheck(op, dctx[], cs[], matcher__, fctx[], &next) 200 | ``` 201 | 202 | 203 | #### Parameters 204 | 205 | 206 | | **Name** | **Info** | 207 | | --- | --- | 208 | | `op` | `Opcode ` | 209 | | `dctx` | ` [5] ` | 210 | | `cs` | ` [171] ` | 211 | | `matcher__` | | 212 | | `fctx` | ` [172] ` | 213 | | `next` | ` & ` | 214 | 215 | #### Tag 216 | `bool:` 217 | 218 | 219 | #### Depends on 220 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 221 | * [`CodeScanMatch_hole_count`](#CodeScanMatch_hole_count) 222 | * [`CodeScanMatch_holes`](#CodeScanMatch_holes) 223 | * [`CodeScanMatch_size`](#CodeScanMatch_size) 224 | * [`CodeScanMatcher_code`](#CodeScanMatcher_code) 225 | * [`CodeScanMatcher_holeidx`](#CodeScanMatcher_holeidx) 226 | * [`CodeScanMatcher_holes`](#CodeScanMatcher_holes) 227 | * [`CodeScanMatcher_len`](#CodeScanMatcher_len) 228 | * [`CodeScanMatcher_next`](#CodeScanMatcher_next) 229 | * [`CodeScanMatcher_offset`](#CodeScanMatcher_offset) 230 | * [`CodeScanMatcher_start`](#CodeScanMatcher_start) 231 | * [`CodeScanMatcher_type`](#CodeScanMatcher_type) 232 | * [`DisasmContext_cip`](#DisasmContext_cip) 233 | * [`DisasmContext_nip`](#DisasmContext_nip) 234 | * [`DisasmGetOperandReloc`](#DisasmGetOperandReloc) 235 | * [`FIXES_memcpy`](#FIXES_memcpy) 236 | * [`OP_CASETBL`](#OP_CASETBL) 237 | * [`cellbytes`](#cellbytes) 238 | * [`false`](#false) 239 | * [`gAMXOpcodeParameterCounts`](#gAMXOpcodeParameterCounts) 240 | * [`gAmxAssemblyAddress_`](#gAmxAssemblyAddress_) 241 | * [`true`](#true) 242 | #### Estimated stack usage 243 | 13 cells 244 | 245 | 246 | 247 | ### `CodeScanCheckJumpTarget`: 248 | 249 | 250 | #### Syntax 251 | 252 | 253 | ```pawn 254 | CodeScanCheckJumpTarget(cip, deloc, &stk, &hea, jumpTargets[], num) 255 | ``` 256 | 257 | 258 | #### Parameters 259 | 260 | 261 | | **Name** | **Info** | 262 | | --- | --- | 263 | | `cip` | | 264 | | `deloc` | | 265 | | `stk` | ` & ` | 266 | | `hea` | ` & ` | 267 | | `jumpTargets` | ` [172] ` | 268 | | `num` | | 269 | 270 | #### Tag 271 | `bool:` 272 | 273 | 274 | #### Depends on 275 | * [`CodeScanner_jump_heap`](#CodeScanner_jump_heap) 276 | * [`CodeScanner_jump_stack`](#CodeScanner_jump_stack) 277 | * [`CodeScanner_jump_switch`](#CodeScanner_jump_switch) 278 | * [`CodeScanner_jump_target`](#CodeScanner_jump_target) 279 | * [`CodeScanner_minn`](#CodeScanner_minn) 280 | * [`cellbytes`](#cellbytes) 281 | * [`cellmin`](#cellmin) 282 | * [`false`](#false) 283 | * [`gAmxAssemblyAddress_`](#gAmxAssemblyAddress_) 284 | * [`true`](#true) 285 | #### Estimated stack usage 286 | 4 cells 287 | 288 | 289 | 290 | ### `CodeScanClone`: 291 | 292 | 293 | #### Syntax 294 | 295 | 296 | ```pawn 297 | CodeScanClone(dest[], src[]) 298 | ``` 299 | 300 | 301 | #### Parameters 302 | 303 | 304 | | **Name** | **Info** | 305 | | --- | --- | 306 | | `dest` | ` [172] ` | 307 | | `src` | ` [172] ` | 308 | 309 | #### Depends on 310 | * [`CodeScanner_first`](#CodeScanner_first) 311 | #### Estimated stack usage 312 | 1 cells 313 | 314 | 315 | 316 | ### `CodeScanFindOneFastPattern2`: 317 | 318 | 319 | #### Syntax 320 | 321 | 322 | ```pawn 323 | CodeScanFindOneFastPattern2(matcher[], matcher__, &addr) 324 | ``` 325 | 326 | 327 | #### Parameters 328 | 329 | 330 | | **Name** | **Info** | 331 | | --- | --- | 332 | | `matcher` | ` [171] ` | 333 | | `matcher__` | | 334 | | `addr` | ` & ` | 335 | 336 | #### Tag 337 | `bool:` 338 | 339 | 340 | #### Depends on 341 | * [`CodeScanFindOneFastPattern3`](#CodeScanFindOneFastPattern3) 342 | * [`CodeScanMatcher_code`](#CodeScanMatcher_code) 343 | * [`CodeScanMatcher_len`](#CodeScanMatcher_len) 344 | * [`CodeScanMatcher_next`](#CodeScanMatcher_next) 345 | * [`CodeScanMatcher_type`](#CodeScanMatcher_type) 346 | * [`OP_CALL`](#OP_CALL) 347 | * [`false`](#false) 348 | * [`gFakeMatcher`](#gFakeMatcher) 349 | * [`true`](#true) 350 | #### Estimated stack usage 351 | 10 cells 352 | 353 | 354 | 355 | ### `CodeScanFindOneFastPattern3`: 356 | 357 | 358 | #### Syntax 359 | 360 | 361 | ```pawn 362 | CodeScanFindOneFastPattern3(matcher[], matcher__, addr, &cur) 363 | ``` 364 | 365 | 366 | #### Parameters 367 | 368 | 369 | | **Name** | **Info** | 370 | | --- | --- | 371 | | `matcher` | ` [171] ` | 372 | | `matcher__` | | 373 | | `addr` | | 374 | | `cur` | ` & ` | 375 | 376 | #### Tag 377 | `bool:` 378 | 379 | 380 | #### Depends on 381 | * [`CodeScanMatcher_code`](#CodeScanMatcher_code) 382 | * [`CodeScanMatcher_len`](#CodeScanMatcher_len) 383 | * [`CodeScanMatcher_next`](#CodeScanMatcher_next) 384 | * [`CodeScanMatcher_type`](#CodeScanMatcher_type) 385 | * [`OP_CALL`](#OP_CALL) 386 | * [`false`](#false) 387 | * [`true`](#true) 388 | #### Estimated stack usage 389 | 3 cells 390 | 391 | 392 | 393 | ### `CodeScanGetFuncName`: 394 | 395 | 396 | #### Syntax 397 | 398 | 399 | ```pawn 400 | CodeScanGetFuncName(addr, name[]) 401 | ``` 402 | 403 | 404 | #### Parameters 405 | 406 | 407 | | **Name** | **Info** | 408 | | --- | --- | 409 | | `addr` | | 410 | | `name` | ` [] ` | 411 | 412 | #### Tag 413 | `bool:` 414 | 415 | 416 | #### Depends on 417 | * [`GetPublicIndexFromAddress`](#GetPublicIndexFromAddress) 418 | * [`GetPublicNameFromIndex`](#GetPublicNameFromIndex) 419 | * [`false`](#false) 420 | * [`true`](#true) 421 | #### Estimated stack usage 422 | 7 cells 423 | 424 | 425 | 426 | ### `CodeScanGetFunctionAsm`: 427 | 428 | 429 | #### Syntax 430 | 431 | 432 | ```pawn 433 | CodeScanGetFunctionAsm(csm[], ctx[], offset) 434 | ``` 435 | 436 | 437 | #### Parameters 438 | 439 | 440 | | **Name** | **Info** | 441 | | --- | --- | 442 | | `csm` | ` [172] ` | 443 | | `ctx` | ` [21] ` | 444 | | `offset` | | 445 | 446 | #### Depends on 447 | * [`AsmInitPtr`](#AsmInitPtr) 448 | * [`CodeScanMatch_func`](#CodeScanMatch_func) 449 | * [`cellmax`](#cellmax) 450 | #### Estimated stack usage 451 | 6 cells 452 | 453 | 454 | 455 | ### `CodeScanGetFunctionDisasm`: 456 | 457 | 458 | #### Syntax 459 | 460 | 461 | ```pawn 462 | CodeScanGetFunctionDisasm(csm[], ctx[], offset) 463 | ``` 464 | 465 | 466 | #### Parameters 467 | 468 | 469 | | **Name** | **Info** | 470 | | --- | --- | 471 | | `csm` | ` [172] ` | 472 | | `ctx` | ` [5] ` | 473 | | `offset` | | 474 | 475 | #### Depends on 476 | * [`CodeScanMatch_func`](#CodeScanMatch_func) 477 | * [`DisasmContext_cip`](#DisasmContext_cip) 478 | * [`DisasmContext_end_ip`](#DisasmContext_end_ip) 479 | * [`DisasmContext_nip`](#DisasmContext_nip) 480 | * [`DisasmContext_start_ip`](#DisasmContext_start_ip) 481 | #### Estimated stack usage 482 | 1 cells 483 | 484 | 485 | 486 | ### `CodeScanGetFunctionScanner`: 487 | 488 | 489 | #### Syntax 490 | 491 | 492 | ```pawn 493 | CodeScanGetFunctionScanner(csm[], ret[], ctx[]) 494 | ``` 495 | 496 | 497 | #### Parameters 498 | 499 | 500 | | **Name** | **Info** | 501 | | --- | --- | 502 | | `csm` | ` [172] ` | 503 | | `ret` | ` [172] ` | 504 | | `ctx` | ` [5] ` | 505 | 506 | #### Depends on 507 | * [`CodeScanInit`](#CodeScanInit) 508 | * [`CodeScanMatch_func`](#CodeScanMatch_func) 509 | * [`CodeScanMatch_type`](#CodeScanMatch_type) 510 | * [`CodeScanner_state`](#CodeScanner_state) 511 | * [`DisasmContext_cip`](#DisasmContext_cip) 512 | * [`DisasmContext_end_ip`](#DisasmContext_end_ip) 513 | * [`DisasmContext_nip`](#DisasmContext_nip) 514 | * [`DisasmContext_start_ip`](#DisasmContext_start_ip) 515 | #### Estimated stack usage 516 | 4 cells 517 | 518 | 519 | 520 | ### `CodeScanGetHoleCount`: 521 | 522 | 523 | #### Syntax 524 | 525 | 526 | ```pawn 527 | CodeScanGetHoleCount(csm[]) 528 | ``` 529 | 530 | 531 | #### Parameters 532 | 533 | 534 | | **Name** | **Info** | 535 | | --- | --- | 536 | | `csm` | ` [172] ` | 537 | 538 | #### Depends on 539 | * [`CodeScanMatch_hole_count`](#CodeScanMatch_hole_count) 540 | #### Estimated stack usage 541 | 1 cells 542 | 543 | 544 | 545 | ### `CodeScanGetMatchAddress`: 546 | 547 | 548 | #### Syntax 549 | 550 | 551 | ```pawn 552 | CodeScanGetMatchAddress(csm[]) 553 | ``` 554 | 555 | 556 | #### Parameters 557 | 558 | 559 | | **Name** | **Info** | 560 | | --- | --- | 561 | | `csm` | ` [172] ` | 562 | 563 | #### Depends on 564 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 565 | * [`gDat`](#gDat) 566 | #### Estimated stack usage 567 | 1 cells 568 | 569 | 570 | 571 | ### `CodeScanGetMatchAddressData`: 572 | 573 | 574 | #### Syntax 575 | 576 | 577 | ```pawn 578 | CodeScanGetMatchAddressData(csm[]) 579 | ``` 580 | 581 | 582 | #### Parameters 583 | 584 | 585 | | **Name** | **Info** | 586 | | --- | --- | 587 | | `csm` | ` [172] ` | 588 | 589 | #### Depends on 590 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 591 | #### Estimated stack usage 592 | 1 cells 593 | 594 | 595 | 596 | ### `CodeScanGetMatchAsm`: 597 | 598 | 599 | #### Syntax 600 | 601 | 602 | ```pawn 603 | CodeScanGetMatchAsm(csm[], ctx[], offset) 604 | ``` 605 | 606 | 607 | #### Parameters 608 | 609 | 610 | | **Name** | **Info** | 611 | | --- | --- | 612 | | `csm` | ` [172] ` | 613 | | `ctx` | ` [21] ` | 614 | | `offset` | | 615 | 616 | #### Depends on 617 | * [`AsmInitPtr`](#AsmInitPtr) 618 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 619 | * [`cellmax`](#cellmax) 620 | #### Estimated stack usage 621 | 6 cells 622 | 623 | 624 | 625 | ### `CodeScanGetMatchDisasm`: 626 | 627 | 628 | #### Syntax 629 | 630 | 631 | ```pawn 632 | CodeScanGetMatchDisasm(csm[], ctx[], offset) 633 | ``` 634 | 635 | 636 | #### Parameters 637 | 638 | 639 | | **Name** | **Info** | 640 | | --- | --- | 641 | | `csm` | ` [172] ` | 642 | | `ctx` | ` [5] ` | 643 | | `offset` | | 644 | 645 | #### Depends on 646 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 647 | * [`DisasmContext_cip`](#DisasmContext_cip) 648 | * [`DisasmContext_end_ip`](#DisasmContext_end_ip) 649 | * [`DisasmContext_nip`](#DisasmContext_nip) 650 | * [`DisasmContext_start_ip`](#DisasmContext_start_ip) 651 | #### Estimated stack usage 652 | 1 cells 653 | 654 | 655 | 656 | ### `CodeScanGetMatchFunc`: 657 | 658 | 659 | #### Syntax 660 | 661 | 662 | ```pawn 663 | CodeScanGetMatchFunc(csm[]) 664 | ``` 665 | 666 | 667 | #### Parameters 668 | 669 | 670 | | **Name** | **Info** | 671 | | --- | --- | 672 | | `csm` | ` [172] ` | 673 | 674 | #### Depends on 675 | * [`CodeScanMatch_func`](#CodeScanMatch_func) 676 | * [`gDat`](#gDat) 677 | #### Estimated stack usage 678 | 1 cells 679 | 680 | 681 | 682 | ### `CodeScanGetMatchFuncData`: 683 | 684 | 685 | #### Syntax 686 | 687 | 688 | ```pawn 689 | CodeScanGetMatchFuncData(csm[]) 690 | ``` 691 | 692 | 693 | #### Parameters 694 | 695 | 696 | | **Name** | **Info** | 697 | | --- | --- | 698 | | `csm` | ` [172] ` | 699 | 700 | #### Depends on 701 | * [`CodeScanMatch_func`](#CodeScanMatch_func) 702 | #### Estimated stack usage 703 | 1 cells 704 | 705 | 706 | 707 | ### `CodeScanGetMatchHeap`: 708 | 709 | 710 | #### Syntax 711 | 712 | 713 | ```pawn 714 | CodeScanGetMatchHeap(csm[]) 715 | ``` 716 | 717 | 718 | #### Parameters 719 | 720 | 721 | | **Name** | **Info** | 722 | | --- | --- | 723 | | `csm` | ` [172] ` | 724 | 725 | #### Depends on 726 | * [`CodeScanMatch_heap`](#CodeScanMatch_heap) 727 | #### Estimated stack usage 728 | 1 cells 729 | 730 | 731 | 732 | ### `CodeScanGetMatchHole`: 733 | 734 | 735 | #### Syntax 736 | 737 | 738 | ```pawn 739 | CodeScanGetMatchHole(csm[], idx) 740 | ``` 741 | 742 | 743 | #### Parameters 744 | 745 | 746 | | **Name** | **Info** | 747 | | --- | --- | 748 | | `csm` | ` [172] ` | 749 | | `idx` | | 750 | 751 | #### Depends on 752 | * [`CodeScanMatch_holes`](#CodeScanMatch_holes) 753 | #### Estimated stack usage 754 | 1 cells 755 | 756 | 757 | 758 | ### `CodeScanGetMatchLength`: 759 | 760 | 761 | #### Syntax 762 | 763 | 764 | ```pawn 765 | CodeScanGetMatchLength(csm[]) 766 | ``` 767 | 768 | 769 | #### Parameters 770 | 771 | 772 | | **Name** | **Info** | 773 | | --- | --- | 774 | | `csm` | ` [172] ` | 775 | 776 | #### Depends on 777 | * [`CodeScanMatch_size`](#CodeScanMatch_size) 778 | #### Estimated stack usage 779 | 1 cells 780 | 781 | 782 | 783 | ### `CodeScanGetMatchName`: 784 | 785 | 786 | #### Syntax 787 | 788 | 789 | ```pawn 790 | CodeScanGetMatchName(csm[], name[]) 791 | ``` 792 | 793 | 794 | #### Parameters 795 | 796 | 797 | | **Name** | **Info** | 798 | | --- | --- | 799 | | `csm` | ` [172] ` | 800 | | `name` | ` [] ` | 801 | 802 | #### Depends on 803 | * [`CodeScanGetMatchType`](#CodeScanGetMatchType) 804 | * [`CodeScanMatch_name`](#CodeScanMatch_name) 805 | * [`CodeScanMatch_type`](#CodeScanMatch_type) 806 | * [`strcat`](#strcat) 807 | #### Estimated stack usage 808 | 5 cells 809 | 810 | 811 | 812 | ### `CodeScanGetMatchScanner`: 813 | 814 | 815 | #### Syntax 816 | 817 | 818 | ```pawn 819 | CodeScanGetMatchScanner(csm[], ret[], ctx[], accurate) 820 | ``` 821 | 822 | 823 | #### Parameters 824 | 825 | 826 | | **Name** | **Info** | 827 | | --- | --- | 828 | | `csm` | ` [172] ` | 829 | | `ret` | ` [172] ` | 830 | | `ctx` | ` [5] ` | 831 | | `accurate` | `bool ` | 832 | 833 | #### Depends on 834 | * [`CodeScanGetFunctionScanner`](#CodeScanGetFunctionScanner) 835 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 836 | * [`CodeScanStepInternal`](#CodeScanStepInternal) 837 | * [`CodeScanner_param`](#CodeScanner_param) 838 | * [`CodeScanner_state`](#CodeScanner_state) 839 | * [`DisasmContext_cip`](#DisasmContext_cip) 840 | * [`DisasmContext_nip`](#DisasmContext_nip) 841 | * [`DisasmContext_start_ip`](#DisasmContext_start_ip) 842 | * [`cellmin`](#cellmin) 843 | #### Estimated stack usage 844 | 8 cells 845 | 846 | 847 | 848 | ### `CodeScanGetMatchStack`: 849 | 850 | 851 | #### Syntax 852 | 853 | 854 | ```pawn 855 | CodeScanGetMatchStack(csm[]) 856 | ``` 857 | 858 | 859 | #### Parameters 860 | 861 | 862 | | **Name** | **Info** | 863 | | --- | --- | 864 | | `csm` | ` [172] ` | 865 | 866 | #### Depends on 867 | * [`CodeScanMatch_stack`](#CodeScanMatch_stack) 868 | #### Estimated stack usage 869 | 1 cells 870 | 871 | 872 | 873 | ### `CodeScanGetMatchType`: 874 | 875 | 876 | #### Syntax 877 | 878 | 879 | ```pawn 880 | CodeScanGetMatchType(csm[]) 881 | ``` 882 | 883 | 884 | #### Parameters 885 | 886 | 887 | | **Name** | **Info** | 888 | | --- | --- | 889 | | `csm` | ` [172] ` | 890 | 891 | #### Depends on 892 | * [`CodeScanGetFuncName`](#CodeScanGetFuncName) 893 | * [`CodeScanMatch_func`](#CodeScanMatch_func) 894 | * [`CodeScanMatch_name`](#CodeScanMatch_name) 895 | * [`CodeScanMatch_type`](#CodeScanMatch_type) 896 | #### Estimated stack usage 897 | 5 cells 898 | 899 | 900 | 901 | ### `CodeScanInit`: 902 | 903 | 904 | #### Syntax 905 | 906 | 907 | ```pawn 908 | CodeScanInit(scanner[]) 909 | ``` 910 | 911 | 912 | #### Parameters 913 | 914 | 915 | | **Name** | **Info** | 916 | | --- | --- | 917 | | `scanner` | ` [172] ` | 918 | 919 | #### Depends on 920 | * [`AMX_HDR_COD`](#AMX_HDR_COD) 921 | * [`AMX_HDR_DAT`](#AMX_HDR_DAT) 922 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 923 | * [`CodeScanMatch_heap`](#CodeScanMatch_heap) 924 | * [`CodeScanMatch_name`](#CodeScanMatch_name) 925 | * [`CodeScanMatch_params`](#CodeScanMatch_params) 926 | * [`CodeScanMatch_stack`](#CodeScanMatch_stack) 927 | * [`CodeScanMatch_type`](#CodeScanMatch_type) 928 | * [`CodeScanResetJumpTargets`](#CodeScanResetJumpTargets) 929 | * [`CodeScanner_first`](#CodeScanner_first) 930 | * [`CodeScanner_param`](#CodeScanner_param) 931 | * [`CodeScanner_state`](#CodeScanner_state) 932 | * [`GetAmxBaseAddress`](#GetAmxBaseAddress) 933 | * [`GetAmxHeader`](#GetAmxHeader) 934 | * [`OP_CASETBL`](#OP_CASETBL) 935 | * [`OP_NOP`](#OP_NOP) 936 | * [`RelocateOpcode`](#RelocateOpcode) 937 | * [`cellmin`](#cellmin) 938 | * [`gBase`](#gBase) 939 | * [`gCodBase`](#gCodBase) 940 | * [`gDat`](#gDat) 941 | * [`gHdr`](#gHdr) 942 | * [`gOP_CASETBL`](#gOP_CASETBL) 943 | * [`gOP_NOP`](#gOP_NOP) 944 | #### Estimated stack usage 945 | 5 cells 946 | 947 | 948 | 949 | ### `CodeScanMatcherData`: 950 | 951 | 952 | #### Syntax 953 | 954 | 955 | ```pawn 956 | CodeScanMatcherData(searcher[], val) 957 | ``` 958 | 959 | 960 | #### Parameters 961 | 962 | 963 | | **Name** | **Info** | 964 | | --- | --- | 965 | | `searcher` | ` [171] ` | 966 | | `val` | | 967 | 968 | #### Depends on 969 | * [`CodeScanMatcher_flags`](#CodeScanMatcher_flags) 970 | * [`CodeScanMatcher_user_data`](#CodeScanMatcher_user_data) 971 | #### Estimated stack usage 972 | 1 cells 973 | 974 | 975 | 976 | ### `CodeScanMatcherInit_`: 977 | 978 | 979 | #### Syntax 980 | 981 | 982 | ```pawn 983 | CodeScanMatcherInit_(searcher[], address, flags) 984 | ``` 985 | 986 | 987 | #### Parameters 988 | 989 | 990 | | **Name** | **Info** | 991 | | --- | --- | 992 | | `searcher` | ` [171] ` | 993 | | `address` | | 994 | | `flags` | | 995 | 996 | #### Depends on 997 | * [`CodeScanMatcher_flags`](#CodeScanMatcher_flags) 998 | * [`CodeScanMatcher_func`](#CodeScanMatcher_func) 999 | * [`CodeScanMatcher_holeidx`](#CodeScanMatcher_holeidx) 1000 | * [`CodeScanMatcher_len`](#CodeScanMatcher_len) 1001 | * [`CodeScanMatcher_next`](#CodeScanMatcher_next) 1002 | * [`CodeScanMatcher_offset`](#CodeScanMatcher_offset) 1003 | * [`CodeScanMatcher_user_data`](#CodeScanMatcher_user_data) 1004 | * [`IsOpcodeRelocationRequired`](#IsOpcodeRelocationRequired) 1005 | * [`OP_NOP`](#OP_NOP) 1006 | * [`RelocateOpcode`](#RelocateOpcode) 1007 | * [`gRelocateRequired`](#gRelocateRequired) 1008 | * [`printf`](#printf) 1009 | #### Estimated stack usage 1010 | 4 cells 1011 | 1012 | 1013 | 1014 | ### `CodeScanMatcherPattern_`: 1015 | 1016 | 1017 | #### Syntax 1018 | 1019 | 1020 | ```pawn 1021 | CodeScanMatcherPattern_(searcher[], ...) 1022 | ``` 1023 | 1024 | 1025 | #### Parameters 1026 | 1027 | 1028 | | **Name** | **Info** | 1029 | | --- | --- | 1030 | | `searcher` | ` [171] ` | 1031 | | `...` | ` {Opcode,Float,_} ` | 1032 | 1033 | #### Depends on 1034 | * [`CodeScanMatcher_code`](#CodeScanMatcher_code) 1035 | * [`CodeScanMatcher_len`](#CodeScanMatcher_len) 1036 | * [`CodeScanMatcher_type`](#CodeScanMatcher_type) 1037 | * [`__COMPILER_CELL_SHIFT`](#__COMPILER_CELL_SHIFT) 1038 | * [`__args_offset`](#__args_offset) 1039 | * [`gAMXOpcodeParameterCounts`](#gAMXOpcodeParameterCounts) 1040 | * [`getarg`](#getarg) 1041 | #### Estimated stack usage 1042 | 11 cells 1043 | 1044 | 1045 | 1046 | ### `CodeScanNOPMatch`: 1047 | 1048 | 1049 | #### Syntax 1050 | 1051 | 1052 | ```pawn 1053 | CodeScanNOPMatch(csm[]) 1054 | ``` 1055 | 1056 | 1057 | #### Parameters 1058 | 1059 | 1060 | | **Name** | **Info** | 1061 | | --- | --- | 1062 | | `csm` | ` [172] ` | 1063 | 1064 | #### Depends on 1065 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 1066 | * [`CodeScanMatch_size`](#CodeScanMatch_size) 1067 | * [`cellbytes`](#cellbytes) 1068 | * [`gOP_NOP`](#gOP_NOP) 1069 | #### Estimated stack usage 1070 | 3 cells 1071 | 1072 | 1073 | 1074 | ### `CodeScanReset`: 1075 | 1076 | 1077 | #### Syntax 1078 | 1079 | 1080 | ```pawn 1081 | CodeScanReset(cs[], matcher__, &next) 1082 | ``` 1083 | 1084 | 1085 | #### Parameters 1086 | 1087 | 1088 | | **Name** | **Info** | 1089 | | --- | --- | 1090 | | `cs` | ` [171] ` | 1091 | | `matcher__` | | 1092 | | `next` | ` & ` | 1093 | 1094 | #### Depends on 1095 | * [`CodeScanMatcher_func`](#CodeScanMatcher_func) 1096 | * [`CodeScanMatcher_holeidx`](#CodeScanMatcher_holeidx) 1097 | * [`CodeScanMatcher_len`](#CodeScanMatcher_len) 1098 | * [`CodeScanMatcher_next`](#CodeScanMatcher_next) 1099 | * [`CodeScanMatcher_offset`](#CodeScanMatcher_offset) 1100 | #### Estimated stack usage 1101 | 1 cells 1102 | 1103 | 1104 | 1105 | ### `CodeScanResetJumpTargets`: 1106 | 1107 | 1108 | #### Syntax 1109 | 1110 | 1111 | ```pawn 1112 | CodeScanResetJumpTargets(jumpTargets[], num) 1113 | ``` 1114 | 1115 | 1116 | #### Parameters 1117 | 1118 | 1119 | | **Name** | **Info** | 1120 | | --- | --- | 1121 | | `jumpTargets` | ` [172] ` | 1122 | | `num` | | 1123 | 1124 | #### Depends on 1125 | * [`CodeScanner_jump_target`](#CodeScanner_jump_target) 1126 | * [`CodeScanner_minn`](#CodeScanner_minn) 1127 | * [`cellmin`](#cellmin) 1128 | #### Estimated stack usage 1129 | 1 cells 1130 | 1131 | 1132 | 1133 | ### `CodeScanRun`: 1134 | 1135 | 1136 | #### Syntax 1137 | 1138 | 1139 | ```pawn 1140 | CodeScanRun(csState[]) 1141 | ``` 1142 | 1143 | 1144 | #### Parameters 1145 | 1146 | 1147 | | **Name** | **Info** | 1148 | | --- | --- | 1149 | | `csState` | ` [172] ` | 1150 | 1151 | #### Tag 1152 | `bool:` 1153 | 1154 | 1155 | #### Depends on 1156 | * [`CodeScanCall`](#CodeScanCall) 1157 | * [`CodeScanCheck`](#CodeScanCheck) 1158 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 1159 | * [`CodeScanMatch_func`](#CodeScanMatch_func) 1160 | * [`CodeScanReset`](#CodeScanReset) 1161 | * [`CodeScanStepInternal`](#CodeScanStepInternal) 1162 | * [`CodeScanner_first`](#CodeScanner_first) 1163 | * [`DisasmContext`](#DisasmContext) 1164 | * [`DisasmContext_cip`](#DisasmContext_cip) 1165 | * [`DisasmContext_end_ip`](#DisasmContext_end_ip) 1166 | * [`DisasmContext_nip`](#DisasmContext_nip) 1167 | * [`DisasmContext_opcode`](#DisasmContext_opcode) 1168 | * [`DisasmContext_start_ip`](#DisasmContext_start_ip) 1169 | * [`OP_NONE`](#OP_NONE) 1170 | * [`cellmin`](#cellmin) 1171 | * [`gFakeMatcher`](#gFakeMatcher) 1172 | * [`true`](#true) 1173 | #### Estimated stack usage 1174 | 18 cells 1175 | 1176 | 1177 | 1178 | ### `CodeScanRunFast`: 1179 | 1180 | 1181 | #### Syntax 1182 | 1183 | 1184 | ```pawn 1185 | CodeScanRunFast(csState[], searchFuncAddr) 1186 | ``` 1187 | 1188 | 1189 | #### Parameters 1190 | 1191 | 1192 | | **Name** | **Info** | 1193 | | --- | --- | 1194 | | `csState` | ` [172] ` | 1195 | | `searchFuncAddr` | | 1196 | 1197 | #### Tag 1198 | `bool:` 1199 | 1200 | 1201 | #### Depends on 1202 | * [`CodeScanCall`](#CodeScanCall) 1203 | * [`CodeScanCheck`](#CodeScanCheck) 1204 | * [`CodeScanFindOneFastPattern2`](#CodeScanFindOneFastPattern2) 1205 | * [`CodeScanMatch_cip`](#CodeScanMatch_cip) 1206 | * [`CodeScanReset`](#CodeScanReset) 1207 | * [`CodeScanRun`](#CodeScanRun) 1208 | * [`CodeScanRunFastPrescanLocated`](#CodeScanRunFastPrescanLocated) 1209 | * [`CodeScanRunFastPrescanRelocate`](#CodeScanRunFastPrescanRelocate) 1210 | * [`CodeScanStepInternal`](#CodeScanStepInternal) 1211 | * [`CodeScanner_first`](#CodeScanner_first) 1212 | * [`DisasmContext`](#DisasmContext) 1213 | * [`DisasmContext_cip`](#DisasmContext_cip) 1214 | * [`DisasmContext_end_ip`](#DisasmContext_end_ip) 1215 | * [`DisasmContext_nip`](#DisasmContext_nip) 1216 | * [`DisasmContext_opcode`](#DisasmContext_opcode) 1217 | * [`DisasmContext_start_ip`](#DisasmContext_start_ip) 1218 | * [`DisasmGetOpcode`](#DisasmGetOpcode) 1219 | * [`OP_NONE`](#OP_NONE) 1220 | * [`OP_PROC`](#OP_PROC) 1221 | * [`cellbytes`](#cellbytes) 1222 | * [`cellmin`](#cellmin) 1223 | * [`false`](#false) 1224 | * [`gDat`](#gDat) 1225 | * [`gFakeMatcher`](#gFakeMatcher) 1226 | * [`gRelocateRequired`](#gRelocateRequired) 1227 | * [`print`](#print) 1228 | * [`true`](#true) 1229 | #### Estimated stack usage 1230 | 21 cells 1231 | 1232 | 1233 | 1234 | ### `CodeScanRunFastPrescanLocated`: 1235 | 1236 | 1237 | #### Syntax 1238 | 1239 | 1240 | ```pawn 1241 | CodeScanRunFastPrescanLocated(&proc, &nextaddr, searchFuncAddr) 1242 | ``` 1243 | 1244 | 1245 | #### Parameters 1246 | 1247 | 1248 | | **Name** | **Info** | 1249 | | --- | --- | 1250 | | `proc` | ` & ` | 1251 | | `nextaddr` | ` & ` | 1252 | | `searchFuncAddr` | | 1253 | 1254 | #### Depends on 1255 | * [`OP_CALL`](#OP_CALL) 1256 | * [`OP_CASETBL`](#OP_CASETBL) 1257 | * [`OP_NONE`](#OP_NONE) 1258 | * [`OP_PROC`](#OP_PROC) 1259 | * [`cellbytes`](#cellbytes) 1260 | * [`cellmin`](#cellmin) 1261 | * [`false`](#false) 1262 | * [`gAMXOpcodeBaseSizes`](#gAMXOpcodeBaseSizes) 1263 | * [`gAmxAssemblyAddress_`](#gAmxAssemblyAddress_) 1264 | * [`gCodBase`](#gCodBase) 1265 | * [`true`](#true) 1266 | #### Estimated stack usage 1267 | 3 cells 1268 | 1269 | 1270 | 1271 | ### `CodeScanRunFastPrescanRelocate`: 1272 | 1273 | 1274 | #### Syntax 1275 | 1276 | 1277 | ```pawn 1278 | CodeScanRunFastPrescanRelocate(&proc, &nextaddr, searchFuncAddr) 1279 | ``` 1280 | 1281 | 1282 | #### Parameters 1283 | 1284 | 1285 | | **Name** | **Info** | 1286 | | --- | --- | 1287 | | `proc` | ` & ` | 1288 | | `nextaddr` | ` & ` | 1289 | | `searchFuncAddr` | | 1290 | 1291 | #### Depends on 1292 | * [`OP_CALL`](#OP_CALL) 1293 | * [`OP_CASETBL`](#OP_CASETBL) 1294 | * [`OP_NONE`](#OP_NONE) 1295 | * [`OP_PROC`](#OP_PROC) 1296 | * [`UnrelocateOpcode`](#UnrelocateOpcode) 1297 | * [`cellbytes`](#cellbytes) 1298 | * [`cellmin`](#cellmin) 1299 | * [`false`](#false) 1300 | * [`gAMXOpcodeBaseSizes`](#gAMXOpcodeBaseSizes) 1301 | * [`gAmxAssemblyAddress_`](#gAmxAssemblyAddress_) 1302 | * [`gCodBase`](#gCodBase) 1303 | * [`true`](#true) 1304 | #### Estimated stack usage 1305 | 6 cells 1306 | 1307 | 1308 | 1309 | ### `CodeScanStep`: 1310 | 1311 | 1312 | #### Syntax 1313 | 1314 | 1315 | ```pawn 1316 | CodeScanStep(dctx[], csState[]) 1317 | ``` 1318 | 1319 | 1320 | #### Parameters 1321 | 1322 | 1323 | | **Name** | **Info** | 1324 | | --- | --- | 1325 | | `dctx` | ` [5] ` | 1326 | | `csState` | ` [172] ` | 1327 | 1328 | #### Tag 1329 | `bool:` 1330 | 1331 | 1332 | #### Depends on 1333 | * [`CodeScanStepInternal`](#CodeScanStepInternal) 1334 | * [`CodeScanner_param`](#CodeScanner_param) 1335 | * [`CodeScanner_state`](#CodeScanner_state) 1336 | * [`cellmin`](#cellmin) 1337 | #### Estimated stack usage 1338 | 8 cells 1339 | 1340 | 1341 | 1342 | ### `CodeScanStepInternal`: 1343 | 1344 | 1345 | #### Syntax 1346 | 1347 | 1348 | ```pawn 1349 | CodeScanStepInternal(dctx[], csState[], &parseState, &parseParam) 1350 | ``` 1351 | 1352 | 1353 | #### Parameters 1354 | 1355 | 1356 | | **Name** | **Info** | 1357 | | --- | --- | 1358 | | `dctx` | ` [5] ` | 1359 | | `csState` | ` [172] ` | 1360 | | `parseState` | ` & ` | 1361 | | `parseParam` | ` & ` | 1362 | 1363 | #### Tag 1364 | `bool:` 1365 | 1366 | 1367 | #### Depends on 1368 | * [`CodeScanAddJumpTarget`](#CodeScanAddJumpTarget) 1369 | * [`CodeScanAddSwitchTarget`](#CodeScanAddSwitchTarget) 1370 | * [`CodeScanCheckJumpTarget`](#CodeScanCheckJumpTarget) 1371 | * [`CodeScanMatch_func`](#CodeScanMatch_func) 1372 | * [`CodeScanMatch_heap`](#CodeScanMatch_heap) 1373 | * [`CodeScanMatch_stack`](#CodeScanMatch_stack) 1374 | * [`CodeScanMatch_type`](#CodeScanMatch_type) 1375 | * [`CodeScanResetJumpTargets`](#CodeScanResetJumpTargets) 1376 | * [`DisasmContext_cip`](#DisasmContext_cip) 1377 | * [`DisasmContext_end_ip`](#DisasmContext_end_ip) 1378 | * [`DisasmContext_nip`](#DisasmContext_nip) 1379 | * [`DisasmContext_opcode`](#DisasmContext_opcode) 1380 | * [`NUM_OPCODES`](#NUM_OPCODES) 1381 | * [`OP_ADD_C`](#OP_ADD_C) 1382 | * [`OP_BOUNDS`](#OP_BOUNDS) 1383 | * [`OP_BREAK`](#OP_BREAK) 1384 | * [`OP_CALL`](#OP_CALL) 1385 | * [`OP_CALL_PRI`](#OP_CALL_PRI) 1386 | * [`OP_CASETBL`](#OP_CASETBL) 1387 | * [`OP_HALT`](#OP_HALT) 1388 | * [`OP_HEAP`](#OP_HEAP) 1389 | * [`OP_JEQ`](#OP_JEQ) 1390 | * [`OP_JGEQ`](#OP_JGEQ) 1391 | * [`OP_JGRTR`](#OP_JGRTR) 1392 | * [`OP_JLEQ`](#OP_JLEQ) 1393 | * [`OP_JLESS`](#OP_JLESS) 1394 | * [`OP_JNEQ`](#OP_JNEQ) 1395 | * [`OP_JNZ`](#OP_JNZ) 1396 | * [`OP_JREL`](#OP_JREL) 1397 | * [`OP_JSGEQ`](#OP_JSGEQ) 1398 | * [`OP_JSGRTR`](#OP_JSGRTR) 1399 | * [`OP_JSLEQ`](#OP_JSLEQ) 1400 | * [`OP_JSLESS`](#OP_JSLESS) 1401 | * [`OP_JUMP`](#OP_JUMP) 1402 | * [`OP_JZER`](#OP_JZER) 1403 | * [`OP_LCTRL`](#OP_LCTRL) 1404 | * [`OP_LOAD_PRI`](#OP_LOAD_PRI) 1405 | * [`OP_NONE`](#OP_NONE) 1406 | * [`OP_NOP`](#OP_NOP) 1407 | * [`OP_POP_ALT`](#OP_POP_ALT) 1408 | * [`OP_POP_PRI`](#OP_POP_PRI) 1409 | * [`OP_PROC`](#OP_PROC) 1410 | * [`OP_PUSH`](#OP_PUSH) 1411 | * [`OP_PUSH_ADR`](#OP_PUSH_ADR) 1412 | * [`OP_PUSH_ALT`](#OP_PUSH_ALT) 1413 | * [`OP_PUSH_C`](#OP_PUSH_C) 1414 | * [`OP_PUSH_PRI`](#OP_PUSH_PRI) 1415 | * [`OP_PUSH_R`](#OP_PUSH_R) 1416 | * [`OP_PUSH_S`](#OP_PUSH_S) 1417 | * [`OP_RETN`](#OP_RETN) 1418 | * [`OP_SCTRL`](#OP_SCTRL) 1419 | * [`OP_STACK`](#OP_STACK) 1420 | * [`OP_SWITCH`](#OP_SWITCH) 1421 | * [`UnsafeUnrelocateOpcode`](#UnsafeUnrelocateOpcode) 1422 | * [`cellbytes`](#cellbytes) 1423 | * [`cellmin`](#cellmin) 1424 | * [`false`](#false) 1425 | * [`gAMXOpcodeBaseSizes`](#gAMXOpcodeBaseSizes) 1426 | * [`gAmxAssemblyAddress_`](#gAmxAssemblyAddress_) 1427 | * [`gBase`](#gBase) 1428 | * [`true`](#true) 1429 | #### Estimated stack usage 1430 | 13 cells 1431 | 1432 | -------------------------------------------------------------------------------- /disasm.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined DISASM_INC 22 | #endinput 23 | #endif 24 | #define DISASM_INC 25 | 26 | /** 27 | * 28 | * 29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include 40 | #include 41 | 42 | #include "amx_base" 43 | #include "amx_header" 44 | #include "amx_memory" 45 | #include "opcode" 46 | 47 | #define DISASM_MAX_PUBLIC_NAME 64 48 | #define DISASM_MAX_NATIVE_NAME 100 49 | 50 | ///

51 | 52 | /// amx_assembly disasm 53 | enum DisasmContext { 54 | DisasmContext_start_ip, 55 | DisasmContext_end_ip, 56 | DisasmContext_nip, 57 | DisasmContext_cip, 58 | Opcode:DisasmContext_opcode 59 | } 60 | 61 | /// amx_assembly disasm 62 | const DisasmContext:DisasmContext__ = DisasmContext; 63 | 64 | ///

65 | 66 | /// amx_assembly disasm 67 | enum DisasmResult { 68 | DISASM_DONE = 0, 69 | DISASM_NOP = 1, 70 | DISASM_OK = 2 71 | } 72 | 73 | /// amx_assembly disasm 74 | const DisasmResult:DisasmResult__ = DisasmResult; 75 | 76 | /// amx_assembly disasm 77 | static stock gCodBase = 0; 78 | 79 | /// amx_assembly disasm 80 | stock DisasmInit(ctx[DisasmContext], start = 0, end = 0) { 81 | new hdr[AMX_HDR]; 82 | GetAmxHeader(hdr); 83 | RelocateOpcode(OP_NOP); 84 | 85 | gCodBase = GetAmxBaseAddress() + hdr[AMX_HDR_COD]; 86 | 87 | new dat = hdr[AMX_HDR_DAT]; 88 | new cod = hdr[AMX_HDR_COD]; 89 | 90 | new code_base = cod - dat; 91 | 92 | start += code_base; 93 | 94 | ctx[DisasmContext_nip] = start; 95 | ctx[DisasmContext_cip] = start; 96 | 97 | ctx[DisasmContext_start_ip] = start; 98 | if (end != 0) { 99 | ctx[DisasmContext_end_ip] = code_base + end; 100 | } else { 101 | // code_base + (dat - cod) 102 | // = (cod - dat) + (dat - cod) 103 | // = cod - dat + dat - cod 104 | // = cod - cod + dat - dat 105 | // = 0 106 | ctx[DisasmContext_end_ip] = 0; 107 | } 108 | } 109 | 110 | /// amx_assembly disasm 111 | stock bool:DisasmDecodeInsn(ctx[DisasmContext]) { 112 | new ip = ctx[DisasmContext_nip]; 113 | if (ip >= 0) { 114 | return false; 115 | } 116 | 117 | new Opcode:opcode = UnsafeUnrelocateOpcode(Opcode:ReadAmxMemory(ip)); 118 | if (opcode <= OP_NONE || _:opcode >= NUM_OPCODES) { 119 | return false; 120 | } 121 | 122 | ctx[DisasmContext_cip] = ip, 123 | ctx[DisasmContext_opcode] = opcode; 124 | 125 | if (opcode == OP_CASETBL) { 126 | ip += cellbytes, 127 | ip += ReadAmxMemory(ip) * (2 * cellbytes); 128 | } 129 | 130 | return 131 | ctx[DisasmContext_nip] = ip + gAMXOpcodeBaseSizes[_:opcode], 132 | true; 133 | } 134 | 135 | /// amx_assembly disasm 136 | stock DisasmResult:DisasmNext(ctx[DisasmContext]) { 137 | // This function is just a slightly more consistent wrapper around 138 | // "DisasmDecodeInsn". I wanted to change that function, but that would 139 | // cause breaking changes. This returns non-zero while there is still data 140 | // being read, even if that data is not a valid opcode. For invalid code, 141 | // it skips the current cell and returns an invalid opcode. It also now 142 | // checks the end point correctly. 143 | if (ctx[DisasmContext_nip] >= ctx[DisasmContext_end_ip]) { 144 | return DISASM_DONE; 145 | } else if (DisasmDecodeInsn(ctx)) { 146 | return DISASM_OK; 147 | } else { 148 | ctx[DisasmContext_cip] = ctx[DisasmContext_nip], 149 | ctx[DisasmContext_nip] += cellbytes, 150 | ctx[DisasmContext_opcode] = Opcode:NUM_OPCODES; 151 | return DISASM_NOP; 152 | } 153 | } 154 | 155 | /// amx_assembly disasm 156 | stock Opcode:DisasmNextInsn(ctx[DisasmContext]) { 157 | if (DisasmDecodeInsn(ctx)) { 158 | return ctx[DisasmContext_opcode]; 159 | } 160 | return OP_NONE; 161 | } 162 | 163 | /// amx_assembly disasm 164 | stock Opcode:DisasmGetOpcode(const ctx[DisasmContext]) { 165 | return ctx[DisasmContext_opcode]; 166 | } 167 | 168 | /// amx_assembly disasm 169 | stock DisasmGetOperand(const ctx[DisasmContext], index = 0) { 170 | return ReadAmxMemory(ctx[DisasmContext_cip] + (index + 1) * cellbytes); 171 | } 172 | 173 | /// amx_assembly disasm 174 | stock DisasmGetNumOperands(const ctx[DisasmContext]) { 175 | new Opcode:opcode = ctx[DisasmContext_opcode]; 176 | 177 | if (opcode == OP_CASETBL) { 178 | return ReadAmxMemory(ctx[DisasmContext_cip] + cellbytes); 179 | } else { 180 | return gAMXOpcodeParameterCounts[_:opcode]; 181 | } 182 | } 183 | 184 | /// amx_assembly disasm 185 | stock bool:DisasmNeedReloc(const ctx[DisasmContext]) { 186 | return HasReloc() && GetOpcodeInstructionRelocatable(ctx[DisasmContext_opcode]); 187 | } 188 | 189 | /// amx_assembly disasm 190 | stock DisasmReloc(addr) { 191 | return addr - gCodBase; 192 | } 193 | 194 | /// amx_assembly disasm 195 | stock DisasmGetNextIp(const ctx[DisasmContext]) { 196 | return ctx[DisasmContext_nip]; 197 | } 198 | 199 | /// amx_assembly disasm 200 | stock DisasmGetCurIp(const ctx[DisasmContext]) { 201 | return ctx[DisasmContext_cip]; 202 | } 203 | 204 | /// amx_assembly disasm 205 | stock DisasmGetRemaining(const ctx[DisasmContext]) { 206 | return ctx[DisasmContext_end_ip] - ctx[DisasmContext_nip]; 207 | } 208 | 209 | /// amx_assembly disasm 210 | stock DisasmGetInsnName(const ctx[DisasmContext], name[], size = sizeof(name)) { 211 | name[0] = '\0'; 212 | strcat(name, GetOpcodeInstructionName(ctx[DisasmContext_opcode]), size); 213 | } 214 | 215 | /// amx_assembly disasm 216 | stock DisasmGetOperandReloc(const ctx[DisasmContext], index = 0) { 217 | new param = ReadAmxMemory(ctx[DisasmContext_cip] + (index + 1) * cellbytes); 218 | new Opcode:op = ctx[DisasmContext_opcode]; 219 | // Needs special code for dealing with "CASETBL", which has multiple 220 | // parameters - not all of them to be relocated. If the opcode is NOT that, 221 | // then check if it is any other opcode requiring relocation. This does 222 | // result in the odd pattern of having a triadic operator in a conditional, 223 | // but the alternative would be: 224 | // 225 | // if (ctx[DisasmContext_opcode == OP_CASETBL) { 226 | // if (index & 1) { 227 | // return param - base; 228 | // } 229 | // } else if (DisasmNeedReloc(ctx)) { 230 | // return param - base; 231 | // } 232 | // return param; 233 | // 234 | // Or: 235 | // 236 | // if ((ctx[DisasmContext_opcode == OP_CASETBL && (index & 1)) || 237 | // (ctx[DisasmContext_opcode != OP_CASETBL && DisasmNeedReloc(ctx))) { 238 | // return param - base; 239 | // } else { 240 | // return param; 241 | // } 242 | // 243 | // I think the conditional ends up nicer in this rare case. 244 | if ((op == OP_CASETBL) ? (index & 1) : _:gAMXOpcodeNeedsReloc[_:op]) { 245 | return param - gCodBase; 246 | } else { 247 | return param; 248 | } 249 | } 250 | 251 | /// amx_assembly disasm 252 | static stock ToHexStr(x) { 253 | new s[11]; 254 | new i = 0; 255 | new j = 0; 256 | 257 | while (i < sizeof(s) && j < cellbytes * 2) { 258 | new n = x >> (cellbytes * 2 - 1 - j) * 4 & 0xF; 259 | switch (n) { 260 | case 0x0..0x9: 261 | s[i] = n + '0'; 262 | case 0xA..0xF: 263 | s[i] = n + 'a' - 0xA; 264 | } 265 | i++; 266 | j++; 267 | } 268 | 269 | return s; 270 | } 271 | 272 | /// amx_assembly disasm 273 | static stock bool:IsPrintableAscii(c) { 274 | return 32 <= c <= 126; 275 | } 276 | 277 | /// amx_assembly disasm 278 | static stock ToPrintableAscii(c) { 279 | return IsPrintableAscii(c) ? c : ' '; 280 | } 281 | 282 | /// amx_assembly disasm 283 | stock DisasmWriteCode(File:file) { 284 | new ctx[DisasmContext]; 285 | DisasmInit(ctx); 286 | 287 | new hdr[AMX_HDR]; 288 | GetAmxHeader(hdr); 289 | 290 | new dat = hdr[AMX_HDR_DAT]; 291 | new cod = hdr[AMX_HDR_COD]; 292 | 293 | fwrite(file, "; CODE\n\n"); 294 | 295 | while (DisasmGetNextIp(ctx) < ctx[DisasmContext_end_ip]) 296 | { 297 | if (!DisasmDecodeInsn(ctx)) { 298 | new cip = DisasmGetNextIp(ctx); 299 | ctx[DisasmContext_nip] += cellbytes; 300 | fwrite(file, ToHexStr(cip + dat - cod)); 301 | fwrite(file, " ???? "); 302 | fwrite(file, ToHexStr(ReadAmxMemory(cip))); 303 | fwrite(file, "\n"); 304 | continue; 305 | } 306 | 307 | new cip = DisasmGetCurIp(ctx); 308 | new Opcode:opcode = DisasmGetOpcode(ctx); 309 | 310 | if (opcode == OP_PROC) { 311 | fwrite(file, "\n"); 312 | } 313 | 314 | new insn_name[OPCODE_MAX_INSN_NAME]; 315 | DisasmGetInsnName(ctx, insn_name); 316 | fwrite(file, ToHexStr(cip + dat - cod)); 317 | fwrite(file, " "); 318 | fwrite(file, insn_name); 319 | fwrite(file, " "); 320 | 321 | switch (opcode) { 322 | case OP_PROC: { 323 | new name[DISASM_MAX_PUBLIC_NAME]; 324 | new address = cip + dat - cod; 325 | if (address == hdr[AMX_HDR_CIP]) { 326 | strcat(name, "main"); 327 | } else { 328 | new index = GetPublicIndexFromAddress(address); 329 | if (index >= 0) { 330 | GetPublicNameFromIndex(index, name); 331 | } 332 | } 333 | if (strlen(name) != 0) { 334 | fwrite(file, "; "); 335 | fwrite(file, name); 336 | } 337 | } 338 | case OP_CASETBL: { 339 | new num = DisasmGetNumOperands(ctx); 340 | fwrite(file, ToHexStr(num)); 341 | fwrite(file, " "); 342 | new rel_addr = DisasmGetOperand(ctx, 1) - gCodBase; 343 | fwrite(file, ToHexStr(rel_addr)); 344 | for (new i = 1; i <= num; i++) { 345 | fwrite(file, "\n case "); 346 | new val = DisasmGetOperand(ctx, i * 2); 347 | fwrite(file, ToHexStr(val)); 348 | fwrite(file, " "); 349 | rel_addr = DisasmGetOperand(ctx, i * 2 + 1) - gCodBase; 350 | fwrite(file, ToHexStr(rel_addr)); 351 | } 352 | } 353 | case OP_CALL: { 354 | new name[DISASM_MAX_PUBLIC_NAME]; 355 | new address = DisasmGetOperand(ctx) - gCodBase; 356 | if (address == hdr[AMX_HDR_CIP]) { 357 | strcat(name, "main"); 358 | } else { 359 | new index = GetPublicIndexFromAddress(address); 360 | if (index >= 0) { 361 | GetPublicNameFromIndex(index, name); 362 | } 363 | } 364 | fwrite(file, ToHexStr(address)); 365 | if (strlen(name) > 0) { 366 | fwrite(file, "; "); 367 | fwrite(file, name); 368 | } 369 | } 370 | case OP_SYSREQ_C, OP_SYSREQ_D: { 371 | new name[DISASM_MAX_NATIVE_NAME]; 372 | new address = DisasmGetOperand(ctx); 373 | if (opcode == OP_SYSREQ_C) { 374 | new index = DisasmGetOperand(ctx); 375 | GetNativeNameFromIndex(index, name); 376 | } else { 377 | new index = GetNativeIndexFromAddress(address); 378 | if (index >= 0) { 379 | GetNativeNameFromIndex(index, name); 380 | } 381 | } 382 | fwrite(file, ToHexStr(address)); 383 | if (strlen(name) > 0) { 384 | fwrite(file, "; "); 385 | fwrite(file, name); 386 | } 387 | } 388 | #if OPCODE_HAS_O2 389 | case OP_SYSREQ_N: { 390 | // sysreq.n 391 | new name[DISASM_MAX_NATIVE_NAME]; 392 | new address = DisasmGetOperand(ctx); 393 | new index = GetNativeIndexFromAddress(address); 394 | if (index >= 0) { 395 | GetNativeNameFromIndex(index, name); 396 | } 397 | fwrite(file, ToHexStr(address)); 398 | fwrite(file, " "); 399 | fwrite(file, ToHexStr(DisasmGetOperand(ctx, 1))); 400 | if (strlen(name) > 0) { 401 | fwrite(file, "; "); 402 | fwrite(file, name); 403 | } 404 | } 405 | #endif 406 | default: { 407 | new n = DisasmGetNumOperands(ctx); 408 | for (new i = 0; i < n; i++) { 409 | new operand = DisasmGetOperandReloc(ctx, i); 410 | fwrite(file, ToHexStr(operand)); 411 | fwrite(file, " "); 412 | } 413 | } 414 | } 415 | 416 | fwrite(file, "\n"); 417 | } 418 | } 419 | 420 | /// amx_assembly disasm 421 | stock DisasmWriteDataRowChar(File:file, start, num, max) { 422 | new cur = start; 423 | new end = start + num * cellbytes; 424 | 425 | while (cur < max) { 426 | new p[cellbytes char + 1]; 427 | p[0] = ReadAmxMemory(cur); 428 | 429 | new u[cellbytes + 1]; 430 | u[0] = ToPrintableAscii(p{0}); 431 | u[1] = ToPrintableAscii(p{1}); 432 | u[2] = ToPrintableAscii(p{2}); 433 | u[3] = ToPrintableAscii(p{3}); 434 | #if cellbits == 32 435 | u[4] = '\0'; 436 | #elseif cellbits == 64 437 | u[4] = ToPrintableAscii(p{4}); 438 | u[5] = ToPrintableAscii(p{5}); 439 | u[6] = ToPrintableAscii(p{6}); 440 | u[7] = ToPrintableAscii(p{7}); 441 | u[8] = '\0'; 442 | #else 443 | #error Unsupported `cellbits`. 444 | #endif 445 | 446 | if (cur < end) { 447 | fwrite(file, u); 448 | } else { 449 | fwrite(file, " "); 450 | } 451 | cur += cellbytes; 452 | } 453 | } 454 | 455 | /// amx_assembly disasm 456 | stock DisasmWriteDataRowHex(File:file, start, num, max) { 457 | new cur = start; 458 | new end = start + num * cellbytes; 459 | 460 | while (cur < max) { 461 | if (cur < end) { 462 | fwrite(file, ToHexStr(ReadAmxMemory(cur))); 463 | } else { 464 | fwrite(file, " "); 465 | } 466 | fwrite(file, " "); 467 | cur += cellbytes; 468 | } 469 | } 470 | 471 | /// amx_assembly disasm 472 | stock DisasmWriteData(File:file) { 473 | fwrite(file, "\n\n; DATA\n"); 474 | 475 | new hdr[AMX_HDR]; 476 | GetAmxHeader(hdr); 477 | 478 | new dat = hdr[AMX_HDR_DAT]; 479 | new hea = hdr[AMX_HDR_HEA]; 480 | new data_end = hea - dat; 481 | 482 | for (new i = 0; i < data_end; i += 4 * cellbytes) { 483 | fwrite(file, ToHexStr(i)); 484 | fwrite(file, " "); 485 | DisasmWriteDataRowHex(file, i, 4, min(i + 4 * cellbytes, data_end)); 486 | fwrite(file, " "); 487 | DisasmWriteDataRowChar(file, i, 4, min(i + 4 * cellbytes, data_end)); 488 | fwrite(file, "\n"); 489 | } 490 | } 491 | 492 | /// amx_assembly disasm 493 | stock DisasmWriteFile(File:file) { 494 | DisasmWriteCode(file); 495 | DisasmWriteData(file); 496 | } 497 | 498 | /// amx_assembly disasm 499 | stock bool:DisasmWrite(const filename[]) { 500 | new File:file = fopen(filename, io_write); 501 | if (file) { 502 | DisasmWriteFile(file); 503 | fclose(file); 504 | return true; 505 | } 506 | return false; 507 | } 508 | 509 | /// amx_assembly disasm 510 | stock DisasmDump(const filename[]) { 511 | DisasmWrite(filename); 512 | } 513 | 514 | -------------------------------------------------------------------------------- /dynamic_call.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined DYNAMIC_CALL_INC 22 | #endinput 23 | #endif 24 | #define DYNAMIC_CALL_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include "amx_jit" 40 | #include "amx_header" 41 | #include "amx_memory" 42 | #include "opcode" 43 | #include "addressof" 44 | 45 | #if !defined DYNAMIC_CALL_MAX_ARGS 46 | #define DYNAMIC_CALL_MAX_ARGS 256 47 | #endif 48 | 49 | forward bool:Push(arg); 50 | forward bool:PushString(const string[]); 51 | forward bool:Pop(&arg = 0); 52 | forward Call(address, bool:auto_pop = true); 53 | forward SysreqC(index, bool:auto_pop = true); 54 | forward SysreqD(address, bool:auto_pop = true); 55 | forward CallN(address, args_to_push, bool:auto_pop = true); 56 | forward SysreqCN(index, args_to_push, bool:auto_pop = true); 57 | forward SysreqDN(address, args_to_push, bool:auto_pop = true); 58 | forward CallFunction(address, {Float,_}:...); 59 | forward CallNative(index, {Float,_}:...); 60 | forward CallNativeByAddress(address, {Float,_}:...); 61 | 62 | /// amx_assembly dynamic_call 63 | /// 64 | /// When you declare an alternative version of a function like this you end up 65 | /// with two copies in the header. As long as we never actually use this 66 | /// function we can keep changing the pointer to whatever we want and call the 67 | /// native via `SYSREQ.C`. Note that if we do this we need to check if 68 | /// `SYSREQ.D` is still in the server because the `SYSREQ.C` will be replaced. 69 | /// 70 | native AmxSysreqDStub() = heapspace; 71 | 72 | /// amx_assembly dynamic_call 73 | static stock Opcode:UpdateSysreqDAndGetIndex(&address, wide) { 74 | // This function handily ensures that the function is in the header on old 75 | // compilers which don't insert it when used via `SYSREQ.C`. 76 | static index = cellmin; 77 | static Opcode:sysreq_c = OP_NONE; 78 | static Opcode:sysreq_d = OP_NONE; 79 | static off = 0; 80 | if (index == cellmin) { 81 | sysreq_d = RelocateOpcode(OP_SYSREQ_D); 82 | // `nativeidxof` is a bypass for the fact that the header may be 83 | // clobbered, which this new `SYSREQ.D` bypass is also for. 84 | index = nativeidxof(AmxSysreqDStub<>); 85 | sysreq_c = RelocateOpcode(OP_SYSREQ_C); 86 | new amxhdr[AMX_HDR]; 87 | GetAmxHeader(amxhdr); 88 | off = amxhdr[AMX_HDR_NATIVES] - amxhdr[AMX_HDR_DAT] + index * amxhdr[AMX_HDR_DEFSIZE]; 89 | } 90 | if (wide == 0 && HasSysreqD()) { 91 | return sysreq_d; 92 | } 93 | // Change where the native points. 94 | WriteAmxMemory(off, address); 95 | if (!HasReloc()) { 96 | WriteAmxMemory(off + cellbytes, wide); 97 | } 98 | address = index; 99 | return sysreq_c; 100 | } 101 | 102 | /// amx_assembly dynamic_call 103 | static stock g_nargs = 0; 104 | 105 | /// amx_assembly dynamic_call 106 | static stock g_args[DYNAMIC_CALL_MAX_ARGS]; 107 | 108 | /// amx_assembly dynamic_call 109 | stock bool:Push(arg) { 110 | if (g_nargs < sizeof(g_args)) { 111 | g_args[g_nargs++] = arg; 112 | return true; 113 | } 114 | return false; 115 | } 116 | 117 | /// amx_assembly dynamic_call 118 | stock bool:PushString(const string[]) { 119 | new address = 0; 120 | #emit load.s.pri string 121 | #emit stor.s.pri address 122 | return Push(address); 123 | } 124 | 125 | /// amx_assembly dynamic_call 126 | stock bool:Pop(&arg = 0) { 127 | if (g_nargs > 0) { 128 | arg = g_args[--g_nargs]; 129 | return true; 130 | } 131 | return false; 132 | } 133 | 134 | /// amx_assembly dynamic_call 135 | stock Call(address, bool:auto_pop = true) { 136 | const cells0 = 9 * cellbytes; 137 | new arg = 0; 138 | new index = g_nargs; 139 | new bytes = g_nargs * cellbytes; 140 | new retval = 0; 141 | 142 | while (--index >= 0) { 143 | arg = g_args[index]; 144 | #emit push.s arg 145 | } 146 | 147 | #emit load.s.pri bytes 148 | #emit push.pri 149 | 150 | #emit lctrl __cip 151 | #emit add.c cells0 152 | #emit lctrl __jmp 153 | #emit push.pri 154 | #emit load.s.pri address 155 | #emit sctrl __cip 156 | 157 | #emit stor.s.pri retval 158 | 159 | if (auto_pop) { 160 | g_nargs = 0; 161 | } 162 | 163 | return retval; 164 | } 165 | 166 | /// amx_assembly dynamic_call 167 | stock CallN(address, args_to_push, bool:auto_pop = true) { 168 | const cells0 = 9 * cellbytes; 169 | // Like "Call", but doesn't pass all parameters. 170 | new arg = 0; 171 | new index = g_nargs; 172 | new bytes = args_to_push * cellbytes; 173 | new end = g_nargs - args_to_push; 174 | new retval = 0; 175 | 176 | if (end < 0) { 177 | return cellmin; 178 | } 179 | 180 | while (--index >= end) { 181 | arg = g_args[index]; 182 | #emit push.s arg 183 | } 184 | 185 | #emit load.s.pri bytes 186 | #emit push.pri 187 | 188 | #emit lctrl __cip 189 | #emit add.c cells0 190 | #emit lctrl __jmp 191 | #emit push.pri 192 | #emit load.s.pri address 193 | #emit sctrl __cip 194 | 195 | #emit stor.s.pri retval 196 | 197 | if (auto_pop) { 198 | g_nargs = end; 199 | } 200 | 201 | return retval; 202 | } 203 | 204 | /// amx_assembly dynamic_call 205 | stock CallFunction(address, {Float,_}:...) { 206 | const cells_ = 2 * cellbytes; 207 | const cells0 = 1 * cellbytes; 208 | const cells1 = 3 * cellbytes; 209 | const cells2 = 4 * cellbytes; 210 | const cells3 = 9 * cellbytes; 211 | const cells4 = 4 * cellbytes; 212 | new arg_bytes = 0, arg_begin = 0, arg_end = 0; 213 | 214 | // Get number of bytes passed. 215 | #emit load.s.pri cells_ 216 | #emit const.alt cells0 217 | #emit sub 218 | #emit stor.s.pri arg_bytes 219 | #emit move.alt 220 | 221 | // Last argument is at FRM + 0x0C + arg_bytes (which is in ALT). 222 | #emit lctrl __frm 223 | #emit add.c cells1 224 | #emit add 225 | #emit stor.s.pri arg_end 226 | 227 | // Frist argument is at FRM + 0x10. 228 | #emit lctrl __frm 229 | #emit add.c cells2 230 | #emit stor.s.pri arg_begin 231 | 232 | new arg = arg_end; 233 | while (arg >= arg_begin) { 234 | #emit lref.s.pri arg 235 | #emit load.i 236 | #emit push.pri 237 | arg -= cellbytes; 238 | } 239 | 240 | // Call the function 241 | #emit push.s arg_bytes 242 | #emit lctrl __cip 243 | #emit add.c cells3 244 | #emit lctrl __jmp 245 | #emit push.pri 246 | #emit load.s.pri address 247 | #emit sctrl __cip 248 | // Arguments are popped by callee. 249 | 250 | // Pop locals and return. 251 | #emit stack cells4 252 | #emit retn 253 | 254 | return 0; // make compiler happy 255 | } 256 | 257 | /// amx_assembly dynamic_call 258 | stock SysreqC(index, bool:auto_pop = true) { 259 | if (GetJITGeneratorVersion()) { 260 | return cellmin; 261 | } 262 | 263 | const cells0 = 23 * cellbytes; 264 | const cells1 = 1 * cellbytes; 265 | const cells2 = 1 * cellbytes; 266 | new arg = 0; 267 | new i = g_nargs; 268 | new bytes = g_nargs * cellbytes; 269 | new tmp = 0; 270 | new Opcode:sysreq_c = RelocateOpcode(OP_SYSREQ_C); 271 | new retval = 0; 272 | 273 | while (--i >= 0) { 274 | arg = g_args[i]; 275 | #emit push.s arg 276 | } 277 | 278 | #emit load.s.pri bytes 279 | #emit push.pri 280 | 281 | // tmp = cod + cip - dat + 282 | #emit lctrl __cod // COD 283 | #emit move.alt 284 | #emit lctrl __cip // CIP 285 | #emit add 286 | #emit move.alt 287 | #emit lctrl __dat // DAT 288 | #emit sub.alt 289 | #emit add.c cells0 290 | #emit stor.s.pri tmp 291 | 292 | // nop #1 = sysreq.c 293 | #emit load.s.pri sysreq_c 294 | #emit sref.s.pri tmp 295 | 296 | // tmp += 4 297 | #emit load.s.pri tmp 298 | #emit add.c cells1 299 | #emit stor.s.pri tmp 300 | 301 | // nop #2 = index 302 | #emit load.s.pri index 303 | #emit sref.s.pri tmp 304 | 305 | #emit nop 306 | #emit nop 307 | // #emit sysreq.c 0 308 | 309 | #emit stor.s.pri retval 310 | 311 | // Pop native arguments. 312 | #emit lctrl __stk 313 | #emit load.s.alt bytes 314 | #emit add 315 | #emit add.c cells2 316 | #emit sctrl __stk 317 | 318 | if (auto_pop) { 319 | g_nargs = 0; 320 | } 321 | 322 | return retval; 323 | } 324 | 325 | /// amx_assembly dynamic_call 326 | stock SysreqD(address, bool:auto_pop = true) { 327 | if (GetJITGeneratorVersion()) { 328 | return cellmin; 329 | } 330 | 331 | const cells0 = 23 * cellbytes; 332 | const cells1 = 1 * cellbytes; 333 | const cells2 = 1 * cellbytes; 334 | new arg = 0; 335 | new i = g_nargs; 336 | new bytes = g_nargs * cellbytes; 337 | new tmp = 0; 338 | new Opcode:sysreq = UpdateSysreqDAndGetIndex(address, 0); 339 | new retval = 0; 340 | 341 | while (--i >= 0) { 342 | arg = g_args[i]; 343 | #emit push.s arg 344 | } 345 | 346 | #emit load.s.pri bytes 347 | #emit push.pri 348 | 349 | // tmp = cod + cip - dat + 350 | #emit lctrl __cod // COD 351 | #emit move.alt 352 | #emit lctrl __cip // CIP 353 | #emit add 354 | #emit move.alt 355 | #emit lctrl __dat // DAT 356 | #emit sub.alt 357 | #emit add.c cells0 358 | #emit stor.s.pri tmp 359 | 360 | // nop #1 = sysreq.d 361 | #emit load.s.pri sysreq 362 | #emit sref.s.pri tmp 363 | 364 | // tmp += 4 365 | #emit load.s.pri tmp 366 | #emit add.c cells1 367 | #emit stor.s.pri tmp 368 | 369 | // nop #2 = address 370 | #emit load.s.pri address 371 | #emit sref.s.pri tmp 372 | 373 | #emit nop 374 | #emit nop 375 | 376 | #emit stor.s.pri retval 377 | 378 | // Pop native arguments. 379 | #emit lctrl __stk 380 | #emit load.s.alt bytes 381 | #emit add 382 | #emit add.c cells2 383 | #emit sctrl __stk 384 | 385 | if (auto_pop) { 386 | g_nargs = 0; 387 | } 388 | 389 | return retval; 390 | } 391 | 392 | /// amx_assembly dynamic_call 393 | stock SysreqDWide(address, wide, bool:auto_pop = true) { 394 | if (GetJITGeneratorVersion()) { 395 | return cellmin; 396 | } 397 | 398 | const cells0 = 23 * cellbytes; 399 | const cells1 = 1 * cellbytes; 400 | const cells2 = 1 * cellbytes; 401 | new arg = 0; 402 | new i = g_nargs; 403 | new bytes = g_nargs * cellbytes; 404 | new tmp = 0; 405 | new Opcode:sysreq = UpdateSysreqDAndGetIndex(address, wide); 406 | new retval = 0; 407 | 408 | while (--i >= 0) { 409 | arg = g_args[i]; 410 | #emit push.s arg 411 | } 412 | 413 | #emit load.s.pri bytes 414 | #emit push.pri 415 | 416 | // tmp = cod + cip - dat + 417 | #emit lctrl __cod // COD 418 | #emit move.alt 419 | #emit lctrl __cip // CIP 420 | #emit add 421 | #emit move.alt 422 | #emit lctrl __dat // DAT 423 | #emit sub.alt 424 | #emit add.c cells0 425 | #emit stor.s.pri tmp 426 | 427 | // nop #1 = sysreq.d 428 | #emit load.s.pri sysreq 429 | #emit sref.s.pri tmp 430 | 431 | // tmp += 4 432 | #emit load.s.pri tmp 433 | #emit add.c cells1 434 | #emit stor.s.pri tmp 435 | 436 | // nop #2 = address 437 | #emit load.s.pri address 438 | #emit sref.s.pri tmp 439 | 440 | #emit nop 441 | #emit nop 442 | 443 | #emit stor.s.pri retval 444 | 445 | // Pop native arguments. 446 | #emit lctrl __stk 447 | #emit load.s.alt bytes 448 | #emit add 449 | #emit add.c cells2 450 | #emit sctrl __stk 451 | 452 | if (auto_pop) { 453 | g_nargs = 0; 454 | } 455 | 456 | return retval; 457 | } 458 | 459 | /// amx_assembly dynamic_call 460 | stock SysreqCN(index, args_to_push, bool:auto_pop = true) { 461 | if (GetJITGeneratorVersion()) { 462 | return cellmin; 463 | } 464 | 465 | const cells0 = 23 * cellbytes; 466 | const cells1 = 1 * cellbytes; 467 | const cells2 = 1 * cellbytes; 468 | new arg = 0; 469 | new i = g_nargs; 470 | new bytes = args_to_push * cellbytes; 471 | new tmp = 0; 472 | new Opcode:sysreq_c = RelocateOpcode(OP_SYSREQ_C); 473 | new end = g_nargs - args_to_push; 474 | new retval = 0; 475 | 476 | if (end < 0) { 477 | return cellmin; 478 | } 479 | 480 | while (--i >= end) { 481 | arg = g_args[i]; 482 | #emit push.s arg 483 | } 484 | #emit load.s.pri bytes 485 | #emit push.pri 486 | 487 | // tmp = cod + cip - dat + 488 | #emit lctrl __cod // COD 489 | #emit move.alt 490 | #emit lctrl __cip // CIP 491 | #emit add 492 | #emit move.alt 493 | #emit lctrl __dat // DAT 494 | #emit sub.alt 495 | #emit add.c cells0 496 | #emit stor.s.pri tmp 497 | 498 | // nop #1 = sysreq.c 499 | #emit load.s.pri sysreq_c 500 | #emit sref.s.pri tmp 501 | 502 | // tmp += 4 503 | #emit load.s.pri tmp 504 | #emit add.c cells1 505 | #emit stor.s.pri tmp 506 | 507 | // nop #2 = index 508 | #emit load.s.pri index 509 | #emit sref.s.pri tmp 510 | 511 | #emit nop 512 | #emit nop 513 | 514 | #emit stor.s.pri retval 515 | 516 | // Pop native arguments. 517 | #emit lctrl __stk 518 | #emit load.s.alt bytes 519 | #emit add 520 | #emit add.c cells2 521 | #emit sctrl __stk 522 | 523 | if (auto_pop) { 524 | g_nargs = end; 525 | } 526 | 527 | return retval; 528 | } 529 | 530 | /// amx_assembly dynamic_call 531 | stock SysreqDN(address, args_to_push, bool:auto_pop = true) { 532 | if (GetJITGeneratorVersion()) { 533 | return cellmin; 534 | } 535 | 536 | const cells0 = 23 * cellbytes; 537 | const cells1 = 1 * cellbytes; 538 | const cells2 = 1 * cellbytes; 539 | new arg = 0; 540 | new i = g_nargs; 541 | new bytes = args_to_push * cellbytes; 542 | new tmp = 0; 543 | new Opcode:sysreq = UpdateSysreqDAndGetIndex(address, 0); 544 | new end = g_nargs - args_to_push; 545 | new retval = 0; 546 | 547 | if (end < 0) { 548 | return cellmin; 549 | } 550 | 551 | while (--i >= end) { 552 | arg = g_args[i]; 553 | #emit push.s arg 554 | } 555 | 556 | #emit load.s.pri bytes 557 | #emit push.pri 558 | 559 | // tmp = cod + cip - dat + 560 | #emit lctrl __cod // COD 561 | #emit move.alt 562 | #emit lctrl __cip // CIP 563 | #emit add 564 | #emit move.alt 565 | #emit lctrl __dat // DAT 566 | #emit sub.alt 567 | #emit add.c cells0 568 | #emit stor.s.pri tmp 569 | 570 | // nop #1 = sysreq.d 571 | #emit load.s.pri sysreq 572 | #emit sref.s.pri tmp 573 | 574 | // tmp += 4 575 | #emit load.s.pri tmp 576 | #emit add.c cells1 577 | #emit stor.s.pri tmp 578 | 579 | // nop #2 = address 580 | #emit load.s.pri address 581 | #emit sref.s.pri tmp 582 | 583 | #emit nop 584 | #emit nop 585 | 586 | #emit stor.s.pri retval 587 | 588 | // Pop native arguments. 589 | #emit lctrl __stk 590 | #emit load.s.alt bytes 591 | #emit add 592 | #emit add.c cells2 593 | #emit sctrl __stk 594 | 595 | if (auto_pop) { 596 | g_nargs = end; 597 | } 598 | 599 | return retval; 600 | } 601 | 602 | /// amx_assembly dynamic_call 603 | stock SysreqDNWide(address, wide, args_to_push, bool:auto_pop = true) { 604 | if (GetJITGeneratorVersion()) { 605 | return cellmin; 606 | } 607 | 608 | const cells0 = 23 * cellbytes; 609 | const cells1 = 1 * cellbytes; 610 | const cells2 = 1 * cellbytes; 611 | new arg = 0; 612 | new i = g_nargs; 613 | new bytes = args_to_push * cellbytes; 614 | new tmp = 0; 615 | new Opcode:sysreq = UpdateSysreqDAndGetIndex(address, wide); 616 | new end = g_nargs - args_to_push; 617 | new retval = 0; 618 | 619 | if (end < 0) { 620 | return cellmin; 621 | } 622 | 623 | while (--i >= end) { 624 | arg = g_args[i]; 625 | #emit push.s arg 626 | } 627 | 628 | #emit load.s.pri bytes 629 | #emit push.pri 630 | 631 | // tmp = cod + cip - dat + 632 | #emit lctrl __cod // COD 633 | #emit move.alt 634 | #emit lctrl __cip // CIP 635 | #emit add 636 | #emit move.alt 637 | #emit lctrl __dat // DAT 638 | #emit sub.alt 639 | #emit add.c cells0 640 | #emit stor.s.pri tmp 641 | 642 | // nop #1 = sysreq.d 643 | #emit load.s.pri sysreq 644 | #emit sref.s.pri tmp 645 | 646 | // tmp += 4 647 | #emit load.s.pri tmp 648 | #emit add.c cells1 649 | #emit stor.s.pri tmp 650 | 651 | // nop #2 = address 652 | #emit load.s.pri address 653 | #emit sref.s.pri tmp 654 | 655 | #emit nop 656 | #emit nop 657 | 658 | #emit stor.s.pri retval 659 | 660 | // Pop native arguments. 661 | #emit lctrl __stk 662 | #emit load.s.alt bytes 663 | #emit add 664 | #emit add.c cells2 665 | #emit sctrl __stk 666 | 667 | if (auto_pop) { 668 | g_nargs = end; 669 | } 670 | 671 | return retval; 672 | } 673 | 674 | /// amx_assembly dynamic_call 675 | stock CallNative(index, {Float,_}:...) { 676 | if (GetJITGeneratorVersion()) { 677 | return cellmin; 678 | } 679 | 680 | const cells0 = -1 * cellbytes; 681 | const cells1 = 3 * cellbytes; 682 | const cells2 = 4 * cellbytes; 683 | const cells3 = 23 * cellbytes; 684 | const cells4 = 1 * cellbytes; 685 | const cells5 = 1 * cellbytes; 686 | new arg_bytes = 0, arg_begin = 0, arg_end = 0; 687 | new Opcode:sysreq_c = RelocateOpcode(OP_SYSREQ_C); 688 | 689 | // Get number of bytes passed. 690 | #emit load.s.pri 0x8 691 | #emit add.c cells0 692 | #emit stor.s.pri arg_bytes 693 | #emit move.alt 694 | 695 | // Last argument is at FRM + 0x0C + arg_bytes (which is in ALT). 696 | #emit lctrl __frm 697 | #emit add.c cells1 698 | #emit add 699 | #emit stor.s.pri arg_end 700 | 701 | // Frist argument is at FRM + 0x10. 702 | #emit lctrl __frm 703 | #emit add.c cells2 704 | #emit stor.s.pri arg_begin 705 | 706 | new arg = arg_end; 707 | new tmp = 0; 708 | new retval = 0; 709 | 710 | while (arg >= arg_begin) { 711 | #emit lref.s.pri arg 712 | #emit load.i 713 | #emit push.pri 714 | arg -= cellbytes; 715 | } 716 | 717 | // Push number of arguments * 4 (which is params[0]). 718 | #emit push.s arg_bytes 719 | 720 | // tmp = cod + cip - dat + 721 | #emit lctrl __cod // COD 722 | #emit move.alt 723 | #emit lctrl __cip // CIP 724 | #emit add 725 | #emit move.alt 726 | #emit lctrl __dat // DAT 727 | #emit sub.alt 728 | #emit add.c cells3 729 | #emit stor.s.pri tmp 730 | 731 | // nop #1 = sysreq.c 732 | #emit load.s.pri sysreq_c 733 | #emit sref.s.pri tmp 734 | 735 | // tmp += 4 736 | #emit load.s.pri tmp 737 | #emit add.c cells4 738 | #emit stor.s.pri tmp 739 | 740 | // nop #2 = index 741 | #emit load.s.pri index 742 | #emit sref.s.pri tmp 743 | 744 | #emit nop 745 | #emit nop 746 | 747 | #emit stor.s.pri retval 748 | 749 | // Pop native arguments. 750 | #emit lctrl __stk 751 | #emit load.s.alt arg_bytes 752 | #emit add 753 | #emit add.c cells5 754 | #emit sctrl __stk 755 | 756 | return retval; 757 | } 758 | 759 | /// amx_assembly dynamic_call 760 | /// 761 | /// Unlike CallNative(), this function calls natives directly via SYSREQ.D. 762 | /// 763 | stock CallNativeByAddress(address, {Float,_}:...) { 764 | if (GetJITGeneratorVersion()) { 765 | return cellmin; 766 | } 767 | 768 | const cells0 = -1 * cellbytes; 769 | const cells1 = 3 * cellbytes; 770 | const cells2 = 4 * cellbytes; 771 | const cells3 = 23 * cellbytes; 772 | const cells4 = 1 * cellbytes; 773 | const cells5 = 1 * cellbytes; 774 | new arg_bytes = 0, arg_begin = 0, arg_end = 0; 775 | new Opcode:sysreq = UpdateSysreqDAndGetIndex(address, 0); 776 | 777 | // Get number of bytes passed. 778 | #emit load.s.pri 0x8 779 | #emit add.c cells0 780 | #emit stor.s.pri arg_bytes 781 | #emit move.alt 782 | 783 | // Last argument is at FRM + 0x0C + arg_bytes (which is in ALT). 784 | #emit lctrl __frm 785 | #emit add.c cells1 786 | #emit add 787 | #emit stor.s.pri arg_end 788 | 789 | // Frist argument is at FRM + 0x10. 790 | #emit lctrl __frm 791 | #emit add.c cells2 792 | #emit stor.s.pri arg_begin 793 | 794 | new arg = arg_end; 795 | new tmp = 0; 796 | new retval = 0; 797 | 798 | while (arg >= arg_begin) { 799 | #emit lref.s.pri arg 800 | #emit load.i 801 | #emit push.pri 802 | arg -= cellbytes; 803 | } 804 | 805 | // Push number of arguments * 4 (which is params[0]). 806 | #emit push.s arg_bytes 807 | 808 | // tmp = cod + cip - dat + 809 | #emit lctrl __cod // COD 810 | #emit move.alt 811 | #emit lctrl __cip // CIP 812 | #emit add 813 | #emit move.alt 814 | #emit lctrl __dat // DAT 815 | #emit sub.alt 816 | #emit add.c cells3 817 | #emit stor.s.pri tmp 818 | 819 | // nop #1 = sysreq.d 820 | #emit load.s.pri sysreq 821 | #emit sref.s.pri tmp 822 | 823 | // tmp += 4 824 | #emit load.s.pri tmp 825 | #emit add.c cells4 826 | #emit stor.s.pri tmp 827 | 828 | // nop #2 = address 829 | #emit load.s.pri address 830 | #emit sref.s.pri tmp 831 | 832 | #emit nop 833 | #emit nop 834 | 835 | #emit stor.s.pri retval 836 | 837 | // Pop native arguments. 838 | #emit lctrl __stk 839 | #emit load.s.alt arg_bytes 840 | #emit add 841 | #emit add.c cells5 842 | #emit sctrl __stk 843 | 844 | return retval; 845 | } 846 | 847 | /// amx_assembly dynamic_call 848 | /// 849 | /// Unlike CallNative(), this function calls natives directly via SYSREQ.D. 850 | /// Takes two pointers to replicate 64-bit pointers. 851 | /// 852 | stock CallNativeByAddressWide(address, wide, {Float,_}:...) { 853 | if (GetJITGeneratorVersion()) { 854 | return cellmin; 855 | } 856 | 857 | const cells0 = -1 * cellbytes; 858 | const cells1 = 3 * cellbytes; 859 | const cells2 = 4 * cellbytes; 860 | const cells3 = 23 * cellbytes; 861 | const cells4 = 1 * cellbytes; 862 | const cells5 = 1 * cellbytes; 863 | new arg_bytes = 0, arg_begin = 0, arg_end = 0; 864 | new Opcode:sysreq = UpdateSysreqDAndGetIndex(address, wide); 865 | 866 | // Get number of bytes passed. 867 | #emit load.s.pri 0x8 868 | #emit add.c cells0 869 | #emit stor.s.pri arg_bytes 870 | #emit move.alt 871 | 872 | // Last argument is at FRM + 0x0C + arg_bytes (which is in ALT). 873 | #emit lctrl __frm 874 | #emit add.c cells1 875 | #emit add 876 | #emit stor.s.pri arg_end 877 | 878 | // Frist argument is at FRM + 0x10. 879 | #emit lctrl __frm 880 | #emit add.c cells2 881 | #emit stor.s.pri arg_begin 882 | 883 | new arg = arg_end; 884 | new tmp = 0; 885 | new retval = 0; 886 | 887 | while (arg >= arg_begin) { 888 | #emit lref.s.pri arg 889 | #emit load.i 890 | #emit push.pri 891 | arg -= cellbytes; 892 | } 893 | 894 | // Push number of arguments * 4 (which is params[0]). 895 | #emit push.s arg_bytes 896 | 897 | // tmp = cod + cip - dat + 898 | #emit lctrl __cod // COD 899 | #emit move.alt 900 | #emit lctrl __cip // CIP 901 | #emit add 902 | #emit move.alt 903 | #emit lctrl __dat // DAT 904 | #emit sub.alt 905 | #emit add.c cells3 906 | #emit stor.s.pri tmp 907 | 908 | // nop #1 = sysreq.d 909 | #emit load.s.pri sysreq 910 | #emit sref.s.pri tmp 911 | 912 | // tmp += 4 913 | #emit load.s.pri tmp 914 | #emit add.c cells4 915 | #emit stor.s.pri tmp 916 | 917 | // nop #2 = address 918 | #emit load.s.pri address 919 | #emit sref.s.pri tmp 920 | 921 | #emit nop 922 | #emit nop 923 | 924 | #emit stor.s.pri retval 925 | 926 | // Pop native arguments. 927 | #emit lctrl __stk 928 | #emit load.s.alt arg_bytes 929 | #emit add 930 | #emit add.c cells5 931 | #emit sctrl __stk 932 | 933 | return retval; 934 | } 935 | 936 | -------------------------------------------------------------------------------- /dynamic_call.md: -------------------------------------------------------------------------------- 1 | amx_assembly dynamic_call 2 | ========================================== 3 | AMX Assembly; Call arbitrary functions by name. 4 | ------------------------------------------ 5 | 6 | 7 | 8 | ## Variables 9 | 10 | 11 | ### `g_args`: 12 | 13 | 14 | ### `g_nargs`: 15 | 16 | 17 | ## Functions 18 | 19 | 20 | ### `Pop`: 21 | 22 | 23 | #### Syntax 24 | 25 | 26 | ```pawn 27 | Pop(&arg) 28 | ``` 29 | 30 | 31 | #### Parameters 32 | 33 | 34 | | **Name** | **Info** | 35 | | --- | --- | 36 | | `arg` | ` & ` | 37 | 38 | #### Tag 39 | `bool:` 40 | 41 | 42 | #### Depends on 43 | * [`false`](#false) 44 | * [`g_args`](#g_args) 45 | * [`g_nargs`](#g_nargs) 46 | * [`true`](#true) 47 | #### Estimated stack usage 48 | 1 cells 49 | 50 | 51 | 52 | ### `Push`: 53 | 54 | 55 | #### Syntax 56 | 57 | 58 | ```pawn 59 | Push(arg) 60 | ``` 61 | 62 | 63 | #### Parameters 64 | 65 | 66 | | **Name** | **Info** | 67 | | --- | --- | 68 | | `arg` | | 69 | 70 | #### Tag 71 | `bool:` 72 | 73 | 74 | #### Depends on 75 | * [`false`](#false) 76 | * [`g_args`](#g_args) 77 | * [`g_nargs`](#g_nargs) 78 | * [`true`](#true) 79 | #### Estimated stack usage 80 | 1 cells 81 | 82 | 83 | 84 | ### `PushString`: 85 | 86 | 87 | #### Syntax 88 | 89 | 90 | ```pawn 91 | PushString(string[]) 92 | ``` 93 | 94 | 95 | #### Parameters 96 | 97 | 98 | | **Name** | **Info** | 99 | | --- | --- | 100 | | `string` | ` [] ` | 101 | 102 | #### Tag 103 | `bool:` 104 | 105 | 106 | #### Depends on 107 | * [`Push`](#Push) 108 | #### Estimated stack usage 109 | 5 cells 110 | 111 | -------------------------------------------------------------------------------- /frame_info.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Y_Less 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined FRAME_INFO_INC 22 | #endinput 23 | #endif 24 | #define FRAME_INFO_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include "amx_header" 40 | #include "amx_base" 41 | #include "opcode" 42 | 43 | /// amx_assembly frame_info 44 | stock GetCurrentFrame() { 45 | #emit load.s.pri 0 46 | #emit retn 47 | 48 | return 0; // make compiler happy 49 | } 50 | 51 | /// amx_assembly frame_info 52 | stock GetFramePreviousFrame(frm_addr) { 53 | #emit lref.s.pri frm_addr 54 | #emit retn 55 | 56 | return 0; // make compiler happy 57 | } 58 | 59 | /// amx_assembly frame_info 60 | stock GetFrameReturn(frm_addr) { 61 | const cells0 = 1 * cellbytes; 62 | #emit load.s.pri frm_addr 63 | #emit add.c cells0 64 | #emit load.i 65 | #emit retn 66 | 67 | return 0; // make compiler happy 68 | } 69 | 70 | /// amx_assembly frame_info 71 | stock SetFramePreviousFrame(frm_addr, addr) { 72 | #emit load.s.pri addr 73 | #emit sref.s.pri frm_addr 74 | #emit retn 75 | } 76 | 77 | /// amx_assembly frame_info 78 | stock SetFrameReturn(frm_addr, addr) { 79 | const cells0 = 1 * cellbytes; 80 | #emit load.s.alt addr 81 | #emit load.s.pri frm_addr 82 | #emit add.c cells0 83 | #emit xchg 84 | #emit stor.i 85 | #emit retn 86 | } 87 | 88 | /// amx_assembly frame_info 89 | stock SetFrameParameterSize(frm_addr, size) { 90 | const cells0 = 2 * cellbytes; 91 | #emit load.s.alt size 92 | #emit load.s.pri frm_addr 93 | #emit add.c cells0 94 | #emit xchg 95 | #emit stor.i 96 | #emit retn 97 | } 98 | 99 | /// amx_assembly frame_info 100 | stock SetFrameParameterCount(frm_addr, count) { 101 | SetFrameParameterSize(frm_addr, count * cellbytes); 102 | } 103 | 104 | /// amx_assembly frame_info 105 | stock GetFrameTotalSize(frm_addr) { 106 | return GetFrameLocalSize(frm_addr) + GetFrameHeaderSize(frm_addr) + GetFrameParameterSize(frm_addr); 107 | } 108 | 109 | /// amx_assembly frame_info 110 | stock GetFrameTotalCount(frm_addr) { 111 | return GetFrameTotalSize(frm_addr) / cellbytes; 112 | } 113 | 114 | /// amx_assembly frame_info 115 | stock GetFrameNextFrame(frm_addr) { 116 | // this function always works because it is at the top of the stack 117 | // run back through the stack 118 | new cur_frm = GetCurrentFrame(); 119 | 120 | while (cur_frm != 0) { 121 | new prev_frm = GetFramePreviousFrame(cur_frm); 122 | if (prev_frm == frm_addr) { 123 | break; 124 | } 125 | cur_frm = prev_frm; 126 | } 127 | 128 | return cur_frm; 129 | } 130 | 131 | /// amx_assembly frame_info 132 | stock GetFrameLocalSize(frm_addr) { 133 | // run back through the stack 134 | new next_frm = GetFrameNextFrame(frm_addr); 135 | 136 | // find the size of local variables in the selected frame 137 | new frm_bottom = next_frm + GetFrameHeaderSize(next_frm) + GetFrameParameterSize(next_frm); 138 | 139 | return frm_addr - frm_bottom; 140 | } 141 | 142 | /// amx_assembly frame_info 143 | stock GetFrameLocalCount(frm_addr) { 144 | return GetFrameLocalSize(frm_addr) / cellbytes; 145 | } 146 | 147 | /// amx_assembly frame_info 148 | stock GetFrameHeaderSize(frm_addr) { 149 | #pragma unused frm_addr 150 | return 3 * cellbytes; 151 | } 152 | 153 | /// amx_assembly frame_info 154 | stock GetFrameHeaderCount(frm_addr) { 155 | return GetFrameHeaderSize(frm_addr) / cellbytes; 156 | } 157 | 158 | /// amx_assembly frame_info 159 | stock GetFrameParameterSize(frm_addr) { 160 | const cells0 = 2 * cellbytes; 161 | #emit load.s.pri frm_addr 162 | #emit add.c cells0 163 | #emit load.i 164 | #emit retn 165 | 166 | return 0; // make compiler happy 167 | } 168 | 169 | /// amx_assembly frame_info 170 | stock GetFrameParameterCount(frm_addr) { 171 | return GetFrameParameterSize(frm_addr) / cellbytes; 172 | } 173 | 174 | /// amx_assembly frame_info 175 | stock GetFrameParameter(frm_addr, param, idx = -1) { 176 | if (idx == -1) { 177 | #if cellbits == 32 178 | const cells0 = 2; 179 | #elseif cellbits == 64 180 | const cells0 = 3; 181 | #else 182 | #error Unsupported `cellbits`. 183 | #endif 184 | const cells1 = 3 * cellbytes; 185 | const cells2 = -1 * cellbytes; 186 | const cells3 = 1 * cellbytes; 187 | #emit load.s.pri param 188 | #emit shl.c.pri cells0 189 | 190 | #emit load.s.alt frm_addr 191 | #emit add 192 | 193 | #emit add.c cells1 194 | 195 | #emit push.pri 196 | #emit lref.s.pri cells2 197 | 198 | #emit stack cells3 199 | #emit retn 200 | } else { 201 | #if cellbits == 32 202 | const cells4 = 2; 203 | #elseif cellbits == 64 204 | const cells4 = 3; 205 | #else 206 | #error Unsupported `cellbits`. 207 | #endif 208 | const cells5 = 3 * cellbytes; 209 | const cells6 = -1 * cellbytes; 210 | const cells7 = 1 * cellbytes; 211 | #emit load.s.pri param 212 | #emit shl.c.pri cells4 213 | 214 | #emit load.s.alt frm_addr 215 | #emit add 216 | 217 | #emit add.c cells5 218 | 219 | #emit push.pri 220 | #emit lref.s.alt cells6 221 | 222 | #emit load.s.pri idx 223 | #emit lidx 224 | 225 | #emit stack cells7 226 | #emit retn 227 | } 228 | 229 | return 0; // make compiler happy 230 | } 231 | 232 | /// amx_assembly frame_info 233 | stock GetFrameVariable(frm_addr, param, idx = -1) { 234 | if (idx == -1) { 235 | const cells0 = -1 * cellbytes; 236 | const cells1 = 1 * cellbytes; 237 | #emit load.s.pri param 238 | #emit load.s.alt frm_addr 239 | #emit add 240 | 241 | #emit push.pri 242 | #emit lref.s.pri cells0 243 | 244 | #emit stack cells1 245 | #emit retn 246 | } else { 247 | const cells2 = -1 * cellbytes; 248 | const cells3 = 1 * cellbytes; 249 | #emit load.s.pri param 250 | #emit load.s.alt frm_addr 251 | #emit add 252 | 253 | #emit push.pri 254 | #emit lref.s.alt cells2 255 | 256 | #emit load.s.pri idx 257 | #emit lidx 258 | 259 | #emit stack cells3 260 | #emit retn 261 | } 262 | 263 | return 0; // make compiler happy 264 | } 265 | 266 | /// amx_assembly frame_info 267 | stock GetFrameLocal(frm_addr, param) { 268 | if (param < 0) { 269 | const cells0 = -1 * cellbytes; 270 | const cells1 = 1 * cellbytes; 271 | // probably in correct format for negative offsets 272 | #emit load.s.pri param 273 | #emit load.s.alt frm_addr 274 | #emit add 275 | 276 | #emit push.pri 277 | #emit lref.s.pri cells0 278 | 279 | #emit stack cells1 280 | #emit retn 281 | } else { 282 | #if cellbits == 32 283 | const cells2 = 2; 284 | #elseif cellbits == 64 285 | const cells2 = 3; 286 | #else 287 | #error Unsupported `cellbits`. 288 | #endif 289 | const cells3 = -1 * cellbytes; 290 | const cells4 = -1 * cellbytes; 291 | const cells5 = 1 * cellbytes; 292 | #emit load.s.pri param 293 | #emit shl.c.pri cells2 294 | 295 | #emit add.c cells3 296 | 297 | #emit load.s.alt frm_addr 298 | #emit add 299 | 300 | #emit push.pri 301 | #emit lref.s.pri cells4 302 | 303 | #emit stack cells5 304 | #emit retn 305 | } 306 | 307 | return 0; // make compiler happy 308 | } 309 | 310 | /// amx_assembly frame_info 311 | stock SetFrameParameter(frm_addr, param, value, idx = -1) { 312 | if (idx == -1) { 313 | #if cellbits == 32 314 | const cells0 = 2; 315 | #elseif cellbits == 64 316 | const cells0 = 3; 317 | #else 318 | #error Unsupported `cellbits`. 319 | #endif 320 | const cells1 = 3 * cellbytes; 321 | const cells2 = -1 * cellbytes; 322 | const cells3 = 1 * cellbytes; 323 | #emit load.s.pri param 324 | #emit shl.c.pri cells0 325 | 326 | #emit load.s.alt frm_addr 327 | #emit add 328 | 329 | #emit add.c cells1 330 | 331 | #emit push.pri 332 | #emit load.s.pri value 333 | #emit sref.s.pri cells2 334 | 335 | #emit stack cells3 336 | #emit retn 337 | } else { 338 | #if cellbits == 32 339 | const cells4 = 2; 340 | #elseif cellbits == 64 341 | const cells4 = 3; 342 | #else 343 | #error Unsupported `cellbits`. 344 | #endif 345 | const cells5 = 3 * cellbytes; 346 | const cells6 = -1 * cellbytes; 347 | const cells7 = 1 * cellbytes; 348 | #emit load.s.pri param 349 | #emit shl.c.pri cells4 350 | 351 | #emit load.s.alt frm_addr 352 | #emit add 353 | 354 | #emit add.c cells5 355 | 356 | #emit push.pri 357 | #emit lref.s.alt cells6 358 | 359 | #emit load.s.pri idx 360 | #emit idxaddr 361 | 362 | #emit load.s.alt value 363 | #emit xchg 364 | #emit stor.i 365 | 366 | #emit stack cells7 367 | #emit retn 368 | } 369 | 370 | return 0; // make compiler happy 371 | } 372 | 373 | /// amx_assembly frame_info 374 | stock SetFrameVariable(frm_addr, param, value, idx = -1) { 375 | if (idx == -1) { 376 | const cells0 = -1 * cellbytes; 377 | const cells1 = 1 * cellbytes; 378 | #emit load.s.pri param 379 | #emit load.s.alt frm_addr 380 | #emit add 381 | 382 | #emit push.pri 383 | #emit load.s.pri value 384 | #emit sref.s.pri cells0 385 | 386 | #emit stack cells1 387 | #emit retn 388 | } else { 389 | const cells2 = -1 * cellbytes; 390 | const cells3 = 1 * cellbytes; 391 | #emit load.s.pri param 392 | #emit load.s.alt frm_addr 393 | #emit add 394 | 395 | #emit push.pri 396 | #emit lref.s.alt cells2 397 | 398 | #emit load.s.pri idx 399 | #emit idxaddr 400 | 401 | #emit load.s.alt value 402 | #emit xchg 403 | #emit stor.i 404 | 405 | #emit stack cells3 406 | #emit retn 407 | } 408 | 409 | return 0; // make compiler happy 410 | } 411 | 412 | /// amx_assembly frame_info 413 | stock SetFrameLocal(frm_addr, param, value) { 414 | if (param < 0) { 415 | const cells0 = -1 * cellbytes; 416 | const cells1 = 1 * cellbytes; 417 | // probably in correct format for negative offsets 418 | #emit load.s.pri param 419 | #emit load.s.alt frm_addr 420 | #emit add 421 | 422 | #emit push.pri 423 | #emit load.s.pri value 424 | #emit sref.s.pri cells0 425 | 426 | #emit stack cells1 427 | #emit retn 428 | } else { 429 | #if cellbits == 32 430 | const cells2 = 2; 431 | #elseif cellbits == 64 432 | const cells2 = 3; 433 | #else 434 | #error Unsupported `cellbits`. 435 | #endif 436 | const cells3 = -1 * cellbytes; 437 | const cells4 = -1 * cellbytes; 438 | const cells5 = 1 * cellbytes; 439 | #emit load.s.pri param 440 | #emit shl.c.pri cells2 441 | 442 | #emit add.c cells3 443 | 444 | #emit load.s.alt frm_addr 445 | #emit add 446 | 447 | #emit push.pri 448 | #emit load.s.pri value 449 | #emit sref.s.pri cells4 450 | 451 | #emit stack cells5 452 | #emit retn 453 | } 454 | 455 | return 0; // make compiler happy 456 | } 457 | 458 | /// amx_assembly frame_info 459 | static stock GetCallerFrame() { 460 | const cells0 = -1 * cellbytes; 461 | const cells1 = -2 * cellbytes; 462 | const cells2 = 2 * cellbytes; 463 | #emit lctrl __frm 464 | 465 | #emit push.pri 466 | #emit lref.s.pri cells0 467 | 468 | #emit push.pri 469 | #emit lref.s.pri cells1 470 | 471 | #emit stack cells2 472 | #emit retn 473 | 474 | return 0; // make compiler happy 475 | } 476 | 477 | /// amx_assembly frame_info 478 | stock GetCurrentFramePreviousFrame() { 479 | return GetFramePreviousFrame(GetCallerFrame()); 480 | } 481 | 482 | /// amx_assembly frame_info 483 | stock GetCurrentFrameReturn() { 484 | return GetFrameReturn(GetCallerFrame()); 485 | } 486 | 487 | /// amx_assembly frame_info 488 | stock GetCurrentFrameTotalSize() { 489 | return GetFrameTotalSize(GetCallerFrame()); 490 | } 491 | 492 | /// amx_assembly frame_info 493 | stock GetCurrentFrameTotalCount() { 494 | return GetFrameTotalCount(GetCallerFrame()); 495 | } 496 | 497 | /// amx_assembly frame_info 498 | stock GetCurrentFrameLocalSize() { 499 | return GetFrameLocalSize(GetCallerFrame()); 500 | } 501 | 502 | /// amx_assembly frame_info 503 | stock GetCurrentFrameLocalCount() { 504 | return GetFrameLocalCount(GetCallerFrame()); 505 | } 506 | 507 | /// amx_assembly frame_info 508 | stock GetCurrentFrameHeaderSize() { 509 | return GetFrameHeaderSize(GetCallerFrame()); 510 | } 511 | 512 | /// amx_assembly frame_info 513 | stock GetCurrentFrameHeaderCount() { 514 | return GetFrameHeaderCount(GetCallerFrame()); 515 | } 516 | 517 | /// amx_assembly frame_info 518 | stock GetCurrentFrameParameterSize() { 519 | return GetFrameParameterSize(GetCallerFrame()); 520 | } 521 | 522 | /// amx_assembly frame_info 523 | stock GetCurrentFrameParameter(param, idx = -1) { 524 | return GetFrameParameter(GetCallerFrame(), param, idx); 525 | } 526 | 527 | /// amx_assembly frame_info 528 | stock GetCurrentFrameLocal(param) { 529 | return GetFrameLocal(GetCallerFrame(), param); 530 | } 531 | 532 | /// amx_assembly frame_info 533 | stock GetCurrentFrameParameterCount() { 534 | return GetFrameParameterCount(GetCallerFrame()); 535 | } 536 | 537 | /// amx_assembly frame_info 538 | stock SetCurrentFrameReturn(addr) { 539 | SetFrameReturn(GetCallerFrame(), addr); 540 | } 541 | 542 | /// amx_assembly frame_info 543 | stock SetCurrentFramePreviousFrame(addr) { 544 | SetFramePreviousFrame(GetCallerFrame(), addr); 545 | } 546 | 547 | /// amx_assembly frame_info 548 | stock SetCurrentParameterSize(size) { 549 | SetFrameParameterSize(GetCallerFrame(), size); 550 | } 551 | 552 | /// amx_assembly frame_info 553 | stock SetCurrentParameterCount(count) { 554 | SetFrameParameterCount(GetCallerFrame(), count); 555 | } 556 | 557 | /// amx_assembly frame_info 558 | stock GetFrameFunction(frm_addr) { 559 | // try to determine the start address of the function this frame is for 560 | new prev_frm = GetFramePreviousFrame(frm_addr); 561 | 562 | new amxhdr[AMX_HDR]; 563 | GetAmxHeader(amxhdr); 564 | 565 | if (prev_frm == 0) { 566 | // public entry point 567 | new call_addr = GetFrameReturn(GetFrameNextFrame(frm_addr)); 568 | 569 | // find the closest public/main 570 | new highest_found = 0; 571 | new defsize = amxhdr[AMX_HDR_DEFSIZE]; 572 | new num_publics = (amxhdr[AMX_HDR_NATIVES] - amxhdr[AMX_HDR_PUBLICS]) / defsize; 573 | new off = amxhdr[AMX_HDR_PUBLICS] - amxhdr[AMX_HDR_DAT]; 574 | 575 | for (new i = 0; i != num_publics; ++i) { 576 | new addr = ReadAmxMemory(off); 577 | off += defsize; 578 | 579 | if (highest_found < addr < call_addr) { 580 | highest_found = addr; 581 | } 582 | } 583 | 584 | if (highest_found < amxhdr[AMX_HDR_CIP] < call_addr) { 585 | highest_found = amxhdr[AMX_HDR_CIP]; 586 | } 587 | 588 | // return the best found match 589 | return highest_found; 590 | } else { 591 | // called from inside the script 592 | new ret_addr = (GetFrameReturn(frm_addr) - cellbytes * 2) + (amxhdr[AMX_HDR_COD] - amxhdr[AMX_HDR_DAT]); 593 | 594 | new Opcode:opcode = UnrelocateOpcode(Opcode:ReadAmxMemory(ret_addr)); 595 | 596 | if (opcode == OP_CALL) { 597 | // standard function call, get the call address 598 | return ReadAmxMemory(ret_addr + cellbytes) - amxhdr[AMX_HDR_COD] - GetAmxBaseAddress(); 599 | 600 | } else if (opcode == OP_SCTRL) { 601 | // modified code to call a function by pointer 602 | opcode = UnrelocateOpcode(Opcode:ReadAmxMemory(ret_addr - cellbytes * 2)); 603 | 604 | if (opcode == OP_LOAD_PRI) { 605 | ret_addr = ReadAmxMemory(ret_addr - cellbytes); 606 | return ReadAmxMemory(ret_addr); 607 | } else if (opcode == OP_LOAD_S_PRI) { 608 | return GetFrameVariable(prev_frm, ReadAmxMemory(ret_addr - cellbytes)); 609 | } else if (opcode == OP_CONST_PRI) { 610 | return ReadAmxMemory(ret_addr - cellbytes); 611 | } 612 | } 613 | 614 | // guess! 615 | new end = amxhdr[AMX_HDR_COD] - amxhdr[AMX_HDR_DAT] + cellbytes; // "halt" 616 | ret_addr = GetFrameReturn(GetFrameNextFrame(frm_addr)) + end; 617 | 618 | opcode = RelocateOpcode(OP_RETN); 619 | new Opcode:proc = RelocateOpcode(OP_PROC); 620 | 621 | while (ret_addr >= end) { 622 | if (Opcode:ReadAmxMemory(ret_addr) == proc) { 623 | if (ret_addr == end || Opcode:ReadAmxMemory(ret_addr - cellbytes) == opcode) { 624 | // found a retn/proc pair. 625 | return ret_addr; 626 | } 627 | } 628 | ret_addr -= cellbytes; 629 | } 630 | } 631 | 632 | // give up... 633 | return 0; 634 | } 635 | 636 | /// amx_assembly frame_info 637 | stock GetCurrentFrameFunction() { 638 | // this function gets its own caller - pointless but here for completeness 639 | return GetFrameFunction(GetCallerFrame()); 640 | } 641 | 642 | -------------------------------------------------------------------------------- /frame_info.md: -------------------------------------------------------------------------------- 1 | amx_assembly frame_info 2 | ========================================== 3 | AMX Assembly; Function call frame information. 4 | ------------------------------------------ 5 | 6 | 7 | 8 | ## Functions 9 | 10 | 11 | ### `GetCurrentFrame`: 12 | 13 | 14 | #### Syntax 15 | 16 | 17 | ```pawn 18 | GetCurrentFrame() 19 | ``` 20 | 21 | #### Estimated stack usage 22 | 1 cells 23 | 24 | 25 | 26 | ### `GetCurrentFrameFunction`: 27 | 28 | 29 | #### Syntax 30 | 31 | 32 | ```pawn 33 | GetCurrentFrameFunction() 34 | ``` 35 | 36 | #### Depends on 37 | * [`GetCallerFrame`](#GetCallerFrame) 38 | * [`GetFrameFunction`](#GetFrameFunction) 39 | #### Estimated stack usage 40 | 4 cells 41 | 42 | 43 | 44 | ### `GetCurrentFrameHeaderCount`: 45 | 46 | 47 | #### Syntax 48 | 49 | 50 | ```pawn 51 | GetCurrentFrameHeaderCount() 52 | ``` 53 | 54 | #### Depends on 55 | * [`GetCallerFrame`](#GetCallerFrame) 56 | * [`GetFrameHeaderCount`](#GetFrameHeaderCount) 57 | #### Estimated stack usage 58 | 4 cells 59 | 60 | 61 | 62 | ### `GetCurrentFrameHeaderSize`: 63 | 64 | 65 | #### Syntax 66 | 67 | 68 | ```pawn 69 | GetCurrentFrameHeaderSize() 70 | ``` 71 | 72 | #### Depends on 73 | * [`GetCallerFrame`](#GetCallerFrame) 74 | * [`GetFrameHeaderSize`](#GetFrameHeaderSize) 75 | #### Estimated stack usage 76 | 4 cells 77 | 78 | 79 | 80 | ### `GetCurrentFrameLocal`: 81 | 82 | 83 | #### Syntax 84 | 85 | 86 | ```pawn 87 | GetCurrentFrameLocal(param) 88 | ``` 89 | 90 | 91 | #### Parameters 92 | 93 | 94 | | **Name** | **Info** | 95 | | --- | --- | 96 | | `param` | | 97 | 98 | #### Depends on 99 | * [`GetCallerFrame`](#GetCallerFrame) 100 | * [`GetFrameLocal`](#GetFrameLocal) 101 | #### Estimated stack usage 102 | 5 cells 103 | 104 | 105 | 106 | ### `GetCurrentFrameLocalCount`: 107 | 108 | 109 | #### Syntax 110 | 111 | 112 | ```pawn 113 | GetCurrentFrameLocalCount() 114 | ``` 115 | 116 | #### Depends on 117 | * [`GetCallerFrame`](#GetCallerFrame) 118 | * [`GetFrameLocalCount`](#GetFrameLocalCount) 119 | #### Estimated stack usage 120 | 4 cells 121 | 122 | 123 | 124 | ### `GetCurrentFrameLocalSize`: 125 | 126 | 127 | #### Syntax 128 | 129 | 130 | ```pawn 131 | GetCurrentFrameLocalSize() 132 | ``` 133 | 134 | #### Depends on 135 | * [`GetCallerFrame`](#GetCallerFrame) 136 | * [`GetFrameLocalSize`](#GetFrameLocalSize) 137 | #### Estimated stack usage 138 | 4 cells 139 | 140 | 141 | 142 | ### `GetCurrentFrameParameter`: 143 | 144 | 145 | #### Syntax 146 | 147 | 148 | ```pawn 149 | GetCurrentFrameParameter(param, idx) 150 | ``` 151 | 152 | 153 | #### Parameters 154 | 155 | 156 | | **Name** | **Info** | 157 | | --- | --- | 158 | | `param` | | 159 | | `idx` | | 160 | 161 | #### Depends on 162 | * [`GetCallerFrame`](#GetCallerFrame) 163 | * [`GetFrameParameter`](#GetFrameParameter) 164 | #### Estimated stack usage 165 | 6 cells 166 | 167 | 168 | 169 | ### `GetCurrentFrameParameterCount`: 170 | 171 | 172 | #### Syntax 173 | 174 | 175 | ```pawn 176 | GetCurrentFrameParameterCount() 177 | ``` 178 | 179 | #### Depends on 180 | * [`GetCallerFrame`](#GetCallerFrame) 181 | * [`GetFrameParameterCount`](#GetFrameParameterCount) 182 | #### Estimated stack usage 183 | 4 cells 184 | 185 | 186 | 187 | ### `GetCurrentFrameParameterSize`: 188 | 189 | 190 | #### Syntax 191 | 192 | 193 | ```pawn 194 | GetCurrentFrameParameterSize() 195 | ``` 196 | 197 | #### Depends on 198 | * [`GetCallerFrame`](#GetCallerFrame) 199 | * [`GetFrameParameterSize`](#GetFrameParameterSize) 200 | #### Estimated stack usage 201 | 4 cells 202 | 203 | 204 | 205 | ### `GetCurrentFramePreviousFrame`: 206 | 207 | 208 | #### Syntax 209 | 210 | 211 | ```pawn 212 | GetCurrentFramePreviousFrame() 213 | ``` 214 | 215 | #### Depends on 216 | * [`GetCallerFrame`](#GetCallerFrame) 217 | * [`GetFramePreviousFrame`](#GetFramePreviousFrame) 218 | #### Estimated stack usage 219 | 4 cells 220 | 221 | 222 | 223 | ### `GetCurrentFrameReturn`: 224 | 225 | 226 | #### Syntax 227 | 228 | 229 | ```pawn 230 | GetCurrentFrameReturn() 231 | ``` 232 | 233 | #### Depends on 234 | * [`GetCallerFrame`](#GetCallerFrame) 235 | * [`GetFrameReturn`](#GetFrameReturn) 236 | #### Estimated stack usage 237 | 4 cells 238 | 239 | 240 | 241 | ### `GetCurrentFrameTotalCount`: 242 | 243 | 244 | #### Syntax 245 | 246 | 247 | ```pawn 248 | GetCurrentFrameTotalCount() 249 | ``` 250 | 251 | #### Depends on 252 | * [`GetCallerFrame`](#GetCallerFrame) 253 | * [`GetFrameTotalCount`](#GetFrameTotalCount) 254 | #### Estimated stack usage 255 | 4 cells 256 | 257 | 258 | 259 | ### `GetCurrentFrameTotalSize`: 260 | 261 | 262 | #### Syntax 263 | 264 | 265 | ```pawn 266 | GetCurrentFrameTotalSize() 267 | ``` 268 | 269 | #### Depends on 270 | * [`GetCallerFrame`](#GetCallerFrame) 271 | * [`GetFrameTotalSize`](#GetFrameTotalSize) 272 | #### Estimated stack usage 273 | 4 cells 274 | 275 | 276 | 277 | ### `GetFrameFunction`: 278 | 279 | 280 | #### Syntax 281 | 282 | 283 | ```pawn 284 | GetFrameFunction(frm_addr) 285 | ``` 286 | 287 | 288 | #### Parameters 289 | 290 | 291 | | **Name** | **Info** | 292 | | --- | --- | 293 | | `frm_addr` | | 294 | 295 | #### Depends on 296 | * [`AMX_HDR`](#AMX_HDR) 297 | * [`AMX_HDR_CIP`](#AMX_HDR_CIP) 298 | * [`AMX_HDR_COD`](#AMX_HDR_COD) 299 | * [`AMX_HDR_DAT`](#AMX_HDR_DAT) 300 | * [`AMX_HDR_DEFSIZE`](#AMX_HDR_DEFSIZE) 301 | * [`AMX_HDR_NATIVES`](#AMX_HDR_NATIVES) 302 | * [`AMX_HDR_PUBLICS`](#AMX_HDR_PUBLICS) 303 | * [`GetAmxBaseAddress`](#GetAmxBaseAddress) 304 | * [`GetAmxHeader`](#GetAmxHeader) 305 | * [`GetFrameNextFrame`](#GetFrameNextFrame) 306 | * [`GetFramePreviousFrame`](#GetFramePreviousFrame) 307 | * [`GetFrameReturn`](#GetFrameReturn) 308 | * [`GetFrameVariable`](#GetFrameVariable) 309 | * [`OP_CALL`](#OP_CALL) 310 | * [`OP_CONST_PRI`](#OP_CONST_PRI) 311 | * [`OP_LOAD_PRI`](#OP_LOAD_PRI) 312 | * [`OP_LOAD_S_PRI`](#OP_LOAD_S_PRI) 313 | * [`OP_PROC`](#OP_PROC) 314 | * [`OP_RETN`](#OP_RETN) 315 | * [`OP_SCTRL`](#OP_SCTRL) 316 | * [`RelocateOpcode`](#RelocateOpcode) 317 | * [`UnrelocateOpcode`](#UnrelocateOpcode) 318 | * [`cellbytes`](#cellbytes) 319 | * [`gAmxAssemblyAddress_`](#gAmxAssemblyAddress_) 320 | #### Estimated stack usage 321 | 26 cells 322 | 323 | 324 | 325 | ### `GetFrameHeaderCount`: 326 | 327 | 328 | #### Syntax 329 | 330 | 331 | ```pawn 332 | GetFrameHeaderCount(frm_addr) 333 | ``` 334 | 335 | 336 | #### Parameters 337 | 338 | 339 | | **Name** | **Info** | 340 | | --- | --- | 341 | | `frm_addr` | | 342 | 343 | #### Depends on 344 | * [`GetFrameHeaderSize`](#GetFrameHeaderSize) 345 | * [`cellbytes`](#cellbytes) 346 | #### Estimated stack usage 347 | 4 cells 348 | 349 | 350 | 351 | ### `GetFrameHeaderSize`: 352 | 353 | 354 | #### Syntax 355 | 356 | 357 | ```pawn 358 | GetFrameHeaderSize(frm_addr) 359 | ``` 360 | 361 | 362 | #### Parameters 363 | 364 | 365 | | **Name** | **Info** | 366 | | --- | --- | 367 | | `frm_addr` | | 368 | 369 | #### Depends on 370 | * [`cellbytes`](#cellbytes) 371 | #### Estimated stack usage 372 | 1 cells 373 | 374 | 375 | 376 | ### `GetFrameLocalCount`: 377 | 378 | 379 | #### Syntax 380 | 381 | 382 | ```pawn 383 | GetFrameLocalCount(frm_addr) 384 | ``` 385 | 386 | 387 | #### Parameters 388 | 389 | 390 | | **Name** | **Info** | 391 | | --- | --- | 392 | | `frm_addr` | | 393 | 394 | #### Depends on 395 | * [`GetFrameLocalSize`](#GetFrameLocalSize) 396 | * [`cellbytes`](#cellbytes) 397 | #### Estimated stack usage 398 | 4 cells 399 | 400 | 401 | 402 | ### `GetFrameLocalSize`: 403 | 404 | 405 | #### Syntax 406 | 407 | 408 | ```pawn 409 | GetFrameLocalSize(frm_addr) 410 | ``` 411 | 412 | 413 | #### Parameters 414 | 415 | 416 | | **Name** | **Info** | 417 | | --- | --- | 418 | | `frm_addr` | | 419 | 420 | #### Depends on 421 | * [`GetFrameHeaderSize`](#GetFrameHeaderSize) 422 | * [`GetFrameNextFrame`](#GetFrameNextFrame) 423 | * [`GetFrameParameterSize`](#GetFrameParameterSize) 424 | #### Estimated stack usage 425 | 6 cells 426 | 427 | 428 | 429 | ### `GetFrameNextFrame`: 430 | 431 | 432 | #### Syntax 433 | 434 | 435 | ```pawn 436 | GetFrameNextFrame(frm_addr) 437 | ``` 438 | 439 | 440 | #### Parameters 441 | 442 | 443 | | **Name** | **Info** | 444 | | --- | --- | 445 | | `frm_addr` | | 446 | 447 | #### Depends on 448 | * [`GetCurrentFrame`](#GetCurrentFrame) 449 | * [`GetFramePreviousFrame`](#GetFramePreviousFrame) 450 | #### Estimated stack usage 451 | 6 cells 452 | 453 | 454 | 455 | ### `GetFrameParameterCount`: 456 | 457 | 458 | #### Syntax 459 | 460 | 461 | ```pawn 462 | GetFrameParameterCount(frm_addr) 463 | ``` 464 | 465 | 466 | #### Parameters 467 | 468 | 469 | | **Name** | **Info** | 470 | | --- | --- | 471 | | `frm_addr` | | 472 | 473 | #### Depends on 474 | * [`GetFrameParameterSize`](#GetFrameParameterSize) 475 | * [`cellbytes`](#cellbytes) 476 | #### Estimated stack usage 477 | 4 cells 478 | 479 | 480 | 481 | ### `GetFramePreviousFrame`: 482 | 483 | 484 | #### Syntax 485 | 486 | 487 | ```pawn 488 | GetFramePreviousFrame(frm_addr) 489 | ``` 490 | 491 | 492 | #### Parameters 493 | 494 | 495 | | **Name** | **Info** | 496 | | --- | --- | 497 | | `frm_addr` | | 498 | 499 | #### Estimated stack usage 500 | 1 cells 501 | 502 | 503 | 504 | ### `GetFrameTotalCount`: 505 | 506 | 507 | #### Syntax 508 | 509 | 510 | ```pawn 511 | GetFrameTotalCount(frm_addr) 512 | ``` 513 | 514 | 515 | #### Parameters 516 | 517 | 518 | | **Name** | **Info** | 519 | | --- | --- | 520 | | `frm_addr` | | 521 | 522 | #### Depends on 523 | * [`GetFrameTotalSize`](#GetFrameTotalSize) 524 | * [`cellbytes`](#cellbytes) 525 | #### Estimated stack usage 526 | 4 cells 527 | 528 | 529 | 530 | ### `GetFrameTotalSize`: 531 | 532 | 533 | #### Syntax 534 | 535 | 536 | ```pawn 537 | GetFrameTotalSize(frm_addr) 538 | ``` 539 | 540 | 541 | #### Parameters 542 | 543 | 544 | | **Name** | **Info** | 545 | | --- | --- | 546 | | `frm_addr` | | 547 | 548 | #### Depends on 549 | * [`GetFrameHeaderSize`](#GetFrameHeaderSize) 550 | * [`GetFrameLocalSize`](#GetFrameLocalSize) 551 | * [`GetFrameParameterSize`](#GetFrameParameterSize) 552 | #### Estimated stack usage 553 | 4 cells 554 | 555 | 556 | 557 | ### `SetCurrentFramePreviousFrame`: 558 | 559 | 560 | #### Syntax 561 | 562 | 563 | ```pawn 564 | SetCurrentFramePreviousFrame(addr) 565 | ``` 566 | 567 | 568 | #### Parameters 569 | 570 | 571 | | **Name** | **Info** | 572 | | --- | --- | 573 | | `addr` | | 574 | 575 | #### Depends on 576 | * [`GetCallerFrame`](#GetCallerFrame) 577 | * [`SetFramePreviousFrame`](#SetFramePreviousFrame) 578 | #### Estimated stack usage 579 | 5 cells 580 | 581 | 582 | 583 | ### `SetCurrentFrameReturn`: 584 | 585 | 586 | #### Syntax 587 | 588 | 589 | ```pawn 590 | SetCurrentFrameReturn(addr) 591 | ``` 592 | 593 | 594 | #### Parameters 595 | 596 | 597 | | **Name** | **Info** | 598 | | --- | --- | 599 | | `addr` | | 600 | 601 | #### Depends on 602 | * [`GetCallerFrame`](#GetCallerFrame) 603 | * [`SetFrameReturn`](#SetFrameReturn) 604 | #### Estimated stack usage 605 | 5 cells 606 | 607 | 608 | 609 | ### `SetCurrentParameterCount`: 610 | 611 | 612 | #### Syntax 613 | 614 | 615 | ```pawn 616 | SetCurrentParameterCount(count) 617 | ``` 618 | 619 | 620 | #### Parameters 621 | 622 | 623 | | **Name** | **Info** | 624 | | --- | --- | 625 | | `count` | | 626 | 627 | #### Depends on 628 | * [`GetCallerFrame`](#GetCallerFrame) 629 | * [`SetFrameParameterCount`](#SetFrameParameterCount) 630 | #### Estimated stack usage 631 | 5 cells 632 | 633 | 634 | 635 | ### `SetCurrentParameterSize`: 636 | 637 | 638 | #### Syntax 639 | 640 | 641 | ```pawn 642 | SetCurrentParameterSize(size) 643 | ``` 644 | 645 | 646 | #### Parameters 647 | 648 | 649 | | **Name** | **Info** | 650 | | --- | --- | 651 | | `size` | | 652 | 653 | #### Depends on 654 | * [`GetCallerFrame`](#GetCallerFrame) 655 | * [`SetFrameParameterSize`](#SetFrameParameterSize) 656 | #### Estimated stack usage 657 | 5 cells 658 | 659 | 660 | 661 | ### `SetFrameParameterCount`: 662 | 663 | 664 | #### Syntax 665 | 666 | 667 | ```pawn 668 | SetFrameParameterCount(frm_addr, count) 669 | ``` 670 | 671 | 672 | #### Parameters 673 | 674 | 675 | | **Name** | **Info** | 676 | | --- | --- | 677 | | `frm_addr` | | 678 | | `count` | | 679 | 680 | #### Depends on 681 | * [`SetFrameParameterSize`](#SetFrameParameterSize) 682 | * [`cellbytes`](#cellbytes) 683 | #### Estimated stack usage 684 | 5 cells 685 | 686 | 687 | 688 | ### `SetFramePreviousFrame`: 689 | 690 | 691 | #### Syntax 692 | 693 | 694 | ```pawn 695 | SetFramePreviousFrame(frm_addr, addr) 696 | ``` 697 | 698 | 699 | #### Parameters 700 | 701 | 702 | | **Name** | **Info** | 703 | | --- | --- | 704 | | `frm_addr` | | 705 | | `addr` | | 706 | 707 | #### Estimated stack usage 708 | 1 cells 709 | 710 | -------------------------------------------------------------------------------- /heap_alloc.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined HEAP_ALLOC_INC 22 | #endinput 23 | #endif 24 | #define HEAP_ALLOC_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include "amx_memory" 40 | 41 | #define HeapAlloc HeapAllocCells 42 | 43 | /// amx_assembly heap_alloc 44 | ///

45 | /// Allocates a block of (uninitialized) memory on the heap. 46 | /// 47 | stock HeapAllocBytes(nbytes) { 48 | new address = 0; 49 | #emit lctrl __hea 50 | #emit stor.s.pri address 51 | #emit load.s.alt nbytes 52 | #emit add 53 | #emit sctrl __hea 54 | return address; 55 | } 56 | 57 | /// amx_assembly heap_alloc 58 | /// 59 | /// Same as HeapAllocBytes() but operates on cells. 60 | /// 61 | stock HeapAllocCells(ncells) { 62 | return HeapAllocBytes(ncells * cellbytes); 63 | } 64 | 65 | /// amx_assembly heap_alloc 66 | /// 67 | /// Releases memory allocated with HeapAlloc(). 68 | /// 69 | stock HeapRelease(address) { 70 | #emit load.s.pri address 71 | #emit sctrl __hea 72 | return address; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /heap_alloc.md: -------------------------------------------------------------------------------- 1 | amx_assembly heap_alloc 2 | ========================================== 3 | AMX Assembly Library: Allocate space in the heap. 4 | ------------------------------------------ 5 | 6 | 7 | 8 | ## Functions 9 | 10 | 11 | ### `HeapAllocBytes`: 12 | 13 | Allocates a block of (uninitialized) memory on the heap. 14 | 15 | 16 | 17 | #### Syntax 18 | 19 | 20 | ```pawn 21 | HeapAllocBytes(nbytes) 22 | ``` 23 | 24 | 25 | #### Parameters 26 | 27 | 28 | | **Name** | **Info** | 29 | | --- | --- | 30 | | `nbytes` | | 31 | 32 | #### Estimated stack usage 33 | 2 cells 34 | 35 | 36 | 37 | ### `HeapAllocCells`: 38 | 39 | Same as HeapAllocBytes() but operates on cells. 40 | 41 | 42 | 43 | #### Syntax 44 | 45 | 46 | ```pawn 47 | HeapAllocCells(ncells) 48 | ``` 49 | 50 | 51 | #### Parameters 52 | 53 | 54 | | **Name** | **Info** | 55 | | --- | --- | 56 | | `ncells` | | 57 | 58 | #### Depends on 59 | * [`HeapAllocBytes`](#HeapAllocBytes) 60 | * [`cellbytes`](#cellbytes) 61 | #### Estimated stack usage 62 | 4 cells 63 | 64 | 65 | 66 | ### `HeapRelease`: 67 | 68 | Releases memory allocated with HeapAlloc(). 69 | 70 | 71 | 72 | #### Syntax 73 | 74 | 75 | ```pawn 76 | HeapRelease(address) 77 | ``` 78 | 79 | 80 | #### Parameters 81 | 82 | 83 | | **Name** | **Info** | 84 | | --- | --- | 85 | | `address` | | 86 | 87 | #### Estimated stack usage 88 | 1 cells 89 | 90 | -------------------------------------------------------------------------------- /jit.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Y_Less 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined JIT_INC 22 | #endinput 23 | #endif 24 | #define JIT_INC 25 | 26 | /** 27 | * 28 | * 29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #if !defined __register_names 40 | const __cod = 0; 41 | const __dat = 1; 42 | const __hea = 2; 43 | const __stp = 3; 44 | const __stk = 4; 45 | const __frm = 5; 46 | const __cip = 6; 47 | const __jit = 7; 48 | const __jmp = 8; 49 | const __flg = 9; 50 | #define __register_names 51 | #endif 52 | 53 | /// amx_assembly jit 54 | stock bool:IsJITPresent() { 55 | #emit zero.pri 56 | #emit lctrl __jit 57 | #emit retn 58 | return false; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /os.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined OS_INC 22 | #endinput 23 | #endif 24 | #define OS_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | /// amx_assembly os 40 | enum OS { 41 | OS_UNKNOWN, 42 | OS_LINUX, 43 | OS_WINDOWS 44 | }; 45 | 46 | /// amx_assembly os 47 | const OS:OS__ = OS; 48 | 49 | #if !defined __register_names 50 | const __cod = 0; 51 | const __dat = 1; 52 | const __hea = 2; 53 | const __stp = 3; 54 | const __stk = 4; 55 | const __frm = 5; 56 | const __cip = 6; 57 | const __jit = 7; 58 | const __jmp = 8; 59 | const __flg = 9; 60 | #define __register_names 61 | #endif 62 | 63 | /// amx_assembly os 64 | stock OS:GetOS() { 65 | new 66 | val = 0; 67 | // The code at address 0 is always `HALT` so load and check it. 68 | // Based on code originally in fixes.inc. 69 | #emit lctrl __cod 70 | #emit move.alt 71 | #emit lctrl __dat 72 | #emit sub.alt 73 | #emit stor.s.pri val 74 | #emit lref.s.alt val 75 | #emit stor.s.alt val 76 | // Hard-coded opcode of `120` to avoid including `opcode.inc`. 77 | return val == 120 ? OS_WINDOWS : OS_LINUX; 78 | } 79 | 80 | /// amx_assembly os 81 | stock bool:IsWindows() { 82 | return GetOS() == OS_WINDOWS; 83 | } 84 | 85 | /// amx_assembly os 86 | stock bool:IsLinux() { 87 | return GetOS() == OS_LINUX; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /pawn.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": "Zeex", 3 | "repo": "amx_assembly", 4 | "entry": "test\\all-tests.pwn", 5 | "output": "test\\all-tests.amx", 6 | "contributors": [ 7 | "Slice", 8 | "Y_Less", 9 | "Zeex" 10 | ] 11 | } 12 | 13 | -------------------------------------------------------------------------------- /phys_memory.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011-2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined PHYS_MEMORY_INC 22 | #endinput 23 | #endif 24 | #define PHYS_MEMORY_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include 40 | 41 | #include "amx_base" 42 | 43 | /// amx_assembly phys_memory 44 | static stock GetDat() { 45 | #emit lctrl __dat 46 | #emit retn 47 | return 0; // make compiler happy 48 | } 49 | 50 | /// amx_assembly phys_memory 51 | static stock AbsToRel(address) { 52 | if (HasReloc()) { 53 | return address - (GetAmxBaseAddress() + GetDat()); 54 | } 55 | return 0; 56 | } 57 | 58 | /// amx_assembly phys_memory 59 | static stock RelToAbs(address) { 60 | if (HasReloc()) { 61 | return address + (GetAmxBaseAddress() + GetDat()); 62 | } 63 | return 0; 64 | } 65 | 66 | /// amx_assembly phys_memory 67 | static stock RelToWide(address, &wide) { 68 | // I don't think we can actually write this function, since relocations are 69 | // disabled in 64-bit compatibility mode. 70 | if (HasReloc()) { 71 | wide = 0; 72 | return address + (GetAmxBaseAddress() + GetDat()); 73 | } 74 | return 0; 75 | } 76 | 77 | /// amx_assembly phys_memory 78 | ///

79 | /// Returns the absolute address of a variable/array. 80 | /// 81 | stock refabs(...) { 82 | const cells0 = 3 * cellbytes; 83 | assert(numargs() == 1); 84 | new address = 0; 85 | #emit load.s.pri cells0 86 | #emit stor.s.pri address 87 | return RelToAbs(address); 88 | } 89 | 90 | /// amx_assembly phys_memory 91 | /// 92 | /// Returns the absolute address of a variable/array. 93 | /// 94 | stock refwide(&wide, ...) { 95 | const cells0 = 4 * cellbytes; 96 | assert(numargs() == 2); 97 | new address = 0; 98 | #emit load.s.pri cells0 99 | #emit stor.s.pri address 100 | return RelToWide(address, wide); 101 | } 102 | 103 | /// amx_assembly phys_memory 104 | stock ReadPhysMemory(address, dest[], num = sizeof(dest)) { 105 | const cells0 = 3 * cellbytes; 106 | new rel_addr = AbsToRel(address); 107 | 108 | // Current destination cell address. 109 | new cur_dest = 0; 110 | #emit load.s.pri dest 111 | #emit stor.s.pri cur_dest 112 | 113 | // Currently reading address. 114 | new cur_addr = rel_addr; 115 | 116 | // Read num cells to dest. 117 | for (new i = 0; i < num; i++, cur_addr += cellbytes, cur_dest += cellbytes) { 118 | #emit lref.s.pri cur_addr 119 | #emit sref.s.pri cur_dest 120 | } 121 | 122 | #emit stack cells0 123 | #emit retn 124 | 125 | return 0; // make compiler happy 126 | } 127 | 128 | /// amx_assembly phys_memory 129 | stock WritePhysMemory(address, src[], num = sizeof(src)) { 130 | const cells0 = 3 * cellbytes; 131 | new rel_addr = AbsToRel(address); 132 | 133 | // Current destination cell address.. 134 | new cur_src = 0; 135 | #emit load.s.pri src 136 | #emit stor.s.pri cur_src 137 | 138 | // Currently reading address. 139 | new cur_addr = rel_addr; 140 | 141 | // Write num cells from src. 142 | for (new i = 0; i < num; i++, cur_addr += cellbytes, cur_src += cellbytes) { 143 | #emit lref.s.pri cur_src 144 | #emit sref.s.pri cur_addr 145 | } 146 | 147 | #emit stack cells0 148 | #emit retn 149 | 150 | return 0; // make compiler happy 151 | } 152 | 153 | /// amx_assembly phys_memory 154 | stock ReadPhysMemoryCell(address) { 155 | const cells0 = 1 * cellbytes; 156 | new rel_addr = AbsToRel(address); 157 | #emit lref.s.pri rel_addr 158 | #emit stack cells0 159 | #emit retn 160 | return 0; // make compiler happy 161 | } 162 | 163 | /// amx_assembly phys_memory 164 | stock WritePhysMemoryCell(address, what) { 165 | const cells0 = 1 * cellbytes; 166 | new rel_addr = AbsToRel(address); 167 | #emit load.s.pri what 168 | #emit sref.s.pri rel_addr 169 | #emit stack cells0 170 | #emit retn 171 | return 0; // make compiler happy 172 | } 173 | 174 | -------------------------------------------------------------------------------- /phys_memory.md: -------------------------------------------------------------------------------- 1 | amx_assembly phys_memory 2 | ========================================== 3 | AMX Assembly Library: Read and write real server addresses. 4 | ------------------------------------------ 5 | 6 | 7 | 8 | ## Functions 9 | 10 | 11 | ### `AbsToRel`: 12 | 13 | 14 | #### Syntax 15 | 16 | 17 | ```pawn 18 | AbsToRel(address) 19 | ``` 20 | 21 | 22 | #### Parameters 23 | 24 | 25 | | **Name** | **Info** | 26 | | --- | --- | 27 | | `address` | | 28 | 29 | #### Depends on 30 | * [`GetAmxBaseAddress`](#GetAmxBaseAddress) 31 | * [`GetDat`](#GetDat) 32 | #### Estimated stack usage 33 | 3 cells 34 | 35 | 36 | 37 | ### `GetDat`: 38 | 39 | 40 | #### Syntax 41 | 42 | 43 | ```pawn 44 | GetDat() 45 | ``` 46 | 47 | #### Estimated stack usage 48 | 1 cells 49 | 50 | 51 | 52 | ### `RelToAbs`: 53 | 54 | 55 | #### Syntax 56 | 57 | 58 | ```pawn 59 | RelToAbs(address) 60 | ``` 61 | 62 | 63 | #### Parameters 64 | 65 | 66 | | **Name** | **Info** | 67 | | --- | --- | 68 | | `address` | | 69 | 70 | #### Depends on 71 | * [`GetAmxBaseAddress`](#GetAmxBaseAddress) 72 | * [`GetDat`](#GetDat) 73 | #### Estimated stack usage 74 | 3 cells 75 | 76 | -------------------------------------------------------------------------------- /profiler.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined PROFILER_INC 22 | #endinput 23 | #endif 24 | #define PROFILER_INC 25 | 26 | /** 27 | * 28 | * 29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include 40 | #include

61 | 62 | /// amx_assembly profiler 63 | enum ProfEntryCode { 64 | Opcode:pec_push_c0, 65 | pec_push_address, 66 | Opcode:pec_push_c1, 67 | pec_push_index, 68 | Opcode:pec_push_c2, 69 | pec_push_8, 70 | Opcode:pec_call, 71 | pec_call_enter 72 | } 73 | 74 | /// amx_assembly profiler 75 | const ProfEntryCode:ProfEntryCode__ = ProfEntryCode; 76 | 77 | /// amx_assembly profiler 78 | static stock g_pecs[PROF_MAX_PUBLICS][ProfEntryCode]; 79 | 80 | /// amx_assembly profiler 81 | static stock g_num_pecs = 0; 82 | 83 | ///

84 | 85 | /// amx_assembly profiler 86 | enum ProfCallInfo { 87 | pci_index, 88 | pci_start_time 89 | } 90 | 91 | /// amx_assembly profiler 92 | const ProfCallInfo:ProfCallInfo__ = ProfCallInfo; 93 | 94 | ///

95 | 96 | /// amx_assembly profiler 97 | enum ProfPublicInfo { 98 | ppi_child_time, 99 | ppi_total_time, 100 | ppi_num_calls 101 | } 102 | 103 | /// amx_assembly profiler 104 | const ProfPublicInfo:ProfPublicInfo__ = ProfPublicInfo; 105 | 106 | /// amx_assembly profiler 107 | static stock g_publics[PROF_MAX_PUBLICS][ProfPublicInfo]; 108 | 109 | /// amx_assembly profiler 110 | static stock g_num_publics = 0; 111 | 112 | /// amx_assembly profiler 113 | static stock g_call_stack[PROF_MAX_CALL_STACK][ProfCallInfo]; 114 | 115 | /// amx_assembly profiler 116 | static stock g_call_depth = 0; 117 | 118 | /// amx_assembly profiler 119 | static stock exit_public() { 120 | if (--g_call_depth < sizeof(g_call_stack)) { 121 | new index = g_call_stack[g_call_depth][pci_index]; 122 | 123 | new parent = -1; 124 | if (g_call_depth > 0) { 125 | parent = g_call_stack[g_call_depth-1][pci_index]; 126 | } 127 | 128 | if (index < g_num_publics) { 129 | new i = g_call_depth; 130 | new tick = tickcount(); 131 | new time = tick - g_call_stack[i][pci_start_time]; 132 | if (time < 0) { 133 | // Work around negative intervals due to tick count overflow 134 | if (tick < 0) { 135 | time = (tick - cellmin) + (cellmax - g_call_stack[i][pci_start_time]); 136 | } 137 | } 138 | if (time > 0) { 139 | g_publics[index][ppi_total_time] += time; 140 | if (parent > 0) { 141 | g_publics[parent][ppi_child_time] += time; 142 | } 143 | } 144 | g_publics[index][ppi_num_calls]++; 145 | } 146 | //{ 147 | // new public_name[64]; 148 | // GetPublicNameByIndex(index, public_name); 149 | // printf("Leaving %s", public_name); 150 | //} 151 | } 152 | 153 | #emit halt 0 154 | return 0; // make compiler happy 155 | } 156 | 157 | /// amx_assembly profiler 158 | static stock enter_public(index, address) { 159 | const cells0 = 5 * cellbytes; 160 | const cells1 = 1 * cellbytes; 161 | //{ 162 | // new public_name[64]; 163 | // GetPublicNameByIndex(index, public_name); 164 | // printf("Entering %s", public_name); 165 | //} 166 | 167 | if (g_call_depth < sizeof(g_call_stack)) { 168 | new pci[ProfCallInfo]; 169 | pci[pci_index] = index; 170 | pci[pci_start_time] = tickcount(); 171 | g_call_stack[g_call_depth] = pci; 172 | } else { 173 | printf("profiler warning: PROF_MAX_CALL_STACK is set to %d but current level is %d", PROF_MAX_CALL_STACK, g_call_depth); 174 | } 175 | 176 | // Pop locals + arguments + numbytes + return address + frm. 177 | #emit stack cells0 178 | 179 | ++g_call_depth; 180 | 181 | // modify public's return address so it will jump to exit_public() when done 182 | #emit stack cells1 183 | #emit const.pri exit_public 184 | #emit lctrl __jmp 185 | #emit push.pri 186 | 187 | // jump to the public 188 | #emit load.s.pri address 189 | #emit jump.pri 190 | 191 | return 0; // make compiler happy 192 | } 193 | 194 | /// amx_assembly profiler 195 | static stock new_pec(index, address, code_start) { 196 | if (g_num_pecs < sizeof(g_pecs)) { 197 | new pec[ProfEntryCode]; 198 | 199 | pec[pec_push_c0] = RelocateOpcode(OP_PUSH_C); 200 | pec[pec_push_address] = address; 201 | 202 | pec[pec_push_c1] = RelocateOpcode(OP_PUSH_C); 203 | pec[pec_push_index] = index; 204 | 205 | pec[pec_push_c2] = RelocateOpcode(OP_PUSH_C); 206 | pec[pec_push_8] = 2 * cellbytes; 207 | 208 | new enter_proc = 0; 209 | #emit const.pri enter_public 210 | #emit stor.s.pri enter_proc 211 | pec[pec_call] = RelocateOpcode(OP_CALL); 212 | pec[pec_call_enter] = code_start + enter_proc; 213 | 214 | g_pecs[g_num_pecs] = pec; 215 | return g_num_pecs++; 216 | } 217 | 218 | return -1; 219 | } 220 | 221 | /// amx_assembly profiler 222 | stock ProfilerInit() { 223 | new hdr[AMX_HDR]; 224 | GetAmxHeader(hdr); 225 | 226 | new publics = hdr[AMX_HDR_PUBLICS] - hdr[AMX_HDR_DAT]; 227 | new defsize = hdr[AMX_HDR_DEFSIZE]; 228 | new num_publics = (hdr[AMX_HDR_NATIVES] - hdr[AMX_HDR_PUBLICS]) / defsize; 229 | 230 | new amx_base = GetAmxBaseAddress() + hdr[AMX_HDR_COD]; 231 | 232 | // Redirect all public calls to ProfileHook(). 233 | for (new i = 0, cur = publics; i < num_publics; cur += defsize, i++) { 234 | new address = 0; 235 | #emit lref.s.pri cur 236 | #emit stor.s.pri address 237 | 238 | new pec_index = new_pec(i, address, amx_base); 239 | if (pec_index < 0) { 240 | printf("profiler warning: Too many public functions (%d). Consider increasing PROF_MAX_PUBLICS.", num_publics); 241 | break; 242 | } 243 | 244 | // Get start address of the g_pecs sub-array at pec_index. 245 | new pec_start = 0; 246 | #emit const.alt g_pecs 247 | #emit load.s.pri pec_index 248 | #emit idxaddr 249 | #emit move.alt 250 | #emit load.i 251 | #emit add 252 | #emit stor.s.pri pec_start 253 | 254 | // Alter public table to redirect calls to the PEC. 255 | HookPublic(i, pec_start + hdr[AMX_HDR_DAT] - hdr[AMX_HDR_COD]); 256 | 257 | g_publics[i][ppi_child_time] = 0; 258 | g_publics[i][ppi_total_time] = 0; 259 | g_publics[i][ppi_num_calls] = 0; 260 | g_num_publics++; 261 | } 262 | } 263 | 264 | /// amx_assembly profiler 265 | stock bool:ProfilerWriteData(const filename[]) { 266 | new File:file = fopen(filename, io_write); 267 | new buffer[100]; 268 | 269 | if (!file) { 270 | return false; 271 | } 272 | 273 | fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n"); 274 | fwrite(file, "| name | calls | self_time | total_time |\n"); 275 | fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n"); 276 | 277 | for (new i = 0; i < g_num_publics; i++) { 278 | new name[64]; 279 | GetPublicNameFromIndex(i, name); 280 | #if defined strformat 281 | strformat(buffer, sizeof(buffer), false, "| %32s |%15d |%15d |%15d |\n", 282 | name, 283 | g_publics[i][ppi_num_calls], 284 | g_publics[i][ppi_total_time] - g_publics[i][ppi_child_time], 285 | g_publics[i][ppi_total_time] 286 | ); 287 | #elseif defined format 288 | format(buffer, sizeof(buffer), "| %32s |%15d |%15d |%15d |\n", 289 | name, 290 | g_publics[i][ppi_num_calls], 291 | g_publics[i][ppi_total_time] - g_publics[i][ppi_child_time], 292 | g_publics[i][ppi_total_time] 293 | ); 294 | #else 295 | // Generate proper compiler errors for the missing symbols - ONLY IF the function is 296 | // actually used. 297 | format(buffer, 0, "You need this function or strformat"); 298 | strformat(buffer, 0, false, "You need this function or format"); 299 | #endif 300 | fwrite(file, buffer); 301 | fwrite(file, "+----------------------------------+----------------+----------------+----------------+\n"); 302 | } 303 | 304 | fclose(file); 305 | return true; 306 | } 307 | 308 | -------------------------------------------------------------------------------- /shellcode.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined SHELLCODE_INC 22 | #endinput 23 | #endif 24 | #define SHELLCODE_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #include "amx_header" 40 | #include "dynamic_call" 41 | #include "phys_memory" 42 | 43 | /// amx_assembly shellcode 44 | stock RunShellcode(code_ptr, bool:align = true) { 45 | if (align) { 46 | code_ptr = ((code_ptr + 15) >>> 4) << 4; 47 | } 48 | return SysreqD(code_ptr); 49 | } 50 | 51 | stock RunShellcodeWide(code_ptr, wide_ptr, bool:align = true) { 52 | if (align) { 53 | code_ptr = ((code_ptr + 15) >>> 4) << 4; 54 | } 55 | return SysreqDWide(code_ptr, wide_ptr); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /shellcode.md: -------------------------------------------------------------------------------- 1 | amx_assembly shellcode 2 | ========================================== 3 | AMX Assembly Library: Execute arbitrary x86 assembly. 4 | ------------------------------------------ 5 | 6 | 7 | 8 | ## Functions 9 | 10 | 11 | ### `RunShellcode`: 12 | 13 | 14 | #### Syntax 15 | 16 | 17 | ```pawn 18 | RunShellcode(code_ptr, align) 19 | ``` 20 | 21 | 22 | #### Parameters 23 | 24 | 25 | | **Name** | **Info** | 26 | | --- | --- | 27 | | `code_ptr` | | 28 | | `align` | `bool ` | 29 | 30 | #### Depends on 31 | * [`SysreqD`](#SysreqD) 32 | #### Estimated stack usage 33 | 5 cells 34 | 35 | -------------------------------------------------------------------------------- /stack_dump.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined STACK_DUMP_INC 22 | #endinput 23 | #endif 24 | #define STACK_DUMP_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #if !defined _samp_included 40 | #tryinclude 41 | #endif 42 | 43 | //----------------------------------------------- 44 | // Stack layout 45 | //----------------------------------------------- 46 | // what | stack pointer (STK) 47 | //----------------------------------------------- 48 | // | STP 49 | // ... | STP - 4 50 | // ... | STP - 8 51 | // ... | ... 52 | // argument_N | FRM + 4*(N+3) 53 | // ... | ... 54 | // argument_2 | FRM + 16 55 | // argument_1 | FRM + 12 56 | // 4*N | FRM + 8 57 | // RET address | FRM + 4 58 | // old FRM | FRM 59 | // | 60 | //-------------- N E W F R A M E --------------- 61 | // | 62 | // local_var_1 | FRM - 4 63 | // local_var_2 | FRM - 8 64 | // ... | ... 65 | // local_var_M | FRM - 4*M 66 | // ... | ... 67 | // ... | 8 68 | // ... | 4 69 | // | 0 70 | //----------------------------------------------- 71 | 72 | #include "amx_memory" 73 | #include "frame_info" 74 | #include "stack_trace" 75 | 76 | /// amx_assembly stack_dump 77 | stock DumpStack() 78 | { 79 | new stp = 0, stk = 0; 80 | 81 | #emit lctrl __stp 82 | #emit stor.s.pri stp 83 | #emit lctrl __stk 84 | #emit stor.s.pri stk 85 | 86 | print("------------------------"); 87 | print("Stack dump:"); 88 | print("------------------------"); 89 | 90 | // Skip locals + FRM + RETN address + paramcount. 91 | stk += 5 * cellbytes; 92 | 93 | // Don't print above the top of the stack. 94 | new i = stp; 95 | printf("[0x%08x]: TOP", i); 96 | i -= cellbytes; 97 | for ( ; i >= stk; i -= cellbytes) { 98 | printf("[0x%08x]: 0x%08x", i, ReadAmxMemory(i)); 99 | } 100 | 101 | print("------------------------"); 102 | } 103 | 104 | /// amx_assembly stack_dump 105 | stock DumpFullStack() 106 | { 107 | new stp = 0, stk = 0; 108 | 109 | #emit lctrl __stp 110 | #emit stor.s.pri stp 111 | #emit lctrl __stk 112 | #emit stor.s.pri stk 113 | 114 | // Skip locals + FRM + RETN address + paramcount (to this point in this 115 | // function, hence "3 + 2"). 116 | stk += 5 * cellbytes; 117 | 118 | print("----------------------------------------"); 119 | print("| Stack dump (bottom): |"); 120 | print("----------------------------------------"); 121 | 122 | // "next" and "previous" are going to get confusing here! We are walking UP 123 | // the stack, so all the function calls are from that POV. However, the API 124 | // we're using is from the POV of code execution with the stack growing 125 | // DOWN, so the names are reversed. 126 | new localSize = 0; 127 | new parameterSize = 0; 128 | new previousFrame = GetCurrentFrame(); 129 | new currentFrame = GetFramePreviousFrame(previousFrame); 130 | new returnAddress = 0; 131 | do 132 | { 133 | previousFrame = currentFrame; 134 | currentFrame = GetFramePreviousFrame(previousFrame); 135 | returnAddress = GetFrameReturn(previousFrame); 136 | // Still the parameter count from the previous function. 137 | //localSize = previousFrame - currentFrame + (3 * cellbytes) - parameterSize; 138 | parameterSize = GetFrameParameterSize(previousFrame); 139 | // Can be done, but slower (re-iterates from the top of the stack). 140 | localSize = GetFrameLocalSize(previousFrame); 141 | 142 | new name[64]; 143 | new address = GetFunctionFromReturnAddress(returnAddress); 144 | if (GetPublicNameFromAddress(address, name)) { 145 | printf("| %32s |", name); 146 | } else { 147 | printf("| ???? |"); 148 | } 149 | 150 | printf("| Locals (%2d): |", localSize / cellbytes); 151 | while (localSize) 152 | { 153 | printf("| %08x: %08x |", stk, ReadAmxMemory(stk)); 154 | stk += cellbytes; 155 | localSize -= cellbytes; 156 | } 157 | 158 | printf("| Frame: |"); 159 | printf("| %08x: %08x |", stk, ReadAmxMemory(stk)); 160 | stk += cellbytes; 161 | printf("| Return: |"); 162 | printf("| %08x: %08x |", stk, ReadAmxMemory(stk)); 163 | stk += cellbytes; 164 | printf("| Count: |"); 165 | printf("| %08x: %08x |", stk, ReadAmxMemory(stk)); 166 | stk += cellbytes; 167 | printf("| Parameters (%2d): |", parameterSize / cellbytes); 168 | while (parameterSize > 0) 169 | { 170 | printf("| %08x: %08x |", stk, ReadAmxMemory(stk)); 171 | stk += cellbytes; 172 | parameterSize -= cellbytes; 173 | } 174 | print("----------------------------------------"); 175 | } 176 | while (currentFrame); 177 | } 178 | 179 | -------------------------------------------------------------------------------- /stack_trace.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | #if defined STACK_TRACE_INC 22 | #endinput 23 | #endif 24 | #define STACK_TRACE_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | */ 36 | 37 | ///

38 | 39 | #if !defined _samp_included 40 | #tryinclude 41 | #endif 42 | 43 | #include "amx_base" 44 | #include "amx_header" 45 | #include "frame_info" 46 | 47 | /// amx_assembly stack_trace 48 | stock GetFunctionFromReturnAddress(ret_addr) { 49 | new addr = ret_addr - cellbytes; 50 | 51 | #emit load.s.alt addr 52 | #emit lctrl __cod 53 | #emit add 54 | #emit move.alt 55 | #emit lctrl __dat 56 | #emit sub.alt 57 | 58 | #emit stor.s.pri addr 59 | #emit lref.s.alt addr 60 | #emit lctrl __cod 61 | #emit sub.alt 62 | #emit stor.s.pri addr 63 | 64 | return addr - GetAmxBaseAddress(); 65 | } 66 | 67 | /// amx_assembly stack_trace 68 | stock GetStackTrace(trace[], skip = 0, max = sizeof(trace)) { 69 | new frm_addr = 0; 70 | #emit lctrl __frm 71 | #emit stor.s.pri frm_addr 72 | 73 | new length = 0; 74 | while (length < max) { 75 | new ret_addr = GetFrameReturn(frm_addr); 76 | if (length >= skip) { 77 | trace[length] = ret_addr; 78 | } 79 | if (ret_addr == 0) { 80 | break; 81 | } 82 | frm_addr = GetFramePreviousFrame(frm_addr); 83 | if (frm_addr == 0) { 84 | break; 85 | } 86 | length++; 87 | } 88 | 89 | return length; 90 | } 91 | 92 | /// amx_assembly stack_trace 93 | stock PrintStackTrace(trace[], max = sizeof(trace)) { 94 | print("Stack trace:"); 95 | 96 | new i = 0; 97 | 98 | for (; trace[i] != 0 && i < max - 1; i++) { 99 | new name[64]; 100 | new address = GetFunctionFromReturnAddress(trace[i + 1]); 101 | if (GetPublicNameFromAddress(address, name)) { 102 | printf(" %s[%08x]", name, trace[i]); 103 | } else { 104 | printf(" ??[%08x]", trace[i]); 105 | } 106 | } 107 | 108 | printf(" ??[%08x]", trace[i]); 109 | } 110 | 111 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | ifndef PAWNCC 2 | PAWNCC = pawncc 3 | endif 4 | 5 | INCLUDE = -i$(SAMP_SERVER_ROOT)/pawno/include -i../ 6 | 7 | AMX = amx-test.amx 8 | AMX += asm-test.amx 9 | AMX += disasm-test.amx 10 | AMX += dynamic_call-test.amx 11 | AMX += jit-test.amx 12 | AMX += phys_memory-test.amx 13 | AMX += stack_trace-test.amx 14 | 15 | all: clean $(AMX) windows 16 | 17 | amx-test.amx: amx-test.pwn 18 | $(PAWNCC) $(PFLAGS) $(INCLUDE) $^ -o$@ 19 | 20 | asm-test.amx: asm-test.pwn 21 | $(PAWNCC) $(PFLAGS) $(INCLUDE) $^ -o$@ 22 | 23 | disasm-test.amx: disasm-test.pwn 24 | $(PAWNCC) $(PFLAGS) $(INCLUDE) $^ -o$@ 25 | 26 | dynamic_call-test.amx: dynamic_call-test.pwn 27 | $(PAWNCC) $(PFLAGS) $(INCLUDE) $^ -o$@ 28 | 29 | jit-test.amx: jit-test.pwn 30 | $(PAWNCC) $(PFLAGS) $(INCLUDE) $^ -o$@ 31 | 32 | phys_memory-test.amx: phys_memory-test.pwn 33 | $(PAWNCC) $(PFLAGS) $(INCLUDE) $^ -o$@ 34 | 35 | stack_trace-test.amx: stack_trace-test.pwn 36 | $(PAWNCC) $(PFLAGS) $(INCLUDE) $^ -o$@ 37 | 38 | clean: $(SUBDIRS) 39 | rm -rf $(AMX) 40 | 41 | .PHONY: windows 42 | windows: 43 | $(MAKE) -C $@ 44 | 45 | -------------------------------------------------------------------------------- /test/all-tests.pwn: -------------------------------------------------------------------------------- 1 | #define main main_amx 2 | #include "amx-test.pwn" 3 | #undef main 4 | #define main main_asm 5 | #include "asm-test.pwn" 6 | #undef main 7 | #define main main_disasm 8 | #include "disasm-test.pwn" 9 | #undef main 10 | #define main main_dynamic_call 11 | #include "dynamic_call-test.pwn" 12 | #undef main 13 | #define main main_jit 14 | #include "jit-test.pwn" 15 | #undef main 16 | #define main main_phys_memory 17 | #include "phys_memory-test.pwn" 18 | #undef main 19 | #define main main_stack_trace 20 | #include "stack_trace-test.pwn" 21 | #undef main 22 | 23 | main() { 24 | main_amx(); 25 | main_asm(); 26 | main_disasm(); 27 | main_dynamic_call(); 28 | main_jit(); 29 | main_phys_memory(); 30 | main_stack_trace(); 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/amx-test.pwn: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "..\amx" 4 | 5 | main() { 6 | // This must output "1". 7 | printf("%d", ReadAmxCell(AMX_OFFSET_BASE) == GetAmxBaseAddress()); 8 | } 9 | -------------------------------------------------------------------------------- /test/asm-test.pwn: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "..\asm" 4 | #include "..\dynamic_call" 5 | 6 | forward HandleAsmError(ctx[AsmContext], AsmError:error); 7 | 8 | main() { 9 | // Have to use print() somewhere to make GetNativeAddressFromName() work. 10 | print("Doing #emit at runtime!"); 11 | 12 | new code[10]; 13 | new ctx[AsmContext]; 14 | 15 | AsmInit(ctx, code); 16 | AsmSetErrorHandler(ctx, GetPublicAddressFromName("HandleAsmError")); 17 | 18 | // Build a function that prints a string and returns: 19 | // 20 | // PrintString(const string[]) { 21 | // printf(string); 22 | // } 23 | // 24 | // NOTE: "print" must be called somwhere else in order to for this work! 25 | 26 | @emit proc 27 | @emit push.arg 0 28 | @emit push.num.args 1 29 | @emit sysreq "print" 30 | @emit pop.args 1 31 | @emit retn 32 | 33 | if (AsmGetError(ctx) == ASM_ERROR_NONE) { 34 | CallFunction(AsmGetCode(ctx), ref("Hello!")); 35 | } 36 | } 37 | 38 | public HandleAsmError(ctx[AsmContext]) { 39 | printf("AsmError: %d", _:AsmGetError(ctx)); 40 | } 41 | -------------------------------------------------------------------------------- /test/disasm-test.pwn: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "..\disasm" 4 | 5 | forward funny_public(); 6 | 7 | main() { 8 | DisasmDump("disasm.lst"); 9 | funny_public(); 10 | } 11 | 12 | public funny_public() { 13 | } 14 | 15 | public OnGameModeInit() { 16 | printf("Hello!"); 17 | return 1; 18 | } 19 | -------------------------------------------------------------------------------- /test/dynamic_call-test.pwn: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "..\dynamic_call" 4 | 5 | forward test(i, &j, s[], t[]); 6 | 7 | main() { 8 | new x = 456; 9 | 10 | print("public - method #1"); 11 | CallFunction(GetPublicAddressFromName("test"), 123, ref(x), ref("hell"), ref("yeah")); 12 | 13 | print("public - method #2"); 14 | Push(123); 15 | Push(ref(x)); 16 | Push(ref("hell")); 17 | Push(ref("yeah")); 18 | Call(GetPublicAddressFromName("test")); 19 | 20 | // print("native - method #1"); 21 | // CallNativeByAddress(GetNativeAddressFromName("printf"), ref("Hello, %s!"), ref("World")); 22 | 23 | // print("native - method #2"); 24 | // Push(ref("Hello, %s!")); 25 | // Push(ref("World")); 26 | // SysreqD(GetNativeAddressFromName("printf")); 27 | } 28 | 29 | public test(i, &j, s[], t[]) { 30 | printf("test: %d %d %s %s", i, j, s, t); 31 | } 32 | -------------------------------------------------------------------------------- /test/jit-test.pwn: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "..\jit" 4 | 5 | main() { 6 | printf("JIT is %spresent", IsJITPresent() ? ("") : ("not ")); 7 | } 8 | -------------------------------------------------------------------------------- /test/phys_memory-test.pwn: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "..\amx" 4 | #include "..\phys_memory" 5 | 6 | ToCharString(s[], size = sizeof(s)) { 7 | for (new i = 0; i < size; i++) { 8 | s[i] = swapchars(s[i]); 9 | } 10 | } 11 | 12 | main() { 13 | printf("AMX address: %x", GetAmxAddress()); 14 | printf("AMX base address: %x", GetAmxBaseAddress()); 15 | 16 | new s[24 char]; 17 | new s2[24]; 18 | 19 | ReadPhysMemory(0x004AB8CC, s); 20 | ToCharString(s); 21 | 22 | strunpack(s2, s); 23 | print(s2); // prints "SA-MP Dedicated Server" on SA-MP 0.3d R2 24 | } 25 | -------------------------------------------------------------------------------- /test/stack_trace-test.pwn: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "..\amx_header" 4 | #include "..\stack_trace" 5 | 6 | forward f1(); 7 | forward f3(); 8 | 9 | main() { 10 | f1(); 11 | } 12 | 13 | public f1() { 14 | f2(); 15 | } 16 | 17 | f2() { 18 | f3(); 19 | } 20 | 21 | public f3() { 22 | new stack_trace[10]; 23 | new length = GetStackTrace(stack_trace); 24 | PrintStackTrace(stack_trace, length); 25 | #emit halt 1 26 | } 27 | -------------------------------------------------------------------------------- /test/windows/Makefile: -------------------------------------------------------------------------------- 1 | ifndef PAWNCC 2 | PAWNCC=pawncc 3 | endif 4 | 5 | INCLUDE = -i$(SAMP_SERVER_ROOT)/pawno/include -i../../ -i../../windows 6 | 7 | AMX = ShellExecute-test.amx 8 | 9 | all: clean $(AMX) 10 | 11 | ShellExecute-test.amx: ShellExecute-test.pwn 12 | $(PAWNCC) $(PFLAGS) $(INCLUDE) $^ -o$@ 13 | 14 | clean: 15 | rm -rf $(AMX) 16 | -------------------------------------------------------------------------------- /test/windows/ShellExecute-test.pwn: -------------------------------------------------------------------------------- 1 | #include "windows/ShellExecute" 2 | 3 | static stock ToCharString(s[], size = sizeof(s)) { 4 | for (new i = 0; i < size; i++) { 5 | s[i] = swapchars(s[i]); 6 | } 7 | } 8 | 9 | main() { 10 | new File[] = !"notepad.exe"; 11 | new Operation[] = !"open"; 12 | new Parameters[] = !"server.cfg"; 13 | 14 | ToCharString(File); 15 | ToCharString(Operation); 16 | ToCharString(Parameters); 17 | 18 | new result = ShellExecute(Operation, File, Parameters, SW_SHOW); 19 | printf("ShellExecute() returned %d", result); 20 | } 21 | -------------------------------------------------------------------------------- /windows/ShellExecute.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | // this software and associated documentation files (the "Software"), to deal in 5 | // the Software without restriction, including without limitation the rights to 6 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | // of the Software, and to permit persons to whom the Software is furnished to do 8 | // so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #if defined SHELL_EXECUTE_INC 22 | #endinput 23 | #endif 24 | #define SHELL_EXECUTE_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx 35 | * 36 | */ 37 | 38 | ///

39 | 40 | #include "import_table" 41 | #include "..\amx_header" 42 | #include "..\amx_memory" 43 | #include "..\dynamic_call" 44 | #include "..\phys_memory" 45 | #include "..\shellcode" 46 | 47 | #define SW_HIDE (0) 48 | #define SW_NORMAL (1) 49 | #define SW_MAXIMIZE (3) 50 | #define SW_MINIMIZE (6) 51 | #define SW_RESTORE (9) 52 | #define SW_SHOW (5) 53 | #define SW_SHOWDEFAULT (10) 54 | #define SW_SHOWMAXIMIZED (3) 55 | #define SW_SHOWMINIMIZED (2) 56 | #define SW_SHOWMINNOACTIVE (7) 57 | #define SW_SHOWNA (8) 58 | #define SW_SHOWNOACTIVATE (4) 59 | #define SW_SHOWNORMAL (1) 60 | 61 | // (0) // The operating system is out of memory or resources. 62 | #define ERROR_FILE_NOT_FOUND (2) // The specified file was not found. 63 | #define ERROR_PATH_NOT_FOUND (3) // The specified path was not found. 64 | #define ERROR_BAD_FORMAT (11) // The .exe file is invalid (non-Win32 .exe or error in .exe image). 65 | #define SE_ERR_ACCESSDENIED (5) // The operating system denied access to the specified file. 66 | #define SE_ERR_ASSOCINCOMPLETE (27) // The file name association is incomplete or invalid. 67 | #define SE_ERR_DDEBUSY (30) // The DDE transaction could not be completed because other DDE transactions were being processed. 68 | #define SE_ERR_DDEFAIL (29) // The DDE transaction failed. 69 | #define SE_ERR_DDETIMEOUT (28) // The DDE transaction could not be completed because the request timed out. 70 | #define SE_ERR_DLLNOTFOUND (32) // The specified DLL was not found. 71 | #define SE_ERR_FNF (2) // The specified file was not found. 72 | #define SE_ERR_NOASSOC (31) // There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable. 73 | #define SE_ERR_OOM (8) // There was not enough memory to complete the operation. 74 | #define SE_ERR_PNF (3) // The specified path was not found. 75 | #define SE_ERR_SHARE (26) // A sharing violation occurred. 76 | 77 | /// amx_assembly Windows ShellExecute 78 | /// NOTE: string arguments must be prepared with ToCharString() or similar function. 79 | stock ShellExecute(const Operation[], const File[], const Parameters[], ShowCmd) { 80 | /* 81 | .text:10001000 55 push ebp 82 | .text:10001001 8B EC mov ebp, esp 83 | .text:10001003 8B 45 0C mov eax, [ebp+arg_4] 84 | .text:10001006 8B 48 18 mov ecx, [eax+18h] 85 | .text:10001009 51 push ecx ; nShowCmd 86 | .text:1000100A 8B 55 0C mov edx, [ebp+arg_4] 87 | .text:1000100D 8B 42 14 mov eax, [edx+14h] 88 | .text:10001010 50 push eax ; lpDirectory 89 | .text:10001011 8B 4D 0C mov ecx, [ebp+arg_4] 90 | .text:10001014 8B 51 10 mov edx, [ecx+10h] 91 | .text:10001017 52 push edx ; lpParameters 92 | .text:10001018 8B 45 0C mov eax, [ebp+arg_4] 93 | .text:1000101B 8B 48 0C mov ecx, [eax+0Ch] 94 | .text:1000101E 51 push ecx ; lpFile 95 | .text:1000101F 8B 55 0C mov edx, [ebp+arg_4] 96 | .text:10001022 8B 42 08 mov eax, [edx+8] 97 | .text:10001025 50 push eax ; lpOperation 98 | .text:10001026 8B 4D 0C mov ecx, [ebp+arg_4] 99 | .text:10001029 8B 51 04 mov edx, [ecx+4] 100 | .text:1000102C 52 push edx ; hwnd 101 | .text:1000102D FF 15 78 56 34 12 call ds:ShellExecuteA ; Opens or prints a specified file 102 | .text:10001033 5D pop ebp 103 | .text:10001034 C3 retn 104 | */ 105 | 106 | #define __asm(%0,%1,%2,%3) (((0x%3) << 24) | ((0x%2) << 16) | (0x%1 << 8) | (0x%0)) 107 | 108 | static const asm[] = { 109 | __asm(90,90,90,90), 110 | __asm(90,90,90,90), 111 | __asm(90,90,90,90), 112 | __asm(90,90,90,90), 113 | __asm(55,8B,EC,8B), 114 | __asm(45,0C,8B,48), 115 | __asm(18,51,8B,55), 116 | __asm(0C,8B,42,14), 117 | __asm(50,8B,4D,0C), 118 | __asm(8B,51,10,52), 119 | __asm(8B,45,0C,8B), 120 | __asm(48,0C,51,8B), 121 | __asm(55,0C,8B,42), 122 | __asm(08,50,8B,4D), 123 | __asm(0C,8B,51,04), 124 | __asm(52,FF,15,00), 125 | __asm(00,00,00,5D), 126 | __asm(C3,CC,CC,CC) 127 | }; 128 | 129 | // #undef __ 130 | 131 | new address = GetImportAddress("ShellExecuteA"); 132 | WriteAmxMemory(ref(asm) + 63, refabs(address)); 133 | 134 | Push(0); // HWND hwnd 135 | Push(refabs(Operation)); // LPCTSTR lpOperation 136 | Push(refabs(File)); // LPCTSTR lpFile 137 | Push(refabs(Parameters)); // LPCTSTR lpParameters 138 | Push(0); // LPCTSTR lpDirectory 139 | Push(ShowCmd); // INT nShowCmd 140 | 141 | new wide; 142 | address = refwide(address, wide); 143 | return RunShellcodeWide(address, wide); 144 | } 145 | 146 | -------------------------------------------------------------------------------- /windows/import_table.inc: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2012 Zeex 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | // this software and associated documentation files (the "Software"), to deal in 5 | // the Software without restriction, including without limitation the rights to 6 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | // of the Software, and to permit persons to whom the Software is furnished to do 8 | // so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #if defined IMPORT_TABLE_INC 22 | #endinput 23 | #endif 24 | #define IMPORT_TABLE_INC 25 | 26 | /** 27 | * 28 | *

29 | * This library uses the enhanced pawndoc.xsl from 30 | * pawn-lang/pawndoc. 31 | * This XSL has features such as library and markdown support, and will not 32 | * render this message when used. 33 | * 34 | * 35 | * Helpful resources: 36 | * 41 | * 42 | * 43 | */ 44 | 45 | ///

46 | 47 | #include 48 | #include "..\phys_memory" 49 | 50 | /// amx_assembly Windows import_table 51 | const DefaultImageBase = 0x00400000; 52 | 53 | /// amx_assembly Windows import_table 54 | const SizeOfFileHeader = 0x14; 55 | 56 | /// amx_assembly Windows import_table 57 | const SizeOfOptionalHeader = 0xE0; 58 | 59 | /// amx_assembly Windows import_table 60 | const SizeOfImportDirectory = 0x14; 61 | 62 | /// amx_assembly Windows import_table 63 | stock GetImportPointer(const name[]) { 64 | new DosHeader = DefaultImageBase; 65 | new NtHeaders = DosHeader + ReadDword(DosHeader, 0x3C); 66 | 67 | new FileHeader = NtHeaders + 0x04; 68 | new OptionalHeader = FileHeader + SizeOfFileHeader; 69 | 70 | new ImageBase = ReadDword(OptionalHeader, 0x1C); 71 | 72 | new ImportTableRva = ReadDword(OptionalHeader, 0x68); 73 | new ImportDirectories = ImageBase + ImportTableRva; 74 | 75 | for (new i = 0; ; i++) { 76 | new ImportDirectory = ImportDirectories + i * SizeOfImportDirectory; 77 | 78 | new Name = ReadDword(ImportDirectory, 0x0C); 79 | if (Name == 0) 80 | break; 81 | 82 | new ImportLookupTable = ImageBase + ReadDword(ImportDirectory, 0x00); 83 | new ImportAddressTable = ImageBase + ReadDword(ImportDirectory, 0x10); 84 | 85 | for (new j = 0 ; ; j++) { 86 | new NameOrdinal = ReadDword(ImportLookupTable, j * 4); 87 | 88 | new bool:NameOrdinalFlag = (NameOrdinal & 0x80000000) != 0; 89 | if (NameOrdinalFlag) 90 | continue; 91 | 92 | new ImportByName = NameOrdinal & ~0x80000000; 93 | if (ImportByName == 0) 94 | break; 95 | 96 | new iname[256]; 97 | ReadString(ImageBase, ImportByName + 2, iname); 98 | 99 | if (strcmp(iname, name) == 0) { 100 | return ImportAddressTable + j * 4; 101 | } 102 | } 103 | } 104 | 105 | return 0; 106 | } 107 | 108 | /// amx_assembly Windows import_table 109 | ///

Finds a function in the Import Table and returns its address or 0 if found nothing. 110 | stock GetImportAddress(const name[]) { 111 | new ImportPointer = GetImportPointer(name); 112 | 113 | if (ImportPointer != 0) { 114 | return ReadDword(ImportPointer, 0); 115 | } 116 | 117 | return 0; 118 | } 119 | 120 | /// amx_assembly Windows import_table 121 | static stock ToCharString(s[], size = sizeof(s)) { 122 | for (new i = 0; i < size; i++) { 123 | s[i] = swapchars(s[i]); 124 | } 125 | } 126 | 127 | /// amx_assembly Windows import_table 128 | static stock ReadDword(base, offset = 0) { 129 | return ReadPhysMemoryCell(base + offset); 130 | } 131 | 132 | /// amx_assembly Windows import_table 133 | static stock ReadString(base, offset = 0, dest[], size = sizeof(dest)) { 134 | ReadPhysMemory(base + offset, dest, size); 135 | ToCharString(dest, size); 136 | strunpack(dest, dest, size); 137 | } 138 | 139 | --------------------------------------------------------------------------------