diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..067d9a2 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,11 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.166.1/containers/ubuntu/.devcontainer/base.Dockerfile + +# [Choice] Ubuntu version: bionic, focal +ARG VARIANT="focal" +FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} + +# [Optional] Uncomment this section to install additional OS packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends build-essential + + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..3521ef9 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,29 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.166.1/containers/ubuntu +{ + "name": "Ubuntu", + "build": { + "dockerfile": "Dockerfile", + // Update 'VARIANT' to pick an Ubuntu version: focal, bionic + "args": { "VARIANT": "focal" } + }, + + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-vscode.cpptools" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "uname -a", + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..85dd98f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +# use unix lf line ending for everything except windows bat files +* text=auto eol=lf + +# libraries are bin +*.o binary +*.obj binary +*.a binary +*.so binary +*.lib binary +*.dll binary +*.pdb binary + + +# source files explicitly +*.{c,h,asm,S} text eol=lf + +*.cmd text eol=crlf +*.bat text eol=crlf + +# Visulal studio is happier with crlf +*.sln text eol=crlf +*.vcxproj text eol=crlf +*.vcxproj.filters text eol=crlf diff --git a/.github/workflows/buildcommit.yml b/.github/workflows/buildcommit.yml new file mode 100644 index 0000000..d858b1f --- /dev/null +++ b/.github/workflows/buildcommit.yml @@ -0,0 +1,108 @@ +name: build test and commit + +on: + push: + branches: [ master, dev ] + + pull_request: + branches: [ master ] + +jobs: + + build-linux-gnu: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: [AMD64, i386, arm, aarch64] + include: + - name: i386 + platformflags: -m32 + - name: arm + platformtools: arm-linux-gnueabi + emulator: qemu-arm + - name: aarch64 + platformtools: aarch64-linux-gnu + emulator: qemu-aarch64 + # name: build-linux-gnu (${{matrix.name}}) + env: + PLATFORMFLAGS: ${{matrix.platformflags}} + PLATFORM_PREFIX: ${{matrix.platformtools}} + EMULATOR: ${{matrix.emulator}} + steps: + - uses: actions/checkout@v4 + - name: install multilib + run: sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib + if: ${{ matrix.name == 'i386' }} + - name: install qemu + if: matrix.emulator + run: sudo apt-get install --no-install-recommends -y qemu-user + - name: install abi lib + if: matrix.platformtools + run: sudo apt-get install --no-install-recommends -y gcc-${{matrix.platformtools}} g++-${{matrix.platformtools}} + - name: make + run: make all + - name: test + run: make test + - name: set abi name + run: echo abiname=$(make abiname) >> $GITHUB_ENV + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ env.abiname }} + path: lib/${{ env.abiname }}/libstackman.a + + build-windows: + runs-on: windows-latest + strategy: + fail-fast: true + matrix: + platform: [x86, x64, arm, arm64] + include: + - platform: x86 + folder: Win32 + native: yes + - platform: x64 + folder: x64 + native: yes + - platform: arm + folder: arm + - platform: arm64 + folder: arm64 + + steps: + - uses: actions/checkout@v4 + - uses: microsoft/setup-msbuild@v2 + - name: build + run: msbuild.exe vs2022\stackman.sln /p:Platform=${{matrix.platform}} + - name: strip timestamps from lib + run: python tools/strip-lib.py lib/win_${{matrix.platform}}/stackman.lib + - name: rebuild after stripping + run: msbuild.exe vs2022\stackman.sln /p:Platform=${{matrix.platform}} + - name: test + if: ${{ matrix.native == 'yes' }} + run: vs2022\${{matrix.folder}}\Debug\test.exe + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: win_${{ matrix.platform }} + path: lib/win_${{matrix.platform}}/stackman.lib + + commit-artifacts: + runs-on: ubuntu-latest + needs: [build-linux-gnu, build-windows] + if: ${{ github.event_name == 'push' }} + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + path: lib + + - name: Commit changes + run: | + git config --global user.name 'Automation tool' + git config --global user.email 'automation-tool@users.noreply.github.com' + git add lib/*.a lib/*.lib + git status + git diff-index --quiet HEAD || git commit -m "Automated build" + git push diff --git a/.gitignore b/.gitignore index f50aad5..e9cdfd3 100644 --- a/.gitignore +++ b/.gitignore @@ -42,14 +42,22 @@ *.idb *.pdb -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf # custom bin/ +tmp/ +gen_asm.s + +# Visual Studio noise +# Visual Studio 2015/2017 cache/options directory +.vs/ +*.user +# Build results +[Dd]ebug/ + +# Allow our release libraries +!lib/**/*.lib +!lib/**/*.a + +# vs code +.vscode/ \ No newline at end of file diff --git a/Makefile b/Makefile index 93b0fef..20518c2 100644 --- a/Makefile +++ b/Makefile @@ -1,50 +1,62 @@ -CPPFLAGS += -Isrc $(PLATFORM) -CFLAGS += -fPIC -g $(PLATFORM) -CXXFLAGS += -fPIC -g $(PLATFORM) -LDFLAGS += -L$(LIB) -g $(PLATFORM) +CPPFLAGS += -Istackman $(PLATFORMFLAGS) +CFLAGS += -fPIC -g $(PLATFORMFLAGS) +CXXFLAGS += -fPIC -g $(PLATFORMFLAGS) +LDFLAGS += -L$(LIB) -g $(PLATFORMFLAGS) # add flag to disable Intel CET NO_CET := $(shell ./disable_cet $(CC)) CFLAGS += $(NO_CET) CXXFLAGS += $(NO_CET) +OLDCC := $(CC) +ifdef PLATFORM_PREFIX +CC = $(PLATFORM_PREFIX)-gcc +CXX = $(PLATFORM_PREFIX)-g++ +LD = $(PLATFORM_PREFIX)-ld +AR = $(PLATFORM_PREFIX)-ar +endif # run c preprocessor with any cflags to get cross compilation result, then run regular compile in native -ABI := $(shell mkdir -p bin; $(CC) -E $(CFLAGS) $(CPPFLAGS) -o bin/get_abi.c get_abi.c && $(CC) -o bin/get_abi bin/get_abi.c && bin/get_abi) +ABI := $(shell sh tools/abiname.sh "$(CC)" "$(CFLAGS)") ifndef ABI $(error Could not determine platform) -else -$(info ABI is $(ABI)) endif LIB := lib/$(ABI) all: $(LIB)/libstackman.a -obj = src/stackman.o src/stackman_s.o +# echo the abiname, for build tools. +.PHONY: abiname +abiname: + @echo $(ABI) + +obj = stackman/stackman.o stackman/stackman_s.o $(LIB)/libstackman.a: lib $(obj) + $(info ABI is $(ABI)) $(AR) $(ARFLAGS) -s $@ $(obj) .PHONY: lib clean lib: - mkdir -p $(LIB) + mkdir -p $(LIB) bin clean: - rm -f src/*.o tests/*.o - rm -f bin/* + rm -f stackman/*.o tests/*.o + rm -f bin/* + rm -rf tmp tools/tmp DEBUG = #-DDEBUG_DUMP .PHONY: test tests test: tests - bin/test - bin/test_cc - bin/test_static - bin/test_asm + $(EMULATOR) bin/test + $(EMULATOR) bin/test_cc + $(EMULATOR) bin/test_static + $(EMULATOR) bin/test_asm @echo "*** All test suites passed ***" tests: bin/test @@ -54,13 +66,13 @@ tests: bin/test_asm tests: LDLIBS := -lstackman bin/test: tests/test.o $(LIB)/libstackman.a - $(CC) $(LDFLAGS) -o $@ $< ${DEBUG} $(LDLIBS) + $(CC) $(LDFLAGS) -static -o $@ $< ${DEBUG} $(LDLIBS) bin/test_cc: tests/test_cc.o $(LIB)/libstackman.a - $(CXX) $(LDFLAGS) -o $@ $< ${DEBUG} $(LDLIBS) + $(CXX) $(LDFLAGS) -static -o $@ $< ${DEBUG} $(LDLIBS) bin/test_static: tests/test_static.o - $(CC) $(LDFLAGS) -o $@ $^ ${DEBUG} + $(CC) $(LDFLAGS) -static -o $@ $^ ${DEBUG} bin/test_asm: tests/test_asm.o tests/test_asm_s.o - $(CC) $(LDFLAGS) -o $@ $^ ${DEBUG} + $(CC) $(LDFLAGS) -static -o $@ $^ ${DEBUG} diff --git a/README.md b/README.md index 07254b3..36e4c46 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ +[![build test and commit](https://github.com/kristjanvalur/stackman/actions/workflows/buildcommit.yml/badge.svg)](https://github.com/kristjanvalur/stackman/actions/workflows/buildcommit.yml) + # stackman + Simple low-level stack manipulation API and implementation for common platforms ## Purpose + This library aims to provide a basic API to perfom stack manipulation on various platforms. Stack manipulation involves changing the machine stack pointer while optionally saving and restoring the stack contents. @@ -27,45 +31,58 @@ Additionally, it provides a set of pre-assembled libraries for the most common platforms so that no assembly steps are required by users. ## Features + - Simple api - - `stackman_switch()` is the main function. + + - `stackman_switch()` and `stackman_call()` are the only functions. - The caller provides a callback and context pointer to customize behaviour. - - The callback can save the stack and provide the new stack pointer. - - After the switch, the callback can restore contents of new stack. - - Application behaviour is entirely defined by the callback. + - Simple implementation + - The code involving assembly is as simple as possible, allowing for straightforward implementation on most platforms. - Complex logic and branching is delegated to the C callback. - - Custom platform code must only do three things: + - Custom platform assembly code must only do three things: 1. Save and restore volatile registers and stack state on the stack 2. Call the callback twice with the current stack pointer 3. Set the stack pointer to the value returned by the first callback. -- Assembly support + +- Assembly support + The straightforward and application-agnostic switching allows the switching function to be implemented in full assembler. This removes the risk of inline-assembler doing any sort of unexpected things such as in-lining the function or otherwise change the assumptions that the function makes about its environment. This assembly code can be created by the in-line assembler in a controlled environment. + - No dependencies + The library merely provides stack switching. It consist only of a couple of functions with no dependencies. + - Stable + There is no need to add or modify functionality. + - Libraries provided. + The aim is to provide pre-assembled libraries for the most popular platforms. This relieves other tools that want to do stack manipulation from doing any sort of assembly or complex linkage. Just include the headers and link to the appropriate library. ## Supported platforms + The current code is distilled out of other work, with the aim of simplifying and standardizing the api. A number of ABI specifications is supported, meaning architecture and calling convention, plus archive format: - - microsoft_cdecl (32 bits) - - microsoft_x64 + + - win_x86 (32 bits) + - win_x64 + - win_ARM64 (experimental) - sysv_i386 (linux) - sysv_amd64 (linux) - AAPCS (32 bit arm) - AAPCS64 (64 bit arm) -Supported toolchains: +### Supported toolchains: + - Gnu C - clang - Microsoft Visual Studio @@ -73,32 +90,133 @@ Supported toolchains: Other platforms can be easily adapted from both existing implementations for other projects as well as from example code provided. -### Intel CET -Intel's Conontrol-Flow Enforcement Technology is incompatible with stack switching -because it imploys a secondary Shadow Stack, that the user-mode program cannot -modify. Unexpected return flow after a stack switch would cause the processor -to fault. Because of this, we need to mark any assembly code as not CET compatible. Modern compilers are beginning to generate CET compatible objects and -once supporting CPUs start to arrive, processes which consist entirely of CET compatible code may be run in such a protected environment. See https://software.intel.com/content/www/us/en/develop/articles/technical-look-control-flow-enforcement-technology.html for more information - +## API + +There are two functions that make up the stackman library: `stakman_switch()` and `stackman_call()` who +both take a `stackman_cb_t` callback: + +```C +typedef void *(*stackman_cb_t)( + void *context, int opcode, void *stack_pointer); +void *stackman_switch(stackman_cb_t callback, void *context); +void *stackman_call(stackman_cb_t callback, void *context, void *stack); +``` + +### stackman_switch() + +This is the main _stack manipulation_ API. When called, it will call `callback` function twice: + +1. First it calls it with the current opcode `STACKMAN_OP_SAVE`, passing the current `stack_pointer` to +the callback. This gives the callback the opportunity to _save_ the stack data somewhere. The callback +can then return a **different** stack pointer. +2. It takes the returned value from the calback and replaces the CPU _stack pointer_ with it. +3. It calls the callback a second time, with the opcode `STACKMAN_OP_RESTORE` and the new stack pointer. +This gives the callback the opportunity to replace the data on the stack with previously saved data. +4. It returns the return value from the second call to the callback function. + +The `context` pointer is passed as-is to the callback, allowing it access to user-defined data. + +Depending on how the callback function is implemented, this API can be used for a number of things, like +saving a copy of the stack, perform a stack switch, query the stack pointer, and so on. + +### stackman_call() + +This is a helper function to call a callback function, optionally providing it with a different stack to +use. + +1. It saves the current CPU stack pointer. If `stack` is non-zero, it will replace the stackpointer +with that value. +2. It calls the callback function with the opcode `STACKMAN_OP_CALL`. +3. It replaces the stack pointer with the previously saved value and returns the return value from the callback. + +This function is useful for at least three things: + +- To move the call chain into a custom stack area, some heap-allocated block, for example. +- To query the current stack pointer +- To enforce an actual function call with stack pointer information. + +The last feature is useful to bypass any in-lining that a compiler may do, when one really wants +a proper function call with stack, for example, when setting up a new stack entry point. + ## Usage + - Include `stackman.h` for a decleration of the `stackman_switch()` function and the definition of various platform specific macros. See the documentation in the header file for the various macros. - Implement switching semantics via the callback and call `stackman_switch()` from your program as appropriate. See tests/test.c for examples. -There are two basic ways to add the library to your project: +There are two basic ways to add the library to your project: Using a static library or inlining the code. + ### static library (preferred) + - You link with the `libstackman.a` or `stackman.lib` libraries provided for your platform. ### inlined code + - You inlude `stackman_impl.h` in one of your .c source files to provide inline assembly. - You include `stackman_impl.h` in an assembly (.S) file in your project to include assembly code. - (windows) You include `stackman_s.asm` in an assemby (.asm) file in your project. In the case of inlined code, it can be specified to prefer in-line assembly and static linkage over separate assembly language source. +## Development + +### Adding new platforms + +1. Modify `platform.h` to identif the platform environment. Define an ABI name and + include custom header files. +2. Use the `switch_template.h` to help build a `switch_ABI.h` file for your ABI. +3. Provide an assembler version, `switch_ABI.S` by compiling the `gen_asm.c` file for your platform. +4. Provide cross-compilation tools for linux if possible, by modifying the `Makefile` + +### Cross-compilation + +Linux on x86-64 can be used to cross compile for x86 and ARM targets. This is most useful to generate assembly code, e.g. when compiling +stackman/platform/gen_asm.c + + - x86 requires the -m32 flag to compilers and linkers. + - arm32 requires to use the arm-linux-gnueabi-* tools, including cc and linker + - aarch64 requires the aarch64-linux-gnu-* tools. + +The x86 tools require the **gcc-multilib** and **g++-multilib** packages to be installed. They, however, can't co-exist with the **gcc-arm-linux-gnueabi** or +**gcc-aarch64-linux-gnu** packages on some distributions, and so development for these +platforms may need to be done independently. + +#### Cross compiling for x86 (32 bit) on Linux + + - install __gcc-multilib__ and __g++-multilib__ + - *compile* **gen_asm.c** using `gcc -m32` + - *make* using `make PLATFORMFLAGS=-m32 test` + +#### Cross compiling for ARM (32 bit) on Linux + + - install __gcc-arm-linux-gnueabi__ and __g++-arm-linux-gnueabi__ + - install __qemu-user__ for hardware emulation + - *compile* **gen_asm.c** using `arm-linux-gnueabi-gcc` + - *make* using `make PLATFORM_PREFIX=arm-linux-gnueabi- EMULATOR=qemu-arm test` + +#### Cross compiling for Arm64 on Linux + + - install **gcc-aarch64-linux-gnu** and **g++-aarch64-linux-gnu** + - install __qemu-user__ for hardware emulation + - *compile* using `aarch64-linux-gnu-gcc` + - *make* using `make PLATFORM_PREFIX=aarch64-linux-gnu- EMULATOR=qemu-aarch64 test` + +## A note about Intel CET + +Intel's *Control-Flow Enforcement Technology* is incompatible with stack switching +because it employs a secondary *Shadow Stack*, that the user-mode program cannot +modify. Unexpected return flow after a stack switch would cause the processor +to fault. Because of this, we need to mark any assembly code as **not CET compatible** by +adding special compiler flags to supporting compilers (currently modern GNU C). +Modern compilers are beginning to generate CET compatible objects and +once supporting CPUs start to arrive, processes which consist entirely of CET compatible +code may be run in such a protected environment. +See https://software.intel.com/content/www/us/en/develop/articles/technical-look-control-flow-enforcement-technology.html for more information + ## History + This works was originally inspired by *Stackless Python* by [Christian Tismer](https://github.com/ctismer), where the original switching code was developed. @@ -106,16 +224,6 @@ Later projects, like *gevent/greenlet* have taken that idea and provided additio with a different implementation, making the switching code itself incompatible. Our work on additional stack-manipulating libraries prompted us to try to distill this functionality in its -rawest form into a separate, low-level, library. Such that any project, withing to implement *co-routine*-like +rawest form into a separate, low-level, library. Such that any project, wishing to implement *co-routine*-like behaviour on the C-stack level, could make use of simple, stable code, that can be easily extended for additional platforms as they come along. - -## Cross-compilation -Linux on x86-64 can be used to cross compile for x86 and ARM targets. This is most useful to generate assembly code, e.g. when compiling src/platform/test.c - - x86 requires the -m32 flag to compilers and linkers. - - arm32 requires to use the arm-linux-gnueabi-* tools, including cc and linker - - aarch64 requires the aarch64-linux-gnu-* tools. - -The x86 tools require the **gcc-multilib** and **g++-multilib** packages to be installed. They, however, can't co-exist with the **gcc-arm-linux-gnueabi** or -**gcc-aarch64-linux-gnu** packages on some distributions, and so development for these -platforms may need to be done independently. \ No newline at end of file diff --git a/lib/aarch64/libstackman.a b/lib/aarch64/libstackman.a new file mode 100644 index 0000000..c30dcd0 Binary files /dev/null and b/lib/aarch64/libstackman.a differ diff --git a/lib/arm32/libstackman.a b/lib/arm32/libstackman.a new file mode 100644 index 0000000..426030f Binary files /dev/null and b/lib/arm32/libstackman.a differ diff --git a/lib/sysv_amd64/libstackman.a b/lib/sysv_amd64/libstackman.a new file mode 100644 index 0000000..8b38241 Binary files /dev/null and b/lib/sysv_amd64/libstackman.a differ diff --git a/lib/sysv_i386/libstackman.a b/lib/sysv_i386/libstackman.a new file mode 100644 index 0000000..1d91d87 Binary files /dev/null and b/lib/sysv_i386/libstackman.a differ diff --git a/lib/win_arm/stackman.lib b/lib/win_arm/stackman.lib new file mode 100644 index 0000000..0b34d11 Binary files /dev/null and b/lib/win_arm/stackman.lib differ diff --git a/lib/win_arm64/stackman.lib b/lib/win_arm64/stackman.lib new file mode 100644 index 0000000..eb3917c Binary files /dev/null and b/lib/win_arm64/stackman.lib differ diff --git a/lib/win_x64/stackman.lib b/lib/win_x64/stackman.lib new file mode 100644 index 0000000..f51e16e Binary files /dev/null and b/lib/win_x64/stackman.lib differ diff --git a/lib/win_x86/stackman.lib b/lib/win_x86/stackman.lib new file mode 100644 index 0000000..90c292e Binary files /dev/null and b/lib/win_x86/stackman.lib differ diff --git a/src/platforms/test.c b/src/platforms/test.c deleted file mode 100644 index 753157e..0000000 --- a/src/platforms/test.c +++ /dev/null @@ -1,11 +0,0 @@ -/* function to test the generation - * of assembly code under e.g. Gcc. - * use by calling, for example: - * cc -DSTACKMAN_SWITCH_IMPL -S -m32 -fcf-protection=none test.c - * and examinine the generated test.s assembly code. The fcf-protection - * flag disables generation of intel CET compatible code, but stack switching - * is not compatible with the proposed shadow stack. - */ - -#define STACKMAN_VERBOSE -#include "platform.h" diff --git a/src/extras/slp_switch.h b/stackman/extras/slp_switch.h similarity index 100% rename from src/extras/slp_switch.h rename to stackman/extras/slp_switch.h diff --git a/stackman/platforms/gen_asm.c b/stackman/platforms/gen_asm.c new file mode 100644 index 0000000..bd5785a --- /dev/null +++ b/stackman/platforms/gen_asm.c @@ -0,0 +1,14 @@ +/* function to test the generation + * of assembly code under e.g. Gcc. + * use by calling, for example: + * cc -S -m32 -fcf-protection=none gen_asm.c + * and examinine the generated gen_asm.s assembly code. + * -m32 selects 32 bit mode, use other directives to select a different platform. + * The -fcf-protection flag disables generation of intel CET compatible code, but stack switching + * is not compatible with the proposed shadow stack. Only the latest compilers have it. + */ + +#define STACKMAN_VERBOSE +#define STACKMAN_INLINE_ASM 1 +#define STACKMAN_BUILD_LIB /* so that we don´t generate indirection shims */ +#include "../stackman_impl.h" diff --git a/src/platforms/platform.h b/stackman/platforms/platform.h similarity index 87% rename from src/platforms/platform.h rename to stackman/platforms/platform.h index 8242603..b797de9 100644 --- a/src/platforms/platform.h +++ b/stackman/platforms/platform.h @@ -32,11 +32,19 @@ #if defined(_M_IX86) #include "switch_x86_msvc.h" /* MS Visual Studio on X86 */ #define _STACKMAN_PLATFORM x86_msvc -#define _STACKMAN_ABI microsoft_cdecl +#define _STACKMAN_ABI win_x86 #elif defined(_M_X64) -#include "switch_x86_msvc.h" /* MS Visual Studio on X64 */ +#include "switch_x64_msvc.h" /* MS Visual Studio on X64 */ #define _STACKMAN_PLATFORM x64_msvc -#define _STACKMAN_ABI microsoft_x64 +#define _STACKMAN_ABI win_x64 +#elif defined(_M_ARM) +#include "switch_arm_msvc.h" /* MS Visual Studio on ARM */ +#define _STACKMAN_PLATFORM arm_msvc +#define _STACKMAN_ABI win_arm +#elif defined(_M_ARM64) +#include "switch_arm64_msvc.h" /* MS Visual Studio on ARM */ +#define _STACKMAN_PLATFORM arm64_msvc +#define _STACKMAN_ABI win_aarch64 #endif diff --git a/src/platforms/switch_aarch64_gcc.S b/stackman/platforms/switch_aarch64_gcc.S similarity index 83% rename from src/platforms/switch_aarch64_gcc.S rename to stackman/platforms/switch_aarch64_gcc.S index 70fba4b..5b0406a 100644 --- a/src/platforms/switch_aarch64_gcc.S +++ b/stackman/platforms/switch_aarch64_gcc.S @@ -5,12 +5,12 @@ * implementation that is independent of subtleties of inline * assembly code which may change with compiler versions. * The file is generated using - * "aarch64-linux-gnu-gcc -DSTACKMAN_SWITCH_IMPL -S test.c" - * and then copying the code from test.s into this file. + * "aarch64-linux-gnu-gcc -S gen_asm.c" + * and then copying the code from gen_asm.s into this file. * */ .arch armv8-a - .file "test.c" + .file "gen_asm.c" .text .align 2 .global stackman_switch @@ -53,7 +53,7 @@ stackman_switch: mov x3, x0 mov x0, x1 #APP -// 57 "switch_aarch64_gcc.h" 1 +// 59 "../platforms/switch_aarch64_gcc.h" 1 mov x2, sp // 0 "" 2 #NO_APP @@ -63,7 +63,7 @@ stackman_switch: blr x3 mov x2, x0 #APP -// 61 "switch_aarch64_gcc.h" 1 +// 63 "../platforms/switch_aarch64_gcc.h" 1 mov sp, x0 // 0 "" 2 #NO_APP @@ -118,24 +118,28 @@ stackman_call: mov x29, sp str x19, [sp, 16] .cfi_offset 19, -16 - mov x3, x0 + mov x4, x0 mov x0, x1 - mov x1, x2 + mov x3, x2 #APP -// 77 "switch_aarch64_gcc.h" 1 +// 78 "../platforms/switch_aarch64_gcc.h" 1 mov x19, sp // 0 "" 2 -// 78 "switch_aarch64_gcc.h" 1 +// 79 "../platforms/switch_aarch64_gcc.h" 1 mov x2, sp // 0 "" 2 -// 82 "switch_aarch64_gcc.h" 1 - mov sp, x1 +#NO_APP + cbz x3, .L4 +#APP +// 84 "../platforms/switch_aarch64_gcc.h" 1 + mov sp, x3 // 0 "" 2 #NO_APP +.L4: mov w1, 2 - blr x3 + blr x4 #APP -// 86 "switch_aarch64_gcc.h" 1 +// 88 "../platforms/switch_aarch64_gcc.h" 1 mov sp, x19 // 0 "" 2 #NO_APP @@ -149,5 +153,5 @@ stackman_call: .cfi_endproc .LFE1: .size stackman_call, .-stackman_call - .ident "GCC: (Ubuntu 9.3.0-10ubuntu1) 9.3.0" + .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" .section .note.GNU-stack,"",@progbits diff --git a/src/platforms/switch_aarch64_gcc.h b/stackman/platforms/switch_aarch64_gcc.h similarity index 91% rename from src/platforms/switch_aarch64_gcc.h rename to stackman/platforms/switch_aarch64_gcc.h index 93d1067..87d2a45 100644 --- a/src/platforms/switch_aarch64_gcc.h +++ b/stackman/platforms/switch_aarch64_gcc.h @@ -14,16 +14,18 @@ #endif #endif +#ifndef STACKMAN_HAVE_CALL #define STACKMAN_HAVE_CALL 1 #define STACKMAN_STACK_ALIGN 16 +#endif #ifdef STACKMAN_SWITCH_IMPL #if !__ASSEMBLER__ && !defined(STACKMAN_ASSEMBLY_SRC) /* - * To test this, #include this file in a file, test.c and - * gcc -S -DSTACKMAN_SWITCH_IMPL test.c - * then examine test.s for the result. + * To test this, compile with appropriate cross platform flags, e.g. + * "arm-linux-gnueabi-gcc -S gen_asm.c" + * then examine gen_asm.s for the result. * We instruct optimizer to not omit frame pointers, -fno-omit_frame_pointer * and not use local stack vars, -O1. * option is applied with an __attribute__. @@ -62,7 +64,6 @@ void *STACKMAN_SWITCH_INASM_NAME(stackman_cb_t callback, void *context) sp = callback(context, STACKMAN_OP_RESTORE, sp); return sp; } -#endif /* * Similar, but we want the frame pointer so that a debugger @@ -79,7 +80,8 @@ void *stackman_call(stackman_cb_t callback, void *context, void *stack_pointer) /* set stack pointer from provided using assembly */ - __asm__ ("mov sp, %[var]" :: [var] "r" (stack_pointer)); + if(stack_pointer != 0) + __asm__ ("mov sp, %[var]" :: [var] "r" (stack_pointer)); result = callback(context, STACKMAN_OP_CALL, old_sp); /* restore stack pointer */ @@ -88,6 +90,8 @@ void *stackman_call(stackman_cb_t callback, void *context, void *stack_pointer) return result; } +#endif + #if __ASSEMBLER__ && defined(STACKMAN_ASSEMBLY_SRC) /* pre-generated assembly code */ #include STACKMAN_ASSEMBLY_SRC diff --git a/stackman/platforms/switch_arm64_msvc.asm b/stackman/platforms/switch_arm64_msvc.asm new file mode 100644 index 0000000..b4b1f13 --- /dev/null +++ b/stackman/platforms/switch_arm64_msvc.asm @@ -0,0 +1,105 @@ + + +; this code is adopted from the switch_aarch64_gcc.S +; which again was generated from switch_arm_gcc.h +; This code is also modelled on the hand-customized switch_arm_msvc.asm +; see https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-160 + + ;ARM64 + + EXPORT |stackman_switch| + ALIGN +|stackman_switch| PROC + ; args "callback", "context" are in x0, x1 + ; push non-volatile x18-x28 registers, in addition to fp,rl pair. + ; x29 is fp, x30 is lr. + stp x29, x30, [sp, #-176]! ; store fp,lr pair, allocate stack space + mov x29, sp ; and set fp pointing to old fp on stack + ; push non-volatile registers 18-x28 and fp regs d8-d15) + ; also fpcr (fp control register) + mrs x2,fpcr + stp x2, x18, [sp, #16] + stp x19, x20, [sp, #32] + stp x21, x22, [sp, #48] + stp x23, x24, [sp, #64] + stp x25, x26, [sp, #80] + stp x27, x28, [sp, #96] + stp d8, d9, [sp, #112] + stp d10, d11, [sp, #128] + stp d12, d13, [sp, #144] + stp d14, d15, [sp, #160] + + ; args are x0=callback, x1=context + ; shuffle calling arguments into r0, r1 and r2 + ; for the call to the callback. + mov x3, x0 + mov x0, x1 ;context + mov x2, sp ;stack pointer + mov x1, #0 ;operation + mov x19, x0 ;store these a bit in nv registers + mov x18, x3 + blr x3 ;first callback + mov x2, x0 ;store new stackpointer in register, for next cb, + ; switch stack, maintaining fp offset + sub x1, sp, x0 + mov sp, x0 ;switch stack pointer + sub fp, fp, x1 + + mov x1, #1 ;operation + mov x0, x19 ;context + blr x18 ;second callback + + ; restore registers from stack + ldp x2, x18, [sp, #16] + ldp x19, x20, [sp, #32] + ldp x21, x22, [sp, #48] + ldp x23, x24, [sp, #64] + ldp x25, x26, [sp, #80] + ldp x27, x28, [sp, #96] + ldp d8, d9, [sp, #112] + ldp d10, d11, [sp, #128] + ldp d12, d13, [sp, #144] + ldp d14, d15, [sp, #160] + msr fpcr, x2 + ;return + ldp x29, x30, [sp], #176 + ret + ENDP + + EXPORT |stackman_call| + ALIGN +|stackman_call| PROC +; args "callback", "context" are in x0, x1 + ; push non-volatile x18-x28 registers, in addition to fp,rl pair. + ; x29 is fp, x30 is lr. + stp x29, x30, [sp, #-32]! ; store fp,lr pair, allocate stack space + mov x29, sp ; and set fp pointing to old fp on stack + ; push non-volatile register 18 + str x18, [sp, #16] + + ; args are x0 = callback, x1 = context, x2=stack + mov x3, x0 + mov x0, x1 ;context + mov x1, x2 ;stack + ; store current sp in nv register + mov x18, sp + + mov x2, sp ; old stack + + ; change stack, if provided non-zero + cbz x1, nullptr + mov sp, x1 +nullptr + mov x1, #2 ; callback opcode + blr x3 ; call callback, with context, opcode, old stack + + ; restore stack (could do: sub sp, fp #12) + mov sp, x18 + + ; return + ldr x18, [sp, #16] + ldp x29, x30, [sp], #32 + ret + ENDP + + END diff --git a/stackman/platforms/switch_arm64_msvc.h b/stackman/platforms/switch_arm64_msvc.h new file mode 100644 index 0000000..891c035 --- /dev/null +++ b/stackman/platforms/switch_arm64_msvc.h @@ -0,0 +1,8 @@ +/* The actual stack saving function, which just stores the stack, + * this declared in an .asm file + */ +#ifndef STACKMAN_ASSEMBLY_SRC +#define STACKMAN_ASSEMBLY_SRC switch_arm64_msvc.asm +#define STACKMAN_HAVE_CALL 1 +#define STACKMAN_STACK_ALIGN 16 +#endif diff --git a/src/platforms/switch_arm_gcc.S b/stackman/platforms/switch_arm_gcc.S similarity index 74% rename from src/platforms/switch_arm_gcc.S rename to stackman/platforms/switch_arm_gcc.S index b48923d..8e11645 100644 --- a/src/platforms/switch_arm_gcc.S +++ b/stackman/platforms/switch_arm_gcc.S @@ -3,8 +3,8 @@ * dependable, such as with clang on linux x86_64 or if * STACKMAN_INLINE_ASM is false * The file is generated using - * "arm-linux-gnueabi-gcc -DSTACKMAN_SWITCH_IMPL -S test.c" and then copying the code - * from test.s into this file. + * "arm-linux-gnueabi-gcc -S gen_asm.c" and then copying the code + * from gen_asm.s into this file. * */ .arch armv5t @@ -17,7 +17,7 @@ .eabi_attribute 30, 6 .eabi_attribute 34, 0 .eabi_attribute 18, 4 - .file "test.c" + .file "gen_asm.c" .text .align 2 .global stackman_switch @@ -32,7 +32,7 @@ stackman_switch: mov r3, r0 mov r0, r1 .syntax divided -@ 82 "switch_arm_gcc.h" 1 +@ 84 "../platforms/switch_arm_gcc.h" 1 mov r2, sp @ 0 "" 2 .arm @@ -43,7 +43,7 @@ stackman_switch: blx r3 mov r2, r0 .syntax divided -@ 86 "switch_arm_gcc.h" 1 +@ 88 "../platforms/switch_arm_gcc.h" 1 mov sp, r0 @ 0 "" 2 .arm @@ -64,30 +64,36 @@ stackman_call: @ frame_needed = 1, uses_anonymous_args = 0 push {r4, r5, fp, lr} add fp, sp, #12 - mov r3, r0 + mov r5, r0 mov r0, r1 - mov r1, r2 + mov r3, r2 .syntax divided -@ 102 "switch_arm_gcc.h" 1 +@ 104 "../platforms/switch_arm_gcc.h" 1 mov r4, sp @ 0 "" 2 -@ 103 "switch_arm_gcc.h" 1 +@ 105 "../platforms/switch_arm_gcc.h" 1 mov r2, sp @ 0 "" 2 -@ 106 "switch_arm_gcc.h" 1 - mov sp, r1 + .arm + .syntax unified + cmp r3, #0 + beq .L4 + .syntax divided +@ 109 "../platforms/switch_arm_gcc.h" 1 + mov sp, r3 @ 0 "" 2 .arm .syntax unified +.L4: mov r1, #2 - blx r3 + blx r5 .syntax divided -@ 110 "switch_arm_gcc.h" 1 +@ 113 "../platforms/switch_arm_gcc.h" 1 mov sp, r4 @ 0 "" 2 .arm .syntax unified pop {r4, r5, fp, pc} .size stackman_call, .-stackman_call - .ident "GCC: (Ubuntu 9.3.0-10ubuntu1) 9.3.0" + .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" .section .note.GNU-stack,"",%progbits diff --git a/src/platforms/switch_arm_gcc.h b/stackman/platforms/switch_arm_gcc.h similarity index 94% rename from src/platforms/switch_arm_gcc.h rename to stackman/platforms/switch_arm_gcc.h index 3e239a3..5fd867c 100644 --- a/src/platforms/switch_arm_gcc.h +++ b/stackman/platforms/switch_arm_gcc.h @@ -15,8 +15,10 @@ #endif #endif +#ifndef STACKMAN_HAVE_CALL #define STACKMAN_HAVE_CALL 1 #define STACKMAN_STACK_ALIGN 8 +#endif #ifdef STACKMAN_SWITCH_IMPL #if !__ASSEMBLER__ && !defined(STACKMAN_ASSEMBLY_SRC) @@ -27,10 +29,10 @@ * stack pointer on function exit, thereby invalidating our changes * to the stack pointer. * To arrive at reasonable assembler, follow some approach similar to: - * cp switch_arm_gcc.h test.c - * gcc -S -O -DSTACKMAN_SWITCH_IMPL test.c - * mv test.s switch_arm_gcc.s - * assembly code which can then be modified for actual use. Simple optimized + * "gcc -S gen_asm.c" + * to generate gen_asm.s which can then be + * modified for actual use. + * Simple optimized * version is better than no-optimized because the latter uses stack * variables for arguments. And it turns out that this version actually * runs because it does not use fp for restoring the stack pointer @@ -103,7 +105,8 @@ void *stackman_call(stackman_cb_t callback, void *context, void *stack_pointer) __asm__ ("mov %[var], sp" : [var] "=r" (old_sp)); /* set stack pointer from provided using assembly */ - __asm__ ("mov sp, %[var]" :: [var] "r" (stack_pointer)); + if (stack_pointer != 0) + __asm__ ("mov sp, %[var]" :: [var] "r" (stack_pointer)); result = callback(context, STACKMAN_OP_CALL, old_sp); /* restore stack pointer */ diff --git a/stackman/platforms/switch_arm_msvc.asm b/stackman/platforms/switch_arm_msvc.asm new file mode 100644 index 0000000..f002dfa --- /dev/null +++ b/stackman/platforms/switch_arm_msvc.asm @@ -0,0 +1,72 @@ + + +; this code is adopted from the switch_arm_gcc.S +; which again was generated from switch_arm_gcc.h + + THUMB + + EXPORT |stackman_switch| + ALIGN +|stackman_switch| PROC + push {r4 - r10, fp, lr} ;9 x 4bytes + add fp, sp, #28 ; (4x7) fp must point to the previous fp on the stack + + ; also push floating point registers and fpscr (status&control register) + vpush {q4-q7} + vmrs r2,fpscr + push {r2} ;5 x 4bytes + ; total of 14 words pushed, satisfying 8 byte stack alignment. + + ; args are r0=callback, r1=context + ; shuffle calling arguments into r0, r1 and r2 + ; for the call to the callback. + mov r3, r0 + mov r0, r1 ;context + mov r2, sp ;stack pointer + mov r1, #0 ;operation + mov r5, r0 ;store these a bit + mov r4, r3 + blx r3 ;first callback + mov r2, r0 ;store new stackpointer in register, for next cb, + ; switch stack pointer, maintaing fp offset + sub r1, sp, r0 + mov sp, r0 ;switch stack pointer + sub fp, fp, r1 + + mov r1, #1 ;operation + mov r0, r5 ;context + blx r4 ;second callback + ; pop and return + pop {r2} + vmsr fpscr, r2 + vpop {q4, q5, q6, q7} + pop {r4-r9, r10, fp, pc} + ENDP + + EXPORT |stackman_call| + ALIGN +|stackman_call| PROC + ; args are r0 = callback, r1 = context, r2=stack + push {r4, fp, lr} + add fp, sp, #4 ;fp must always point to the last fp (r11) + mov r3, r0 + mov r0, r1 ;context + mov r1, r2 ;stack + ; store current sp in nv register + mov r4, sp + mov r2, sp ; old stack + + ; change stack if non-zero + cmp r1, #0 + beq nullptr + mov sp, r1 +nullptr + mov r1, #2 ; callback opcode + blx r3 ; call callback, with context, opcode, old stack + ; restore stack (could do: sub sp, fp #12) + mov sp, r4 + ; return + pop {r4, fp, pc} + ENDP + + END diff --git a/stackman/platforms/switch_arm_msvc.h b/stackman/platforms/switch_arm_msvc.h new file mode 100644 index 0000000..02025c6 --- /dev/null +++ b/stackman/platforms/switch_arm_msvc.h @@ -0,0 +1,8 @@ +/* The actual stack saving function, which just stores the stack, + * this declared in an .asm file + */ +#ifndef STACKMAN_ASSEMBLY_SRC +#define STACKMAN_ASSEMBLY_SRC switch_arm_msvc.asm +#define STACKMAN_HAVE_CALL 1 +#define STACKMAN_STACK_ALIGN 8 +#endif diff --git a/src/platforms/switch_template.h b/stackman/platforms/switch_template.h similarity index 75% rename from src/platforms/switch_template.h rename to stackman/platforms/switch_template.h index d3f11d4..149209f 100644 --- a/src/platforms/switch_template.h +++ b/stackman/platforms/switch_template.h @@ -44,6 +44,25 @@ void *STACKMAN_SWITCH_INASM_NAME(stackman_cb_t callback, void *context) /* __asm__("pop volatile registers") */ return stack_pointer; } + +STACKMAN_LINKAGE_SWITCH +void *stackman_call(stackman_cb_t callback, void *context, void *stack_pointer) +{ + void *old_sp, *result; + /* sp = store stack pointer in rbx */ + /*__asm__ ("movq %%rsp, %%rbx" : : : "rbx");*/ + /*__asm__ ("movq %%rsp, %[sp]" : [sp] "=r" (old_sp));*/ + + /* if non-null, set stack pointer as provided using assembly */ + if (stack_pointer != 0) + /*__asm__ ("movq %[sp], %%rsp" :: [sp] "r" (stack_pointer))*/; + + result = callback(context, STACKMAN_OP_CALL, old_sp); + /* restore stack pointer */ + /*__asm__ ("movq %%rbx, %%rsp" :::); */ + + return result; +} #endif #if __ASSEMBLER__ && defined(STACKMAN_ASSEMBLY_SRC) diff --git a/src/platforms/switch_x64_msvc.asm b/stackman/platforms/switch_x64_msvc.asm similarity index 69% rename from src/platforms/switch_x64_msvc.asm rename to stackman/platforms/switch_x64_msvc.asm index c318a9a..998eb24 100644 --- a/src/platforms/switch_x64_msvc.asm +++ b/stackman/platforms/switch_x64_msvc.asm @@ -1,98 +1,138 @@ -; -; stack switching code for MASM on x64 -; Kristjan Valur Jonsson, apr 2011 -; Modified for stackman, dec 2019 -; - -include macamd64.inc - -pop_reg MACRO reg - pop reg -ENDM - -load_xmm128 macro Reg, Offset - movdqa Reg, Offset[rsp] -endm - -.code - -;arguments callback, context, are passed in rcx, rdx, respectively -;stackman_switch PROC FRAME -NESTED_ENTRY stackman_switch, _TEXT$00 - ; save all registers that the x64 ABI specifies as non-volatile. - ; This includes some mmx registers. May not always be necessary, - ; unless our application is doing 3D, but better safe than sorry. - alloc_stack 168; 10 * 16 bytes, plus 8 bytes to make stack 16 byte aligned - save_xmm128 xmm15, 144 - save_xmm128 xmm14, 128 - save_xmm128 xmm13, 112 - save_xmm128 xmm12, 96 - save_xmm128 xmm11, 80 - save_xmm128 xmm10, 64 - save_xmm128 xmm9, 48 - save_xmm128 xmm8, 32 - save_xmm128 xmm7, 16 - save_xmm128 xmm6, 0 - - push_reg r15 - push_reg r14 - push_reg r13 - push_reg r12 - - push_reg rbp - push_reg rbx - push_reg rdi - push_reg rsi - - sub rsp, 20h ;allocate shadow stack space for callee arguments - .allocstack 20h -.endprolog - - ;save argments in nonvolatile registers - mov r12, rcx ;callback - mov r13, rdx ;context - - ; load stack base that we are saving minus the callee argument - ; shadow stack. We don't want that clobbered - mov rcx, r13 ;arg1, context - mov rdx, 0 ;arg2, opcode STACKMAN_OP_SAVE - lea r8, [rsp+20h] ;arg3, stack pointer - call r12 ; - - ;actual stack switch (and re-allocating the shadow stack): - lea rsp, [rax-20h] - - mov rcx, r13 ;arg1, context - mov rdx, 1 ;arg2, opcode STACKMAN_OP_RESTORE - mov r8, rax ;arg3, new stack pointer - call r12 - ;return the rax - - add rsp, 20h - pop_reg rsi - pop_reg rdi - pop_reg rbx - pop_reg rbp - - pop_reg r12 - pop_reg r13 - pop_reg r14 - pop_reg r15 - - load_xmm128 xmm15, 144 - load_xmm128 xmm14, 128 - load_xmm128 xmm13, 112 - load_xmm128 xmm12, 96 - load_xmm128 xmm11, 80 - load_xmm128 xmm10, 64 - load_xmm128 xmm9, 48 - load_xmm128 xmm8, 32 - load_xmm128 xmm7, 16 - load_xmm128 xmm6, 0 - add rsp, 168 - ret - -NESTED_END stackman_switch, _TEXT$00 -;stackman_switch ENDP - -END +; +; stack switching code for MASM on x64 +; Kristjan Valur Jonsson, apr 2011 +; Modified for stackman, dec 2019 +; Added stackman_call, dec 2020 +; + +include macamd64.inc + +pop_reg MACRO reg + pop reg +ENDM + +load_xmm128 macro Reg, Offset + movdqa Reg, Offset[rsp] +endm + +.code + +;arguments callback, context, are passed in rcx, rdx, respectively +;stackman_switch PROC FRAME +NESTED_ENTRY stackman_switch, _TEXT$00 + ; save all registers that the x64 ABI specifies as non-volatile. + ; This includes some mmx registers. May not always be necessary, + ; unless our application is doing 3D, but better safe than sorry. + alloc_stack 168; 10 * 16 bytes, plus 8 bytes to make stack 16 byte aligned + save_xmm128 xmm15, 144 + save_xmm128 xmm14, 128 + save_xmm128 xmm13, 112 + save_xmm128 xmm12, 96 + save_xmm128 xmm11, 80 + save_xmm128 xmm10, 64 + save_xmm128 xmm9, 48 + save_xmm128 xmm8, 32 + save_xmm128 xmm7, 16 + save_xmm128 xmm6, 0 + + push_reg r15 + push_reg r14 + push_reg r13 + push_reg r12 + + push_reg rbp + push_reg rbx + push_reg rdi + push_reg rsi + + sub rsp, 20h ;allocate shadow stack space for callee arguments + .allocstack 20h +.endprolog + + ;save argments in nonvolatile registers + mov r12, rcx ;callback + mov r13, rdx ;context + + ; load stack base that we are saving minus the callee argument + ; shadow stack. We don't want that clobbered + mov rcx, r13 ;arg1, context + mov rdx, 0 ;arg2, opcode STACKMAN_OP_SAVE + lea r8, [rsp+20h] ;arg3, stack pointer + mov rbx, rsp; ; keep old stack pointer + call r12 ; + + ;actual stack switch (and re-allocating the shadow stack): + lea rsp, [rax-20h] + ;re-adjust base pointer + sub rbx, rsp; + sub rbp, rbx; + + mov rcx, r13 ;arg1, context + mov rdx, 1 ;arg2, opcode STACKMAN_OP_RESTORE + mov r8, rax ;arg3, new stack pointer + call r12 + ;return the rax + + add rsp, 20h + pop_reg rsi + pop_reg rdi + pop_reg rbx + pop_reg rbp + + pop_reg r12 + pop_reg r13 + pop_reg r14 + pop_reg r15 + + load_xmm128 xmm15, 144 + load_xmm128 xmm14, 128 + load_xmm128 xmm13, 112 + load_xmm128 xmm12, 96 + load_xmm128 xmm11, 80 + load_xmm128 xmm10, 64 + load_xmm128 xmm9, 48 + load_xmm128 xmm8, 32 + load_xmm128 xmm7, 16 + load_xmm128 xmm6, 0 + add rsp, 168 + ret + +NESTED_END stackman_switch, _TEXT$00 +;stackman_switch ENDP + +; based on template from https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=msvc-160 +stackman_call PROC FRAME + push rbp + .pushreg rbp + ; now our stack is 16 byte aligned. don't need additional space + ;sub rsp, 040h + ;.allocstack 040h + lea rbp, [rsp+00h] + .setframe rbp, 00h + .endprolog + + ; suffle arguments into volatile registers + mov rax, rcx ; callback + mov rcx, rdx ; context into first arg + mov r9, r8 ; and stack pointer in volatile registers + + ; set up call + mov r8, rsp + mov edx, 2 + ; rcx already set up with context + + ; modify stack pointer before call + test r9, r9 + je nullptr + mov rsp, r9 +nullptr: + sub rsp, 32 ;pre-allocate parameter stack for the callee + call rax + + ; officialepilog + lea rsp, [rbp+0h] + pop rbp + ret 0 +stackman_call ENDP + +END \ No newline at end of file diff --git a/src/platforms/switch_x64_msvc.h b/stackman/platforms/switch_x64_msvc.h similarity index 75% rename from src/platforms/switch_x64_msvc.h rename to stackman/platforms/switch_x64_msvc.h index da5a604..d1dc6ac 100644 --- a/src/platforms/switch_x64_msvc.h +++ b/stackman/platforms/switch_x64_msvc.h @@ -1,6 +1,8 @@ -/* The actual stack saving function, which just stores the stack, - * this declared in an .asm file - */ -#ifndef STACKMAN_ASSEMBLY_SRC -#define STACKMAN_ASSEMBLY_SRC switch_x64_msvc.asm -#endif +/* The actual stack saving function, which just stores the stack, + * this declared in an .asm file + */ +#ifndef STACKMAN_ASSEMBLY_SRC +#define STACKMAN_ASSEMBLY_SRC switch_x64_msvc.asm +#define STACKMAN_HAVE_CALL 1 +#define STACKMAN_STACK_ALIGN 16 +#endif diff --git a/src/platforms/switch_x86_64_gcc.S b/stackman/platforms/switch_x86_64_gcc.S similarity index 72% rename from src/platforms/switch_x86_64_gcc.S rename to stackman/platforms/switch_x86_64_gcc.S index 9843c55..aca7168 100644 --- a/src/platforms/switch_x86_64_gcc.S +++ b/stackman/platforms/switch_x86_64_gcc.S @@ -5,11 +5,11 @@ * implementation that is independent of subtleties of inline * assembly code which may change with compiler versions. * The file is generated using - * "gcc -DSTACKMAN_SWITCH_IMPL -S -fcf-protection=none test.c" - * and then copying the code from test.s into this file. + * "gcc -S -fcf-protection=none gen_asm.c" + * and then copying the code from gen_asm.s into this file. * */ - .file "test.c" + .file "gen_asm.c" .text .globl stackman_switch .type stackman_switch, @function @@ -39,31 +39,38 @@ stackman_switch: movq %rdi, %rax movq %rsi, %rcx #APP -# 52 "switch_x86_64_gcc.h" 1 +# 56 "../platforms/switch_x86_64_gcc.h" 1 fstcw 10(%rsp) stmxcsr 12(%rsp) # 0 "" 2 -# 58 "switch_x86_64_gcc.h" 1 - movq %rsp, %rdx +# 62 "../platforms/switch_x86_64_gcc.h" 1 + movq %rsp, %rbx # 0 "" 2 #NO_APP + movq %rbx, %rdx movl $0, %esi - movq %rcx, %r14 + movq %rcx, %r15 movq %rcx, %rdi - movq %rax, %rbx + movq %rax, %r14 call *%rax - movq %rax, %rdx #APP -# 62 "switch_x86_64_gcc.h" 1 +# 67 "../platforms/switch_x86_64_gcc.h" 1 movq %rax, %rsp # 0 "" 2 #NO_APP + subq %rbx, %rax +#APP +# 68 "../platforms/switch_x86_64_gcc.h" 1 + addq %rax, %rbp +# 0 "" 2 +#NO_APP + movq %rbx, %rdx movl $1, %esi - movq %r14, %rdi - call *%rbx + movq %r15, %rdi + call *%r14 #APP -# 66 "switch_x86_64_gcc.h" 1 +# 72 "../platforms/switch_x86_64_gcc.h" 1 ldmxcsr 12(%rsp) fldcw 10(%rsp) @@ -103,21 +110,26 @@ stackman_call: movq %rdi, %rax movq %rsi, %rdi #APP -# 86 "switch_x86_64_gcc.h" 1 +# 92 "../platforms/switch_x86_64_gcc.h" 1 movq %rsp, %rbx # 0 "" 2 -# 87 "switch_x86_64_gcc.h" 1 +# 93 "../platforms/switch_x86_64_gcc.h" 1 movq %rsp, %rcx # 0 "" 2 -# 90 "switch_x86_64_gcc.h" 1 +#NO_APP + testq %rdx, %rdx + je .L4 +#APP +# 97 "../platforms/switch_x86_64_gcc.h" 1 movq %rdx, %rsp # 0 "" 2 #NO_APP +.L4: movq %rcx, %rdx movl $2, %esi call *%rax #APP -# 94 "switch_x86_64_gcc.h" 1 +# 101 "../platforms/switch_x86_64_gcc.h" 1 movq %rbx, %rsp # 0 "" 2 #NO_APP @@ -129,5 +141,5 @@ stackman_call: .cfi_endproc .LFE1: .size stackman_call, .-stackman_call - .ident "GCC: (Ubuntu 9.3.0-10ubuntu2) 9.3.0" + .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" .section .note.GNU-stack,"",@progbits diff --git a/src/platforms/switch_x86_64_gcc.h b/stackman/platforms/switch_x86_64_gcc.h similarity index 89% rename from src/platforms/switch_x86_64_gcc.h rename to stackman/platforms/switch_x86_64_gcc.h index 20375f3..50f566a 100644 --- a/src/platforms/switch_x86_64_gcc.h +++ b/stackman/platforms/switch_x86_64_gcc.h @@ -16,13 +16,16 @@ #endif #endif +#ifndef STACKMAN_HAVE_CALL #define STACKMAN_HAVE_CALL 1 #define STACKMAN_STACK_ALIGN 16 +#endif #if defined(STACKMAN_SWITCH_IMPL) #if !__ASSEMBLER__ && !defined(STACKMAN_ASSEMBLY_SRC) /* inline assembly */ +#include #include "../stackman_switch.h" /* @@ -46,7 +49,8 @@ __attribute__((optimize(OPTIMIZE))) STACKMAN_LINKAGE_SWITCH_INASM void *STACKMAN_SWITCH_INASM_NAME(stackman_cb_t callback, void *context) { - void *stack_pointer; + void *stack_pointer, *stack_pointer2; + ptrdiff_t diff; /* assembly to save non-volatile registers, including x87 and mmx */ int mxcsr; short x87cw; __asm__ volatile ( @@ -56,10 +60,12 @@ void *STACKMAN_SWITCH_INASM_NAME(stackman_cb_t callback, void *context) /* sp = get stack pointer from assembly code */ __asm__ ("movq %%rsp, %[result]" : [result] "=r" (stack_pointer)); - stack_pointer = callback(context, STACKMAN_OP_SAVE, stack_pointer); + stack_pointer2 = callback(context, STACKMAN_OP_SAVE, stack_pointer); + diff = stack_pointer2 - stack_pointer; /* set stack pointer from sp using assembly */ - __asm__ ("movq %[result], %%rsp" :: [result] "r" (stack_pointer)); + __asm__ ("movq %[result], %%rsp" :: [result] "r" (stack_pointer2)); + __asm__ ("addq %[arg], %%rbp" :: [arg] "r" (diff)); stack_pointer = callback(context, STACKMAN_OP_RESTORE, stack_pointer); /* restore non-volatile registers from stack */ @@ -87,7 +93,8 @@ void *stackman_call(stackman_cb_t callback, void *context, void *stack_pointer) __asm__ ("movq %%rsp, %[sp]" : [sp] "=r" (old_sp)); /* set stack pointer from provided using assembly */ - __asm__ ("movq %[sp], %%rsp" :: [sp] "r" (stack_pointer)); + if (stack_pointer != 0) + __asm__ ("movq %[sp], %%rsp" :: [sp] "r" (stack_pointer)); result = callback(context, STACKMAN_OP_CALL, old_sp); /* restore stack pointer */ diff --git a/src/platforms/switch_x86_gcc.S b/stackman/platforms/switch_x86_gcc.S similarity index 70% rename from src/platforms/switch_x86_gcc.S rename to stackman/platforms/switch_x86_gcc.S index 3d258eb..671a6b5 100644 --- a/src/platforms/switch_x86_gcc.S +++ b/stackman/platforms/switch_x86_gcc.S @@ -5,11 +5,11 @@ * implementation that is independent of subtleties of inline * assembly code which may change with compiler versions. * The file is generated using - * "gcc -DSTACKMAN_SWITCH_IMPL -S -m32 -fcf-protection=none test.c" - * and then copying the code from test.s into this file. + * "gcc -S -m32 -fcf-protection=none gen_asm.c" + * and then copying the code from gen_asm.s into this file. * */ - .file "test.c" + .file "gen_asm.c" .text .globl stackman_switch .type stackman_switch, @function @@ -30,21 +30,25 @@ stackman_switch: movl 8(%ebp), %eax movl 12(%ebp), %edx #APP -# 48 "switch_x86_gcc.h" 1 - subl $12, %esp +# 50 "../platforms/switch_x86_gcc.h" 1 + subl $0x1c, %esp movl %eax, %esi movl %edx, %edi -movl %esp, 8(%esp) +leal 0x10(%esp), %ebx +movl %ebx, 8(%esp) movl $0, 4(%esp) movl %edi, 0(%esp) call *%esi -movl %eax, %esp +leal -0x10(%eax), %esp +subl %ebx, %eax +addl %eax, %ebp +leal 0x10(%esp), %eax movl %eax, 8(%esp) movl $1, 4(%esp) movl %edi, 0(%esp) call *%esi movl %eax, %eax -addl $12, %esp +addl $0x1c, %esp # 0 "" 2 #NO_APP @@ -72,28 +76,32 @@ stackman_call: movl %esp, %ebp .cfi_def_cfa_register 5 pushl %ebx - subl $8, %esp + subl $4, %esp .cfi_offset 3, -12 + movl 16(%ebp), %eax #APP -# 96 "switch_x86_gcc.h" 1 +# 108 "../platforms/switch_x86_gcc.h" 1 movl %esp, %ebx # 0 "" 2 -# 97 "switch_x86_gcc.h" 1 - movl %esp, %eax +# 111 "../platforms/switch_x86_gcc.h" 1 + movl %esp, %edx # 0 "" 2 #NO_APP - movl 16(%ebp), %edx + testl %eax, %eax + je .L4 #APP -# 100 "switch_x86_gcc.h" 1 - movl %edx, %esp +# 117 "../platforms/switch_x86_gcc.h" 1 + movl %eax, %esp # 0 "" 2 #NO_APP - pushl %eax +.L4: + subl $4, %esp + pushl %edx pushl $2 pushl 12(%ebp) call *8(%ebp) #APP -# 104 "switch_x86_gcc.h" 1 +# 124 "../platforms/switch_x86_gcc.h" 1 movl %ebx, %esp # 0 "" 2 #NO_APP @@ -106,5 +114,5 @@ stackman_call: .cfi_endproc .LFE1: .size stackman_call, .-stackman_call - .ident "GCC: (Ubuntu 9.3.0-10ubuntu2) 9.3.0" + .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" .section .note.GNU-stack,"",@progbits diff --git a/src/platforms/switch_x86_gcc.h b/stackman/platforms/switch_x86_gcc.h similarity index 72% rename from src/platforms/switch_x86_gcc.h rename to stackman/platforms/switch_x86_gcc.h index c380529..38fbee8 100644 --- a/src/platforms/switch_x86_gcc.h +++ b/stackman/platforms/switch_x86_gcc.h @@ -10,8 +10,10 @@ #endif #endif +#ifndef STACKMAN_HAVE_CALL #define STACKMAN_HAVE_CALL 1 #define STACKMAN_STACK_ALIGN 16 +#endif #if defined(STACKMAN_SWITCH_IMPL ) #if !__ASSEMBLER__ && !defined(STACKMAN_ASSEMBLY_SRC) @@ -50,31 +52,41 @@ void *result; /* adjust stack pointer to be 16 byte (4 quad) aligned with * room for call args * since the call instruction, 5 quads have - * been pushed (ip, bp, bx, si, di), need extra 3 quads + * been pushed (ip, bp, bx, si, di), need extra 3 quads (0xc) * for alignment, which fits our three quad call args. + * Add another 16 (0x10) bytes, so that we can pass to the callbacks + * a 16 byte boundary that lies above the pushed arguments + * so that the call arguments of the functions aren't clobbered + * by memory transfer. */ - "subl $12, %%esp\n" + "subl $0x1c, %%esp\n" "movl %[cb], %%esi\n" /* save 'callback' for later */ "movl %[ctx], %%edi\n" /* save 'context' for later */ - /* first call */ - "movl %%esp, 8(%%esp)\n" /* arg 2 sp */ + /* first call */ + "leal 0x10(%%esp), %%ebx\n" /* arg 2 adjusted sp (also in nv. reg) */ + "movl %%ebx, 8(%%esp)\n" "movl $0, 4(%%esp)\n" /* arg 1 opcode STACKMAN_OP_SAVE */ "movl %%edi, 0(%%esp)\n" /* arg 0 context */ "call *%%esi\n" - /* restore esp */ - "movl %%eax, %%esp\n" + /* restore esp, re-adding shadow space */ + "leal -0x10(%%eax), %%esp\n" + + /* and adjust ebp with difference between new and old */ + "subl %%ebx, %%eax\n" + "addl %%eax, %%ebp\n" /* second call */ - "movl %%eax, 8(%%esp)\n" /* arg 2 sp */ + "leal 0x10(%%esp), %%eax\n" /* arg 2 sp */ + "movl %%eax, 8(%%esp)\n" "movl $1, 4(%%esp)\n" /* arg 1 opcode STACKMAN_OP_RESTORE */ "movl %%edi, 0(%%esp)\n" /* arg 0 context */ "call *%%esi\n" "movl %%eax, %[result]\n" - "addl $12, %%esp\n" + "addl $0x1c, %%esp\n" : [result] "=r" (result) /* output variables */ : [cb] "r" (callback), /* input variables */ [ctx] "r" (context) @@ -94,10 +106,20 @@ void *stackman_call(stackman_cb_t callback, void *context, void *stack_pointer) /* sp = store stack pointer in ebx */ __asm__ ("movl %%esp, %%ebx" : : : "ebx"); + /* save old stack pointer at same offset as new stack pointer */ + /* (adjustment of stack pointer not required now) + /*__asm__ ("leal 4(%%esp), %[sp]" : [sp] "=r" (old_sp)); */ __asm__ ("movl %%esp, %[sp]" : [sp] "=r" (old_sp)); - + + /* set stack pointer from provided using assembly */ - __asm__ ("movl %[sp], %%esp" :: [sp] "r" (stack_pointer)); + if (stack_pointer != 0) + { + __asm__ ("movl %[sp], %%esp" :: [sp] "r" (stack_pointer)); + /* subtract 4 bytes to make it 16 byte alighed after pushing 3 args */ + /* (adjustment of stack pointer not required now) + /* __asm__ ("subl $4, %esp"); */ + } result = callback(context, STACKMAN_OP_CALL, old_sp); /* restore stack pointer */ diff --git a/src/platforms/switch_x86_msvc.asm b/stackman/platforms/switch_x86_msvc.asm similarity index 50% rename from src/platforms/switch_x86_msvc.asm rename to stackman/platforms/switch_x86_msvc.asm index 1e6542e..2f28cf9 100644 --- a/src/platforms/switch_x86_msvc.asm +++ b/stackman/platforms/switch_x86_msvc.asm @@ -1,41 +1,65 @@ - -.386 -.model flat, c - -.code - -stackman_switch_raw PROC callback:DWORD, context:DWORD - - ;save registers. EAX ECX and EDX are available for function use and thus - ;do not have to be stored. - push ebx - push esi - push edi - push ebp - - mov esi, callback ; /* save 'callback' for later */ - mov edi, extra ; /* save 'context' for later */ - - mov eax, esp - - push eax ; /* arg 3: current (old) stack pointer */ - push 0; /* arg 2: opcode STACKMAN_OP_SAVE */ - push edi ; /* arg 1: context */ - call esi ; /* call callback() */ - - mov esp, eax; /* change the stack pointer */ - - push eax ; /* arg 3: current (new) stack pointer */ - push 1; /* arg 2: opcode STACKMAN_OP_RESTORE */ - push edi ; /* arg 1: context */ - call esi ; /* call callback() */ - add esp, 12 - - pop ebp - pop edi - pop esi - pop ebx - ret -stackman_switch_raw ENDP - -end + +.386 +.model flat, c + +.code + +stackman_switch_raw PROC callback:DWORD, context:DWORD + ;Assembler has already pushed the basepointer and saved it. + ;save registers. EAX ECX and EDX are available for function use and thus + ;do not have to be stored. + push ebx + push esi + push edi + + mov esi, callback ; /* save 'callback' for later */ + mov edi, context ; /* save 'context' for later */ + + mov eax, esp + + push eax ; /* arg 3: current (old) stack pointer */ + push 0; /* arg 2: opcode STACKMAN_OP_SAVE */ + push edi ; /* arg 1: context */ + call esi ; /* call callback() */ + add esp, 12; + + mov ecx, eax; /* change stack pointer to eax, preserving */ + sub ecx, esp; /* base pointer offset from esp */ + mov esp, eax; + add ebp, ecx; + + + push eax ; /* arg 3: current (new) stack pointer */ + push 1; /* arg 2: opcode STACKMAN_OP_RESTORE */ + push edi ; /* arg 1: context */ + call esi ; /* call callback() */ + add esp, 12 + + pop edi + pop esi + pop ebx + ret +stackman_switch_raw ENDP + + +stackman_call PROC callback:DWORD, context:DWORD, stack_pointer:DWORD + ;enter prolog has pushed ebp and saved esp in ebp + + mov eax, callback + mov ecx, context + mov edx, stack_pointer + + ; switch stack pointer + test edx, edx + je nullptr + mov esp, stack_pointer +nullptr: + push ebp ;old stack pointer + push 2 ;STACKMAN_OP_CALL + push ecx ;context + call eax ;callback + ret ; this assembles a LEAVE instruction which restores esp to ebp, then pops the ebp +stackman_call ENDP + + +end diff --git a/src/platforms/switch_x86_msvc.h b/stackman/platforms/switch_x86_msvc.h similarity index 83% rename from src/platforms/switch_x86_msvc.h rename to stackman/platforms/switch_x86_msvc.h index afa3669..e30824e 100644 --- a/src/platforms/switch_x86_msvc.h +++ b/stackman/platforms/switch_x86_msvc.h @@ -1,35 +1,38 @@ -/* The actual stack saving function, which just stores the stack, - * this declared in an .asm file - * The C function defined here, saves and restores the structured - * exception handling state. - */ -#ifndef STACKMAN_ASSEMBLY_SRC -#define STACKMAN_ASSEMBLY_SRC switch_x86_msvc.asm -#define STACKMAN_SWITCH_C 1 /* contains a C implementation */ -#endif - -#ifdef STACKMAN_SWITCH_IMPL -#include "../stackman_switch.h" - -extern void *stackman_switch_raw(stackman_cb_t callback, void *context); - -#define WIN32_LEAN_AND_MEAN -#include - -/* Store any other runtime information on the local stack */ -#pragma optimize("", off) /* so that autos are stored on the stack */ -#pragma warning(disable:4733) /* disable warning about modifying FS[0] */ - -STACKMAN_LINKAGE_SWITCH -void *stackman_switch(vtackman_cb_t callback, void *context) -{ - /* store the structured exception state for this stack */ - DWORD seh_state = __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); - void * result = sta_switchckman_raw(callback, context); - __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), seh_state); - return result; -} -#pragma warning(default:4733) /* disable warning about modifying FS[0] */ -#pragma optimize("", on) - -#endif +/* The actual stack saving function, which just stores the stack, + * this declared in an .asm file + * The C function defined here, saves and restores the structured + * exception handling state. + */ +#if !defined(STACKMAN_ASSEMBLY_SRC) +#define STACKMAN_ASSEMBLY_SRC switch_x86_msvc.asm +#define STACKMAN_SWITCH_C 1 /* contains a C implementation */ +#define STACKMAN_HAVE_CALL 1 +#define STACKMAN_STACK_ALIGN 4 +#endif + + +#ifdef STACKMAN_SWITCH_IMPL +#include "../stackman_switch.h" + +extern void *stackman_switch_raw(stackman_cb_t callback, void *context); + +#define WIN32_LEAN_AND_MEAN +#include + +/* Store any other runtime information on the local stack */ +#pragma optimize("", off) /* so that autos are stored on the stack */ +#pragma warning(disable:4733) /* disable warning about modifying FS[0] */ + +STACKMAN_LINKAGE_SWITCH +void *stackman_switch(stackman_cb_t callback, void *context) +{ + /* store the structured exception state for this stack */ + DWORD seh_state = __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); + void * result = stackman_switch_raw(callback, context); + __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), seh_state); + return result; +} +#pragma warning(default:4733) /* disable warning about modifying FS[0] */ +#pragma optimize("", on) + +#endif diff --git a/src/stackman.c b/stackman/stackman.c similarity index 100% rename from src/stackman.c rename to stackman/stackman.c diff --git a/src/stackman.h b/stackman/stackman.h similarity index 96% rename from src/stackman.h rename to stackman/stackman.h index defd15f..4a2bf78 100644 --- a/src/stackman.h +++ b/stackman/stackman.h @@ -72,7 +72,7 @@ #define STACKMAN_SP_ALIGN_UP(a) (((intptr_t)((a)+STACKMAN_STACK_ALIGN-1) & ~(STACKMAN_STACK_ALIGN-1))) #if STACKMAN_STACK_DIR == 0 -#define STACKMAN_SP_FURTHEST ((void*) ^(intptr_t)-1) +#define STACKMAN_SP_FURTHEST ((void*) (intptr_t) -STACKMAN_STACK_ALIGN) #define STACKMAN_SP_NEAREST ((void*) 0) #define STACKMAN_SP_LS(a, b) ((void*)(a) < (void*)(b)) /* to compare stack position */ #define STACKMAN_SP_LE(a, b) ((void*)(a) <= (void*)(b)) /* to compare stack position */ @@ -82,7 +82,7 @@ #else /* upwards growing stacks */ #define STACKMAN_SP_FURTHEST ((void*) 0) -#define STACKMAN_SP_NEAREST ((void*) ^(intptr_t)-1) +#define STACKMAN_SP_NEAREST ((void*) (intptr_t) -STACKMAN_STACK_ALIGN) #define STACKMAN_SP_LS(a, b) ((void*)(a) > (void*)(b)) /* to compare stack position */ #define STACKMAN_SP_LE(a, b) ((void*)(a) >= (void*)(b)) /* to compare stack position */ #define STACKMAN_SP_ADD(a, b) ((a) - (b)) /* to add offset to stack pointer */ diff --git a/src/stackman_impl.h b/stackman/stackman_impl.h similarity index 100% rename from src/stackman_impl.h rename to stackman/stackman_impl.h diff --git a/src/stackman_s.S b/stackman/stackman_s.S similarity index 100% rename from src/stackman_s.S rename to stackman/stackman_s.S diff --git a/src/stackman_s.asm b/stackman/stackman_s.asm similarity index 100% rename from src/stackman_s.asm rename to stackman/stackman_s.asm diff --git a/src/stackman_switch.h b/stackman/stackman_switch.h similarity index 96% rename from src/stackman_switch.h rename to stackman/stackman_switch.h index 11d4157..3694cdf 100644 --- a/src/stackman_switch.h +++ b/stackman/stackman_switch.h @@ -30,8 +30,11 @@ * building a library, because an optimizing compiler may * decide to inline functions that contain in-line assembler */ -#define STACKMAN_SWITCH_NEED_INDIRECT \ - (!defined(STACKMAN_ASSEMBLY_SRC) && !defined(STACKMAN_BUILD_LIB)) +#if (!defined(STACKMAN_ASSEMBLY_SRC) && !defined(STACKMAN_BUILD_LIB)) +#define STACKMAN_SWITCH_NEED_INDIRECT 1 +#else +#define STACKMAN_SWITCH_NEED_INDIRECT 0 +#endif #if STACKMAN_SWITCH_NEED_INDIRECT #define STACKMAN_SWITCH_INASM_NAME _stackman_switch_inasm diff --git a/tests/test.c b/tests/test.c index a06f003..a8beffc 100644 --- a/tests/test.c +++ b/tests/test.c @@ -7,6 +7,8 @@ #include +#define assert_align(p) assert((intptr_t)(p) == STACKMAN_SP_ALIGN(p)) + /* simple test ithout modifying stack pointers. * test that the callback is called wiht valid * stackpoiners and the stage member in the @@ -83,6 +85,7 @@ void restore_stack(void *sp, void *buf, size_t size) void *jmp_cb(void* context, int opcode, void *sp) { + assert_align(sp); jmp *c = (jmp*)context; c->log[c->counter++] = c->val; if (c->val == 0) { @@ -108,7 +111,10 @@ void *jmp_cb(void* context, int opcode, void *sp) jmp *jmp_save(void *farptr) { jmp *res = (jmp*)malloc(sizeof(jmp)); + assert(res); memset(res, 0, sizeof(jmp)); + + /* add safety margin */ /* far end of stack, add buffer to catch memory backed registers, etc. */ res->stack_far = STACKMAN_SP_ADD((char*)farptr, 32); @@ -135,13 +141,12 @@ void test_02_called(jmp *c) assert(0); } -void test_02(void) +void test_02(void *stack_marker) { - int stack_marker; static jmp *c; jmp * tmp; - tmp = jmp_save(&stack_marker); + tmp = jmp_save(stack_marker); if (tmp) { c = tmp; assert(c->counter == 2); @@ -161,9 +166,8 @@ void test_02(void) } /* test stack stuff not changing */ -void test_03(void) +void test_03(void *stack_marker) { - int stack_marker; static jmp *c; jmp * tmp; int foo[2]; @@ -190,6 +194,7 @@ void test_03(void) void *test_04_cb(void* context, int _opcode, void *old_sp) { + assert_align(old_sp); ctxt01 *c = (ctxt01*)context; stackman_op_t opcode = (stackman_op_t)_opcode; assert(opcode == STACKMAN_OP_CALL || opcode == 100); @@ -201,17 +206,19 @@ void *test_04_cb(void* context, int _opcode, void *old_sp) c->sp[1] = old_sp; return 0; } + +/* test stackman_call() with a non-null stack pointer */ void test_04(void) { char *block, *stack, *stack2; - int stacksize=1024; + intptr_t stacksize=1024; int i, cnt; - void *res; ctxt01 ctxt; assert(STACKMAN_STACK_FULL_DESCENDING); block = (char*)malloc(stacksize); + assert(block); stack = block + stacksize; /* clear it all to a certain value */ memset(block, '\x7f', stacksize); @@ -242,26 +249,64 @@ void test_04(void) for(i=0; i<64; i++) cnt += stack2[-i] == '\x7f'; assert(cnt != 64); +} +/* test stackman_call() with a null stack pointer */ +void test_05(void) +{ + ctxt01 ctxt; + assert(STACKMAN_STACK_FULL_DESCENDING); + /* perform the call */ + stackman_call(test_04_cb, &ctxt, 0); + + /* verify that it was passed a stack */ + assert(ctxt.sp[1]); + assert(STACKMAN_SP_LE(ctxt.sp[0], ctxt.sp[1])); + + /* and that it was passed valid lower stack pointer */ + assert(STACKMAN_SP_LE(ctxt.sp[1], &ctxt)); } #endif +/* Test our various macros */ +void test_06() +{ + + int local=0; + void *away = STACKMAN_SP_FURTHEST; + void *close = STACKMAN_SP_NEAREST; + assert(STACKMAN_SP_LE(&local, away)); + assert(STACKMAN_SP_LS(&local, away)); + assert(STACKMAN_SP_LE(close, &local)); + assert(STACKMAN_SP_LS(close, &local)); + + assert(STACKMAN_SP_LE(&local, &local)); + assert(!STACKMAN_SP_LS(&local, &local)); + + assert((void*)STACKMAN_SP_ALIGN(away) == away); + +} int main(int argc, char*argv[]) { + int stack_marker = 0; test_01(); printf("test_01 ok\n"); - test_02(); + test_02((void*)&stack_marker); printf("test_02 ok\n"); - test_03(); + test_03((void*)&stack_marker); printf("test_03 ok\n"); #ifdef TEST_04 test_04(); printf("test_04 ok\n"); + test_05(); + printf("test_05 ok\n"); #endif + test_06(); + printf("test_06 ok\n"); return 0; } diff --git a/tests/test_asm.c b/tests/test_asm.c index 03533bf..72f1866 100644 --- a/tests/test_asm.c +++ b/tests/test_asm.c @@ -1,5 +1,6 @@ /* testing of inline asm functionality */ +#undef STACKMAN_INLINE_ASM #define STACKMAN_INLINE_ASM 1 #include "stackman.h" #include "stackman_impl.h" diff --git a/tests/test_static.c b/tests/test_static.c index eaa3806..69ad40e 100644 --- a/tests/test_static.c +++ b/tests/test_static.c @@ -2,6 +2,7 @@ * rather than linking in an object file. Only possible if the * implementation is inline asm. Real assembler must be linked */ +#undef STACKMAN_INLINE_ASM #define STACKMAN_INLINE_ASM 1 #define STACKMAN_LINKAGE_STATIC #include "stackman.h" diff --git a/get_abi.c b/tools/abiname.c similarity index 100% rename from get_abi.c rename to tools/abiname.c diff --git a/tools/abiname.sh b/tools/abiname.sh new file mode 100644 index 0000000..e899f05 --- /dev/null +++ b/tools/abiname.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# this script compiles and runs stackman/abiname.c which merely prints +# out the name of the abi. This can be used by makefiles to identify +# the correct library path to use to link the library +# Instead of just compiling and running, we will use the provided compiler +# and flags to just invoke the pre-processor. We then use the default +# compiler and linker to compile and link it. This ensures that the +# script works in cross-compilation environments and can actually +# run the provided code. +set -eu +here=$(dirname "$0") +mkdir -p "${here}/tmp" +tmp=$(mktemp "${here}/tmp/abinameXXX.c") + +#1 create the preprocessed file +CC=${1:-cc} +CFLAGS=${2:-} +${CC} ${CFLAGS} -I${here}/../stackman -E -o "${tmp}" "${here}/abiname.c" +#2 compile resulting file +cc -o "${tmp}.out" "${tmp}" +#3 run it +"${tmp}.out" \ No newline at end of file diff --git a/tools/strip-lib.py b/tools/strip-lib.py new file mode 100644 index 0000000..f783d81 --- /dev/null +++ b/tools/strip-lib.py @@ -0,0 +1,313 @@ +# nulls the timestamp filed in a windows .lib archive, +# making the lib reproducable. +# the time is the TimeDateStamp in the COFF file header, four bytes at offset 4 +# See https://blog.conan.io/2019/09/02/Deterministic-builds-with-C-C++.html +# also: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#archive-library-file-format +# +# There are some additional fixes added for reproducability, such as fixing the zero-padding of names in the coff +# section headers. + +import struct +import sys + +verbose = True + +libheader = b"!\n" + + +def main(): + infilename = sys.argv[1] + if len(sys.argv) > 2: + outfilename = sys.argv[2] + else: + outfilename = infilename + + with open(infilename, "rb") as fp: + lib = read_lib(fp) + strip_lib_timestamp(lib) + with open(outfilename, "wb") as fp: + write_lib(fp, lib) + + +def read_lib(fp): + """ + read microsoft .lib file, + """ + # lib file header + h = fp.read(len(libheader)) + assert h == libheader + + # read first and second link members + h1 = header_read(fp) + p = fp.tell() + if verbose: + print("header", h1) + m1 = first_lm_read(fp) + assert fp.tell() - p == h1["size"] + if verbose: + print("first linker member", m1) + + h2 = header_read(fp) + if verbose: + print("header", h2) + p = fp.tell() + m2 = second_lm_read(fp) + assert fp.tell() - p == h2["size"] + if verbose: + print("second linker member", m2) + + result = { + "h1": h1, + "m1": m1, + "h2": h2, + "m2": m2, + "hl": None, + "longnames": [], + "ho": [], + "o": [], + } + + # now we might have an optional longnames member + h = header_read(fp) + if not h: + return result + + if h["name"] == "//": + result["hl"] = h + p = fp.tell() + while fp.tell() < p + h["size"]: + result["longnames"].append(readcstr(fp)) + if verbose: + print("header", h) + print("longnames", result["longnames"]) + h = None + + # now read the headers, possibly we alread read one above. + while True: + if h is None: + h = header_read(fp) + if h is None: + return result + + result["ho"].append(h) + result["o"].append(fp.read(h["size"])) + if verbose: + print("header:", result["ho"][-1]) + print("coff length:", len(result["o"][-1])) + h = None + + +def write_lib(fp, lib): + fp.write(libheader) + header_write(fp, lib["h1"]) + first_lm_write(fp, lib["m1"]) + header_write(fp, lib["h2"]) + second_lm_write(fp, lib["m2"]) + + if lib["hl"]: + header_write(fp, lib["hl"]) + for s in lib["longnames"]: + writecstr(fp, s) + + for h, c in zip(lib["ho"], lib["o"]): + header_write(fp, h) + fp.write(c) + + +def strip_lib_timestamp(lib): + def fix_header(h): + h["date"] = "-1" + + fix_header(lib["h1"]) + fix_header(lib["h2"]) + if lib["hl"]: + fix_header(lib["hl"]) + for h in lib["ho"]: + fix_header(h) + lib["o"] = [strip_coff_timestamp(c) for c in lib["o"]] + lib["o"] = [fix_coff_null_padding(c) for c in lib["o"]] + + +def header_read(fp): + """ + read a header entry from a microsoft archive + """ + + # header can start with optional newline + optnl = read_optional_nl(fp) + + name = fp.read(16) + if len(name) < 16: + return None # eof + name = name.decode("ascii").strip() + date = fp.read(12).decode("ascii").strip() + uid = fp.read(6).decode("ascii").strip() + gid = fp.read(6).decode("ascii").strip() + mode = fp.read(8).decode("ascii").strip() + size = fp.read(10).decode("ascii").strip() + size = eval(size) + eoh = fp.read(2) + assert eoh == b"\x60\x0a" + return { + "optnl": optnl, + "name": name, + "date": date, + "uid": uid, + "gid": gid, + "mode": mode, + "size": size, + } + + +def header_write(fp, h): + def writestr(s, n): + """helper to write space padded string of fixed length""" + e = s.encode("ascii") + b" " * n + fp.write(e[:n]) + + if h["optnl"]: + fp.write(h["optnl"]) + writestr(h["name"], 16) + writestr(h["date"], 12) + writestr(h["uid"], 6) + writestr(h["gid"], 6) + writestr(h["mode"], 8) + writestr(str(h["size"]), 10) + fp.write(b"\x60\x0a") + + +def first_lm_read(fp): + nos = fp.read(4) + nos = struct.unpack(">L", nos)[0] # unsigned long, big-endian + + offsets = [] + strings = [] + for _ in range(nos): + offset = fp.read(4) + offsets.append(struct.unpack(">L", offset)[0]) + for _ in range(nos): + strings.append(readcstr(fp)) + return {"offsets": offsets, "strings": strings} + # sometimes there is an extra \0a after the strings + + +def first_lm_write(fp, lm): + nos = len(lm["offsets"]) + fp.write(struct.pack(">L", nos)) + for o in lm["offsets"]: + fp.write(struct.pack(">L", o)) + for s in lm["strings"]: + writecstr(fp, s) + + +def second_lm_read(fp): + # number of members + m = struct.unpack("= 0: + # everything after first null is null + shortname = name[:i] + namenew = (shortname + b"\0" * 8)[:8] + if name != namenew: + sections[n] = namenew + s[8:] + modified = True + if verbose: + print( + "Fixed null padding of COFF section header name %r" % shortname + ) + if modified: + start = header + b"".join(sections) + coff = start + coff[len(start) :] + return coff + + +if __name__ == "__main__": + main() diff --git a/vs2017/build.cmd b/vs2017/build.cmd new file mode 100644 index 0000000..c3d551c --- /dev/null +++ b/vs2017/build.cmd @@ -0,0 +1,4 @@ +rem Build with visualstudio2017buildtools +echo Building %1 +call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" +msbuild stackman.sln /p:Platform=%1 diff --git a/vs2017/stackman.sln b/vs2017/stackman.sln new file mode 100644 index 0000000..6dff39b --- /dev/null +++ b/vs2017/stackman.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}" + ProjectSection(ProjectDependencies) = postProject + {BF7D0638-AC4F-4206-B426-66CDDD468281} = {BF7D0638-AC4F-4206-B426-66CDDD468281} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stackman", "stackman\stackman.vcxproj", "{BF7D0638-AC4F-4206-B426-66CDDD468281}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM.ActiveCfg = Debug|ARM + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM.Build.0 = Debug|ARM + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM64.Build.0 = Debug|ARM64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x64.ActiveCfg = Debug|x64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x64.Build.0 = Debug|x64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x86.ActiveCfg = Debug|Win32 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x86.Build.0 = Debug|Win32 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM.ActiveCfg = Debug|ARM + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM.Build.0 = Debug|ARM + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM64.Build.0 = Debug|ARM64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x64.ActiveCfg = Debug|x64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x64.Build.0 = Debug|x64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x86.ActiveCfg = Debug|Win32 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x86.Build.0 = Debug|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A71982F0-A89C-45AB-9F23-149A1983A11B} + EndGlobalSection +EndGlobal diff --git a/vs2017/stackman/stackman.vcxproj b/vs2017/stackman/stackman.vcxproj new file mode 100644 index 0000000..b27234a --- /dev/null +++ b/vs2017/stackman/stackman.vcxproj @@ -0,0 +1,230 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + + 16.0 + Win32Proj + {bf7d0638-ac4f-4206-b426-66cddd468281} + stackman + 10.0.18362.0 + stackman + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + true + v141 + Unicode + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\..\lib\win_x86\ + $(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\..\lib\win_x64\ + + + true + $(SolutionDir)\..\lib\win_arm\ + + + true + $(SolutionDir)\..\lib\win_arm64\ + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + + + + + + + + + + + false + Default + + + AssemblyCode + AssemblyCode + AssemblyCode + false + false + false + Default + Default + Default + false + false + false + true + true + true + true + + + + + false + true + true + true + + + Document + true + true + true + + + true + true + true + + + true + true + true + + + + + + + + \ No newline at end of file diff --git a/vs2017/stackman/stackman.vcxproj.filters b/vs2017/stackman/stackman.vcxproj.filters new file mode 100644 index 0000000..9f57a1f --- /dev/null +++ b/vs2017/stackman/stackman.vcxproj.filters @@ -0,0 +1,67 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/vs2017/stackman/template.c b/vs2017/stackman/template.c new file mode 100644 index 0000000..2fe7dbd --- /dev/null +++ b/vs2017/stackman/template.c @@ -0,0 +1,15 @@ +#include "..\..\stackman\stackman.h" + +/* + * template file to create assembly code (template.asm) to modify and add to real assembler. + */ + +void* stackman_call_templ(stackman_cb_t callback, void* context, void* stack) +{ + // We use this variabl here for the template generation. Int the modified assembly + // code, we will store the ebp (base pointer) in that place on the stack, + // before storing the original unmodified stack pointer there. + void* localvar = stack; + return callback(context, 2, localvar); + +} \ No newline at end of file diff --git a/vs2017/test/test.vcxproj b/vs2017/test/test.vcxproj new file mode 100644 index 0000000..9d14c4b --- /dev/null +++ b/vs2017/test/test.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + + 16.0 + Win32Proj + {cafdbd3e-9d0d-4e90-bb6d-6c2a19f5ef53} + test + 10.0.18362.0 + + + + Application + true + v141 + Unicode + + + Application + true + v141 + Unicode + + + Application + true + v141 + Unicode + + + Application + true + v141 + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + true + + + true + + + true + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + ..\..\lib\win_x86;%(AdditionalLibraryDirectories) + stackman.lib;%(AdditionalDependencies) + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_x64;%(AdditionalLibraryDirectories) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_arm;%(AdditionalLibraryDirectories) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_arm64;%(AdditionalLibraryDirectories) + + + + + + + + + \ No newline at end of file diff --git a/vs2017/test/test.vcxproj.filters b/vs2017/test/test.vcxproj.filters new file mode 100644 index 0000000..154d04b --- /dev/null +++ b/vs2017/test/test.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/vs2019/stackman.sln b/vs2019/stackman.sln new file mode 100644 index 0000000..6dff39b --- /dev/null +++ b/vs2019/stackman.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}" + ProjectSection(ProjectDependencies) = postProject + {BF7D0638-AC4F-4206-B426-66CDDD468281} = {BF7D0638-AC4F-4206-B426-66CDDD468281} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stackman", "stackman\stackman.vcxproj", "{BF7D0638-AC4F-4206-B426-66CDDD468281}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM.ActiveCfg = Debug|ARM + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM.Build.0 = Debug|ARM + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM64.Build.0 = Debug|ARM64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x64.ActiveCfg = Debug|x64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x64.Build.0 = Debug|x64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x86.ActiveCfg = Debug|Win32 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x86.Build.0 = Debug|Win32 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM.ActiveCfg = Debug|ARM + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM.Build.0 = Debug|ARM + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM64.Build.0 = Debug|ARM64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x64.ActiveCfg = Debug|x64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x64.Build.0 = Debug|x64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x86.ActiveCfg = Debug|Win32 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x86.Build.0 = Debug|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A71982F0-A89C-45AB-9F23-149A1983A11B} + EndGlobalSection +EndGlobal diff --git a/vs2019/stackman/stackman.vcxproj b/vs2019/stackman/stackman.vcxproj new file mode 100644 index 0000000..5a6a77a --- /dev/null +++ b/vs2019/stackman/stackman.vcxproj @@ -0,0 +1,230 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + + 16.0 + Win32Proj + {bf7d0638-ac4f-4206-b426-66cddd468281} + stackman + 10.0 + stackman + + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + true + v142 + Unicode + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\..\lib\win_x86\ + $(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\..\lib\win_x64\ + + + true + $(SolutionDir)\..\lib\win_arm\ + + + true + $(SolutionDir)\..\lib\win_arm64\ + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + + + + + + + + + + + false + Default + + + AssemblyCode + AssemblyCode + AssemblyCode + false + false + false + Default + Default + Default + false + false + false + true + true + false + true + + + + + false + true + true + true + + + Document + true + true + true + + + true + true + true + + + true + true + true + + + + + + + + \ No newline at end of file diff --git a/vs2019/stackman/stackman.vcxproj.filters b/vs2019/stackman/stackman.vcxproj.filters new file mode 100644 index 0000000..9f57a1f --- /dev/null +++ b/vs2019/stackman/stackman.vcxproj.filters @@ -0,0 +1,67 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/vs2019/stackman/template.c b/vs2019/stackman/template.c new file mode 100644 index 0000000..2fe7dbd --- /dev/null +++ b/vs2019/stackman/template.c @@ -0,0 +1,15 @@ +#include "..\..\stackman\stackman.h" + +/* + * template file to create assembly code (template.asm) to modify and add to real assembler. + */ + +void* stackman_call_templ(stackman_cb_t callback, void* context, void* stack) +{ + // We use this variabl here for the template generation. Int the modified assembly + // code, we will store the ebp (base pointer) in that place on the stack, + // before storing the original unmodified stack pointer there. + void* localvar = stack; + return callback(context, 2, localvar); + +} \ No newline at end of file diff --git a/vs2019/test/test.vcxproj b/vs2019/test/test.vcxproj new file mode 100644 index 0000000..93dc2cf --- /dev/null +++ b/vs2019/test/test.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + + 16.0 + Win32Proj + {cafdbd3e-9d0d-4e90-bb6d-6c2a19f5ef53} + test + 10.0 + + + + Application + true + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + true + v142 + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + true + + + true + + + true + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + ..\..\lib\win_x86;%(AdditionalLibraryDirectories) + stackman.lib;%(AdditionalDependencies) + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_x64;%(AdditionalLibraryDirectories) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_arm;%(AdditionalLibraryDirectories) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_arm64;%(AdditionalLibraryDirectories) + + + + + + + + + \ No newline at end of file diff --git a/vs2019/test/test.vcxproj.filters b/vs2019/test/test.vcxproj.filters new file mode 100644 index 0000000..154d04b --- /dev/null +++ b/vs2019/test/test.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/vs2022/stackman.sln b/vs2022/stackman.sln new file mode 100644 index 0000000..6dff39b --- /dev/null +++ b/vs2022/stackman.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}" + ProjectSection(ProjectDependencies) = postProject + {BF7D0638-AC4F-4206-B426-66CDDD468281} = {BF7D0638-AC4F-4206-B426-66CDDD468281} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stackman", "stackman\stackman.vcxproj", "{BF7D0638-AC4F-4206-B426-66CDDD468281}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM.ActiveCfg = Debug|ARM + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM.Build.0 = Debug|ARM + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|ARM64.Build.0 = Debug|ARM64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x64.ActiveCfg = Debug|x64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x64.Build.0 = Debug|x64 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x86.ActiveCfg = Debug|Win32 + {CAFDBD3E-9D0D-4E90-BB6D-6C2A19F5EF53}.Debug|x86.Build.0 = Debug|Win32 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM.ActiveCfg = Debug|ARM + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM.Build.0 = Debug|ARM + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|ARM64.Build.0 = Debug|ARM64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x64.ActiveCfg = Debug|x64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x64.Build.0 = Debug|x64 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x86.ActiveCfg = Debug|Win32 + {BF7D0638-AC4F-4206-B426-66CDDD468281}.Debug|x86.Build.0 = Debug|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A71982F0-A89C-45AB-9F23-149A1983A11B} + EndGlobalSection +EndGlobal diff --git a/vs2022/stackman/stackman.vcxproj b/vs2022/stackman/stackman.vcxproj new file mode 100644 index 0000000..cb9614a --- /dev/null +++ b/vs2022/stackman/stackman.vcxproj @@ -0,0 +1,230 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + + 16.0 + Win32Proj + {bf7d0638-ac4f-4206-b426-66cddd468281} + stackman + 10.0 + stackman + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + true + v143 + Unicode + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\..\lib\win_x86\ + $(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\..\lib\win_x64\ + + + true + $(SolutionDir)\..\lib\win_arm\ + + + true + $(SolutionDir)\..\lib\win_arm64\ + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + OldStyle + + + + + true + + + /Brepro %(AdditionalOptions) + + + + + + + + + + + + + + + false + Default + + + AssemblyCode + AssemblyCode + AssemblyCode + false + false + false + Default + Default + Default + false + false + false + true + true + false + true + + + + + false + true + true + true + + + Document + true + true + true + + + true + true + true + + + true + true + true + + + + + + + + \ No newline at end of file diff --git a/vs2022/stackman/stackman.vcxproj.filters b/vs2022/stackman/stackman.vcxproj.filters new file mode 100644 index 0000000..9f57a1f --- /dev/null +++ b/vs2022/stackman/stackman.vcxproj.filters @@ -0,0 +1,67 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/vs2022/stackman/template.c b/vs2022/stackman/template.c new file mode 100644 index 0000000..2fe7dbd --- /dev/null +++ b/vs2022/stackman/template.c @@ -0,0 +1,15 @@ +#include "..\..\stackman\stackman.h" + +/* + * template file to create assembly code (template.asm) to modify and add to real assembler. + */ + +void* stackman_call_templ(stackman_cb_t callback, void* context, void* stack) +{ + // We use this variabl here for the template generation. Int the modified assembly + // code, we will store the ebp (base pointer) in that place on the stack, + // before storing the original unmodified stack pointer there. + void* localvar = stack; + return callback(context, 2, localvar); + +} \ No newline at end of file diff --git a/vs2022/test/test.vcxproj b/vs2022/test/test.vcxproj new file mode 100644 index 0000000..266eb72 --- /dev/null +++ b/vs2022/test/test.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + + 16.0 + Win32Proj + {cafdbd3e-9d0d-4e90-bb6d-6c2a19f5ef53} + test + 10.0 + + + + Application + true + v143 + Unicode + + + Application + true + v143 + Unicode + + + Application + true + v143 + Unicode + + + Application + true + v143 + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + true + + + true + + + true + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + ..\..\lib\win_x86;%(AdditionalLibraryDirectories) + stackman.lib;%(AdditionalDependencies) + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_x64;%(AdditionalLibraryDirectories) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_arm;%(AdditionalLibraryDirectories) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\stackman + + + Console + true + stackman.lib;%(AdditionalDependencies) + ..\..\lib\win_arm64;%(AdditionalLibraryDirectories) + + + + + + + + + \ No newline at end of file diff --git a/vs2022/test/test.vcxproj.filters b/vs2022/test/test.vcxproj.filters new file mode 100644 index 0000000..154d04b --- /dev/null +++ b/vs2022/test/test.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file