├── .gitignore ├── .gitmodules ├── Makefile ├── README.adoc ├── common.h ├── download-dependencies ├── fail.S ├── hello_common.S ├── main.c ├── params.mk ├── t ├── v7 ├── Makefile ├── c │ ├── Makefile │ ├── README.adoc │ ├── add.c │ ├── linux │ │ ├── Makefile │ │ ├── hello.c │ │ ├── params.mk │ │ └── t │ ├── params.mk │ ├── params_private.mk │ └── t ├── common.h ├── common_arch.h ├── linux │ ├── Makefile │ ├── README.adoc │ ├── hello.S │ ├── params.mk │ └── t ├── main.c ├── params.mk ├── params_private.mk └── t └── v8 ├── Makefile ├── add.S ├── c ├── Makefile ├── inc.c ├── linux │ ├── Makefile │ ├── hello.c │ ├── params.mk │ └── t ├── multiline.cpp ├── params.mk ├── params_private.mk └── t ├── common.h ├── common_arch.h ├── linux ├── Makefile ├── README.adoc ├── hello.S ├── params.mk └── t ├── main.c ├── params.mk ├── params_private.mk └── t /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.log 3 | *.o 4 | *.objdump 5 | *.out 6 | *.swp 7 | *.tmp 8 | /out 9 | core 10 | tmp.* 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "qemu"] 2 | path = qemu 3 | url = git://git.qemu.org/qemu.git 4 | [submodule "binutils-gdb"] 5 | path = binutils-gdb 6 | url = git://sourceware.org/git/binutils-gdb.git 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | AS = $(BINUTILS_BIN_DIR)/$(ARCH)-elf-as 4 | AS_TARGET = $(AS) 5 | ASM_EXT = .S 6 | ASFLAGS = --gdwarf-2 -march=$(MARCH_AS) $(ASFLAGS_EXTRA) 7 | BINUTILS_BIN_DIR = $(BINUTILS_INSTALL_DIR)/bin 8 | CC = $(PREFIX_PATH)gcc 9 | CXX = $(PREFIX_PATH)g++ 10 | CFLAGS = -std=c99 $(CCFLAGS) 11 | CCFLAGS = -ggdb3 -march=$(MARCH) -pedantic -Wall -Wextra $(CCFLAGS_QEMU) $(CCFLAGS_EXTRA) $(CCFLAGS_CLI) 12 | CXXFLAGS = -std=c++11 $(CCFLAGS) 13 | CPP = $(PREFIX_PATH)cpp 14 | CPP_EXT = $(ASM_EXT).tmp 15 | # no-pie: https://stackoverflow.com/questions/51310756/how-to-gdb-step-debug-a-dynamically-linked-executable-in-qemu-user-mode 16 | # And the flag not present in Raspbian 2017 which has an ancient gcc 4.9, so we have to remove it. 17 | CCFLAGS_QEMU = -fno-pie -no-pie 18 | COMMON_HEADER = common.h 19 | CTNG = 20 | C_EXT = .c 21 | CXX_EXT = .cpp 22 | DEFAULT_SYSROOT = /usr/$(PREFIX) 23 | DRIVER_BASENAME_NOEXT = main 24 | DRIVER_BASENAME = main$(C_EXT) 25 | DRIVER_OBJ = $(DRIVER_BASENAME_NOEXT)$(OBJ_EXT) 26 | GDB = $(BINUTILS_BIN_DIR)/$(ARCH)-elf-gdb 27 | GDB_BREAK = asm_main_after_prologue 28 | GDB_EXPERT = 29 | GDB_PORT = 1234 30 | MARCH_AS = $(MARCH) 31 | NATIVE = 32 | OBJDUMP = $(PREFIX_PATH)objdump 33 | OBJDUMP_EXT = .objdump 34 | OBJ_EXT = .o 35 | OUT_EXT = .out 36 | RECURSE = 37 | ROOT_DIR = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 38 | BINUTILS_SRC_DIR = $(ROOT_DIR)/binutils-gdb 39 | BINUTILS_OUT_DIR = $(OUT_DIR)/binutils-gdb 40 | BINUTILS_BUILD_DIR = $(BINUTILS_OUT_DIR)/build/$(ARCH) 41 | BINUTILS_GDB_TARGET = binutils-gdb 42 | BINUTILS_INSTALL_DIR = $(BINUTILS_OUT_DIR)/install 43 | QEMU_SRC_DIR = $(ROOT_DIR)/qemu 44 | QEMU_EXE = $(QEMU_BUILD_DIR)/$(ARCH)-linux-user/qemu-$(ARCH) 45 | OUT_DIR = $(ROOT_DIR)/out 46 | QEMU_BUILD_DIR = $(OUT_DIR)/qemu/$(ARCH) 47 | DOC_OUT = $(OUT_DIR)/README.html 48 | RUN_CMD = $(QEMU_EXE) -L $(SYSROOT) -E LD_BIND_NOW=1 49 | TEST = test 50 | 51 | ifeq ($(CTNG),) 52 | PREFIX_PATH = $(PREFIX)- 53 | SYSROOT = $(DEFAULT_SYSROOT) 54 | else 55 | PREFIX_PATH = $(CTNG)/$(PREFIX)/bin/$(PREFIX)- 56 | SYSROOT = $(CTNG)/$(PREFIX)/$(PREFIX)/sysroot 57 | endif 58 | 59 | INS_ASM = $(wildcard *$(ASM_EXT)) 60 | INS_ASM_NOEXT = $(basename $(INS_ASM)) 61 | OUTS_ASM = $(addsuffix $(OUT_EXT), $(INS_ASM_NOEXT)) 62 | INS_C = $(filter-out $(DRIVER_BASENAME), $(wildcard *$(C_EXT))) 63 | INS_C_NOEXT = $(basename $(INS_C)) 64 | OUTS_C = $(addsuffix $(OUT_EXT), $(INS_C_NOEXT)) 65 | INS_CXX = $(filter-out $(DRIVER_BASENAME), $(wildcard *$(CXX_EXT))) 66 | INS_CXX_NOEXT = $(basename $(INS_CXX)) 67 | OUTS_CXX = $(addsuffix $(OUT_EXT), $(INS_CXX_NOEXT)) 68 | OBJDUMPS = $(addsuffix $(OBJDUMP_EXT), $(INS_ASM_NOEXT) $(INS_C_NOEXT)) 69 | OUTS = $(OUTS_ASM) $(OUTS_C) $(OUTS_CXX) 70 | TESTS = $(filter-out $(addsuffix $(OUT_EXT), $(SKIP_TESTS)),$(OUTS)) 71 | 72 | -include params.mk 73 | -include params_private.mk 74 | 75 | ifeq ($(NATIVE),y) 76 | AS = as 77 | AS_TARGET = 78 | CCFLAGS_QEMU = 79 | PREFIX_PATH = 80 | QEMU_EXE = 81 | RUN_CMD = PATH=".:${PATH}" 82 | endif 83 | 84 | ifeq ($(FREESTAND),y) 85 | CCFLAGS_EXTRA += -ffreestanding -nostdlib -static 86 | DRIVER_OBJ = 87 | COMMON_HEADER = 88 | endif 89 | 90 | .PHONY: all clean doc objdump $(BINUTILS_GDB_TARGET) binutils-gdb-clean qemu qemu-clean test $(RECURSE) 91 | .PRECIOUS: %$(OBJ_EXT) 92 | 93 | all: $(BINUTILS_GDB_TARGET) $(OUTS) qemu 94 | for phony in $(RECURSE); do \ 95 | $(MAKE) -C $${phony}; \ 96 | done 97 | 98 | %$(OUT_EXT): %$(OBJ_EXT) $(DRIVER_OBJ) 99 | $(CC) $(CFLAGS) -o '$@' '$<' $(DRIVER_OBJ) 100 | 101 | %$(OBJDUMP_EXT): %$(OUT_EXT) 102 | $(OBJDUMP) -S '$<' > '$@' 103 | 104 | %$(OBJ_EXT): %$(C_EXT) 105 | $(CC) -c $(CFLAGS) -o '$@' '$<' 106 | 107 | %$(OBJ_EXT): %$(CXX_EXT) 108 | $(CXX) -c $(CXXFLAGS) -o '$@' '$<' 109 | 110 | %$(OBJ_EXT): %$(ASM_EXT) $(COMMON_HEADER) 111 | $(CPP) -o '$(basename $<)$(CPP_EXT)' '$<' 112 | $(AS) $(ASFLAGS) -c -o '$@' '$(basename $<)$(CPP_EXT)' 113 | 114 | $(DRIVER_OBJ): $(DRIVER_BASENAME) 115 | $(CC) $(CFLAGS) -c -o '$@' '$<' 116 | 117 | clean: 118 | rm -f *.html *.o *.objdump *.out *$(CPP_EXT) 119 | for phony in $(RECURSE); do \ 120 | $(MAKE) -C $${phony} clean; \ 121 | done 122 | 123 | doc: $(DOC_OUT) 124 | 125 | $(DOC_OUT): README.adoc 126 | asciidoctor -b html5 -o '$@' -v '$<' 127 | 128 | gdb-%: %$(OUT_EXT) $(QEMU_EXE) 129 | if [ '$(NATIVE)' = y ]; then \ 130 | gdb_exe=gdb; \ 131 | gdb_after='-ex run'; \ 132 | gdb_args=; \ 133 | else \ 134 | $(RUN_CMD) -g $(GDB_PORT) '$<' & \ 135 | gdb_exe='$(GDB)' \ 136 | gdb_args="\ 137 | -ex 'set architecture $(ARCH)' \ 138 | -ex 'set sysroot $(SYSROOT)' \ 139 | -ex 'target remote localhost:$(GDB_PORT)' \ 140 | "; \ 141 | if [ ! '$(FREESTAND)' = y ]; then \ 142 | gdb_after='-ex continue'; \ 143 | fi; \ 144 | fi; \ 145 | if [ ! '$(GDB_EXPERT)' = y ]; then \ 146 | gdb_args="$${gdb_args} \ 147 | -nh \ 148 | -ex 'set confirm off' \ 149 | -ex 'layout split' \ 150 | "; \ 151 | fi; \ 152 | gdb_cmd="$${gdb_exe} \ 153 | -q \ 154 | -ex 'file $<' \ 155 | $${gdb_args} \ 156 | -ex 'break $(GDB_BREAK)' \ 157 | $$gdb_after \ 158 | "; \ 159 | echo "$$gdb_cmd"; \ 160 | eval "$$gdb_cmd" 161 | 162 | objdump: $(OBJDUMPS) 163 | for phony in $(RECURSE); do \ 164 | $(MAKE) -C $${phony} objdump; \ 165 | done 166 | 167 | qemu: $(QEMU_EXE) 168 | 169 | $(QEMU_EXE): 170 | mkdir -p '$(QEMU_BUILD_DIR)' 171 | cd '$(QEMU_BUILD_DIR)' && \ 172 | "$(QEMU_SRC_DIR)/configure" \ 173 | --enable-debug \ 174 | --target-list="$(ARCH)-linux-user" \ 175 | && \ 176 | make -j`nproc` 177 | 178 | qemu-clean: 179 | rm -rf '$(QEMU_BUILD_DIR)' 180 | for phony in $(QEMU_RECURSE); do \ 181 | $(MAKE) -C $${phony} qemu-clean; \ 182 | done 183 | 184 | binutils-gdb: $(AS_TARGET) 185 | 186 | $(AS): 187 | mkdir -p '$(BINUTILS_BUILD_DIR)' 188 | cd '$(BINUTILS_BUILD_DIR)' && \ 189 | "$(BINUTILS_SRC_DIR)/configure" \ 190 | --target '$(ARCH)-elf' \ 191 | --prefix '$(BINUTILS_INSTALL_DIR)' \ 192 | && \ 193 | make -j`nproc` && \ 194 | make -j`nproc` install 195 | 196 | test-%: %$(OUT_EXT) $(QEMU_EXE) 197 | $(RUN_CMD) '$<' 198 | 199 | test: all 200 | @\ 201 | if [ -x $(TEST) ]; then \ 202 | ./$(TEST) '$(OUT_EXT)' ;\ 203 | else\ 204 | fail=false ;\ 205 | for t in $(TESTS); do\ 206 | echo "$(RUN_CMD) $$t"; \ 207 | if ! $(RUN_CMD) "$$t"; then \ 208 | fail=true ;\ 209 | break ;\ 210 | fi ;\ 211 | done ;\ 212 | if $$fail; then \ 213 | echo "TEST FAILED: $$t" ;\ 214 | exit 1 ;\ 215 | fi ;\ 216 | fi ;\ 217 | for phony in $(RECURSE); do \ 218 | $(MAKE) -C $${phony} test; \ 219 | done; \ 220 | echo 'ALL TESTS PASSED' 221 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = ARM Assembly Cheat 2 | :idprefix: 3 | :idseparator: - 4 | :sectanchors: 5 | :sectlinks: 6 | :sectnumlevels: 6 7 | :sectnums: 8 | :toc: macro 9 | :toclevels: 6 10 | :toc-title: 11 | 12 | Almost all the content in this repository has been moved to: https://github.com/cirosantilli/linux-kernel-module-cheat#userland-assembly 13 | 14 | Only the core infrastructure of this repo was left here. No major new features are intended to be added here. 15 | 16 | Notable advantages of LKMC repository include: 17 | 18 | * a single unified cross arch setup for ARM and x86_64, with cross arch concepts all nicely factored out 19 | * gem5 support. This is because we have integration of QEMU / gem5 / buildroot setups already done there 20 | * parallel testing. Mostly because the build system there is Python, which is more flexible. 21 | * other stuff I can't remember right now. That setup just has a ton of features, and will continue to get more and more ;-) 22 | 23 | There is only one use case left for this repository: since this is more minimal, it is easier to upgrdte to the latest binutils-gdb here without breaking unrelated stuff, in order to get very latest instructions. 24 | 25 | The initial motivation for that was <> although that specific case has already been moved to LKMC as well! 26 | 27 | I might however start taking some risks on LKMC and upgrading binutils-gdb to master when needed anyways, or just build the latest binutils-gdb myself over there for userland only. 28 | 29 | == Old README 30 | 31 | Here is the old README with only infrastructure sections left. 32 | 33 | ARMv7 and <> assembly userland minimal examples tutorial. Runnable <> on x86 hosts with QEMU user mode or natively on ARM targets. Nice <> setup. Tested on Ubuntu 18.04 host and <> and <> targets. Baremetal setup at: https://github.com/cirosantilli/linux-kernel-module-cheat#baremetal-setup x86 cheat at: https://github.com/cirosantilli/x86-assembly-cheat 34 | 35 | toc::[] 36 | 37 | == Getting started 38 | 39 | On Ubuntu, clone, configure, build QEMU and Binutils from source, run all ARMv7 and ARMv8 examples through QEMU user, and assert that they exit with status 0: 40 | 41 | .... 42 | git clone --recursive https://github.com/cirosantilli/arm-assembly-cheat 43 | cd arm-assembly-cheat 44 | ./download-dependencies 45 | make test 46 | echo $? 47 | .... 48 | 49 | Expected outcome: the exit status is successful: 50 | 51 | .... 52 | 0 53 | .... 54 | 55 | For other operating systems, see: <>. 56 | 57 | We compile our own Binutils and QEMU to be able to use the newest ISA features. Those projects build pretty fast (~10 minutes), so it is fine. The cleanest thing would be to also compile GCC with <>. 58 | 59 | The armv7 examples are all located under the link:v7[] directory. Run all of them: 60 | 61 | .... 62 | cd v7 63 | make test 64 | echo $? 65 | .... 66 | 67 | Run just one of them: 68 | 69 | .... 70 | cd v7 71 | make test- 72 | echo $? 73 | .... 74 | 75 | E.g.: 76 | 77 | .... 78 | make test-add 79 | .... 80 | 81 | will run link:userland/arch/arm/add.S[]. 82 | 83 | This just tests some assertions, but does not output anything. See: <>. 84 | 85 | Alternatively, to help with tab completion, the following shortcuts all do the same thing as `make test-add`: 86 | 87 | .... 88 | ./t add 89 | ./t add. 90 | ./t add.out 91 | .... 92 | 93 | <> examples are all located under the link:v8[] directory. They can be run in the same way as ARMv7 examples: 94 | 95 | .... 96 | cd v8 97 | make test-movk 98 | .... 99 | 100 | Just build the examples without running: 101 | 102 | .... 103 | make 104 | .... 105 | 106 | Clean the examples: 107 | 108 | .... 109 | make clean 110 | .... 111 | 112 | This does not clean QEMU builds themselves. To do that run: 113 | 114 | .... 115 | make qemu-clean 116 | .... 117 | 118 | === Asserts 119 | 120 | Almost all example don't output anything, they just assert that the computations are as expected and exit 0 is that was the case. 121 | 122 | Failures however output clear error messages. 123 | 124 | Try messing with the examples to see them fail, e.g. modify link:userland/arch/arm/add.S[] to contain: 125 | 126 | .... 127 | mov r0, #1 128 | add r1, r0, #2 129 | ASSERT_EQ(r1, 4) 130 | .... 131 | 132 | and then watch it fail: 133 | 134 | .... 135 | cd v7 136 | make test-add 137 | .... 138 | 139 | with: 140 | 141 | .... 142 | error 1 at line 12 143 | Makefile:138: recipe for target 'test-add' failed 144 | error 1 at line 12 145 | .... 146 | 147 | since `1 + 2` tends to equal `3` and not `4`. 148 | 149 | So look how nice we are: we even gave you the line number `12` of the failing assert! 150 | 151 | === Getting started on non-Ubuntu operating systems 152 | 153 | If you are not on an Ubuntu host machine, here are some ways in which you can use this repo. 154 | 155 | ==== Other Linux distro hosts 156 | 157 | For other Linux distros, you can either: 158 | 159 | * have a look at what `download-dependencies` does and adapt it to your distro. It should be easy, then proceed normally. 160 | + 161 | Might fail due to some incompatibility, but likely won't. 162 | * run this repo with <>. Requires you to know some Docker boilerplate, but cannot (?) fail. 163 | 164 | ===== Docker host setup 165 | 166 | .... 167 | sudo apt install docker 168 | sudo docker create -it --name arm-assembly-cheat -w "/host/$(pwd)" -v "/:/host" ubuntu:18.04 169 | sudo docker exec -it arm-assembly-cheat /bin/bash 170 | .... 171 | 172 | Then inside Docker just add the `--docker` flag to `./download-dependencies` and proceed otherwise normally: 173 | 174 | .... 175 | ./download-dependencies --docker 176 | make test 177 | .... 178 | 179 | The `download-dependencies` takes a while because `build-dep binutils` is large. 180 | 181 | We share the repository between Docker and host, so you can just edit the files on host with your favorite text editor, and then just run them from inside Docker. 182 | 183 | TODO: GDB TUI GUI is broken inside Docker due to terminal quirks. Forwarding the port and connecting from host will likely work, but I'm lazy to try it out now. 184 | 185 | ==== Non-Linux host 186 | 187 | For non-Linux systems, the easiest thing to do is to use an Ubuntu virtual machine such as VirtualBox: link:https://askubuntu.com/questions/142549/how-to-install-ubuntu-on-virtualbox[]. 188 | 189 | Porting is not however impossible because we use the C standard library for portability, see: <>. Pull requests are welcome. 190 | 191 | [[rpi2]] 192 | ==== Raspberry Pi 2 native 193 | 194 | Yay! Let's see if this actually works on real hardware, or if it is just an emulation pipe dream? 195 | 196 | Tested on link:https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-11-15/2018-11-13-raspbian-stretch-lite.zip[Raspbian Lite 2018-11-13] with this repo at commit bcddf29c8e00b30afe7b3643558b25f22a64405b. 197 | 198 | For now, we will just compile natively, since I'm not in the mood for cross compilation hell today. 199 | 200 | link:https://en.wikipedia.org/wiki/Raspberry_Pi[According to Wikipedia] the Raspberry Pi 2 V 1.1 which I have has a link:https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/README.md[BCM2836] SoC, which has 4 link:https://en.wikipedia.org/wiki/ARM_Cortex-A7[ARM Cortex-A7] cores, which link:https://en.wikipedia.org/wiki/List_of_ARM_microarchitectures[implement ARMv7-A], <> and <>. 201 | 202 | Therefore we will only be able to run `v7` examples on that board. 203 | 204 | First connect to your Pi through SSH as explained at: https://stackoverflow.com/revisions/39086537/10 205 | 206 | Then inside the Pi: 207 | 208 | .... 209 | sudo apt-get update 210 | sudo apt-get install git make gcc gdb 211 | git clone https://github.com/cirosantilli/arm-assembly-cheat 212 | cd arm-assembly-cheat/v7 213 | make NATIVE=y test 214 | make NATIVE=y gdb-add 215 | .... 216 | 217 | GDB TUI is slightly buggier on the ancient 4.9 toolchain (current line gets different indentation, does not break on the right instruction after `asm_main_after_prologue`, link:https://superuser.com/questions/180512/how-to-turn-off-gdb-tui[cannot leave TUI]), but it might still be usable 218 | 219 | The Pi 0 and 1 however have a link:https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/[BCM2835] SoC, which has an ARM1176JZF-S core, which implements the ARMv6Z ISA, which we don't support yet on this repo. 220 | 221 | Bibliography: https://raspberrypi.stackexchange.com/questions/1732/writing-arm-assembly-code/87260#87260 222 | 223 | [[rpi3]] 224 | ==== Raspberry Pi 3 native 225 | 226 | The Raspberry Pi 3 has a link:https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2837/README.md[BCM2837] SoC, which has 4 link:https://en.wikipedia.org/wiki/ARM_Cortex-A53[Cortex A53] cores, which implement ARMv8-A. 227 | 228 | However, as of July 2018, there is no official <> image for the Pi 3, the same ARMv7 image is provided for both: https://raspberrypi.stackexchange.com/questions/43921/raspbian-moving-to-64-bit-mode 229 | 230 | Then we look at the following threads: 231 | 232 | * https://raspberrypi.stackexchange.com/questions/49466/raspberry-pi-3-and-64-bit-kernel-differences-between-armv7-and-armv8 233 | * https://raspberrypi.stackexchange.com/questions/77693/enabling-armv8-on-raspberry-pi-3-b 234 | 235 | which lead us to this 64-bit Debian based distro for the Pi: https://github.com/bamarni/pi64 236 | 237 | So first we flash pi64's link:https://github.com/bamarni/pi64/releases/download/2017-07-31/pi64-lite.zip[2017-07-31 release], and then do exactly the same as for the Raspberry Pi 2, except that you must go into the `v8` directory instead of `v7`. 238 | 239 | TODO: can we run the `v7` folder in ARMv8? First I can't even compile it. Related: https://stackoverflow.com/questions/21716800/does-gcc-arm-linux-gnueabi-build-for-a-64-bit-target For runtime: https://stackoverflow.com/questions/22460589/armv8-running-legacy-32-bit-applications-on-64-bit-os 240 | 241 | === GDB step debug 242 | 243 | Debug one example with GDB: 244 | 245 | .... 246 | make gdb-add 247 | .... 248 | 249 | Shortcut: 250 | 251 | .... 252 | ./t -g add 253 | .... 254 | 255 | This leaves us right at the end of the prologue of `asm_main` in link:https://sourceware.org/gdb/onlinedocs/gdb/TUI.html[GDB TUI mode], which is at the start of the assembly code in the `.S` file. 256 | 257 | Stop on a different symbol instead: 258 | 259 | .... 260 | make GDB_BREAK=main gdb-add 261 | .... 262 | 263 | Shortcut: 264 | 265 | .... 266 | ./t -b main -g add 267 | .... 268 | 269 | It is not possible to restart the running program from GDB as in `gdbserver --multi` unfortunately: https://stackoverflow.com/questions/51357124/how-to-restart-qemu-user-mode-programs-from-the-gdb-stub-as-in-gdbserver-multi 270 | 271 | Quick GDB tips: 272 | 273 | * print a register: 274 | + 275 | .... 276 | i r r0 277 | .... 278 | + 279 | Bibliography: https://stackoverflow.com/questions/5429137/how-to-print-register-values-in-gdb 280 | * print floating point registers: 281 | + 282 | ** https://reverseengineering.stackexchange.com/questions/8992/floating-point-registers-on-arm/20623#20623 283 | ** https://stackoverflow.com/questions/35518673/gdb-print-aarch64-advanced-simd-vector-registers-is-it-possible/54711214#54711214 284 | * print an array of 4 32-bit integers in hex: 285 | + 286 | .... 287 | p/x (unsigned[4])my_array_0 288 | .... 289 | + 290 | Bibliography: https://stackoverflow.com/questions/32300718/printing-array-from-bss-in-gdb 291 | * print the address of a variable: 292 | + 293 | .... 294 | p &my_array_0 295 | .... 296 | 297 | Bibliography: https://stackoverflow.com/questions/20590155/how-to-single-step-arm-assembler-in-gdb-on-qemu/51310791#51310791 298 | 299 | ==== Advanced usage 300 | 301 | The default setup is opinionated and assumes that your are a newb: it ignores your `.gdbinit` and puts you in TUI mode. 302 | 303 | However, you will sooner or later notice that TUI is crappy print on break Python scripts are the path of light, e.g. link:https://github.com/cyrus-and/gdb-dashboard[GDB dashboard]. 304 | 305 | In order to prevent our opinionated defaults get in the way of your perfect setup, use: 306 | 307 | .... 308 | make GDB_EXPERT=y gdb-add 309 | .... 310 | 311 | or the shortcut: 312 | 313 | .... 314 | ./t -G add 315 | .... 316 | 317 | === Disassemble 318 | 319 | Even though <> can already disassemble instructions for us, it is sometimes useful to have the disassembly in a text file for further examination. 320 | 321 | Disassemble all examples: 322 | 323 | .... 324 | make -j `nproc` objdump 325 | .... 326 | 327 | Disassemble one example: 328 | 329 | .... 330 | make add.objdump 331 | .... 332 | 333 | Examine one disassembly: 334 | 335 | .... 336 | less -p asm_main add.objdump 337 | .... 338 | 339 | This jumps directly to `asm_main`, which is what you likely want to see. 340 | 341 | Disassembly is still useful even though we are writing assembly because the assembler can do some non-obvious magic that we want to understand. 342 | 343 | === crosstool-NG toolchain 344 | 345 | Currently we build just Binutils from source, but use the host GCC to save time. 346 | 347 | This could lead to incompatibilities, although we haven't observed any so far. 348 | 349 | link:https://github.com/crosstool-ng/crosstool-ng[crosstool-NG] is a set of scripts that makes it easy to obtain a cross compiled GCC. Ideally we should track it here as a submodule and automate from there. 350 | 351 | You can build the toolchain with crosstool-NG as explained at: https://stackoverflow.com/revisions/51310756/6 352 | 353 | Then run this repo with: 354 | 355 | .... 356 | make \ 357 | CTNG=crosstool-ng/.build/ct_prefix \ 358 | PREFIX=arm-cortex_a15-linux-gnueabihf \ 359 | test \ 360 | ; 361 | .... 362 | 363 | === Build the documentation 364 | 365 | If you don't like reading on GitHub, the HTML documentation can be generated from the README with: 366 | 367 | .... 368 | make doc 369 | xdg-open out/README.html 370 | .... 371 | 372 | === Custom build flags 373 | 374 | E.g., to pass `-static` for an emulator that does not support dynamically linked executables like link:https://stackoverflow.com/questions/50542222/how-to-run-a-dynamically-linked-executable-syscall-emulation-mode-se-py-in-gem5[gem5]: 375 | 376 | .... 377 | make CCFLAGS_CLI=-static 378 | .... 379 | 380 | == Theory 381 | 382 | === Architecture of this repo 383 | 384 | `qemu-arm-static` is used for emulation on x86 hosts. It translates ARM to x86, and forwards system calls to the host kernel. 385 | 386 | OS portability is achieved with the C standard library which makes system calls for us: this would in theory work in operating systems other than Linux if you port the build system to them. 387 | 388 | Using the standard library also allows us to use its convenient functionality such as `printf` formatting and `memcpy` to check memory. 389 | 390 | Non-OS portable examples will be clearly labeled with their OS. 391 | 392 | These examples show how our infrastructure works: 393 | 394 | * link:fail.S[] 395 | * link:userland/arch/arm/hello_driver.S[] 396 | * link:hello_common.S[] 397 | 398 | ==== C driver 399 | 400 | We link all examples against a C program: link:main.c[]. Sample simplified commands: 401 | 402 | .... 403 | arm-linux-gnueabihf-gcc -c -o 'main.o' 'main.c' 404 | arm-linux-gnueabihf-gcc -c -o 'sub.o' 'sub.S' 405 | arm-linux-gnueabihf-gcc -o 'sub.out' 'sub.o' main.o 406 | .... 407 | 408 | The C driver then just calls `asm_main`, which each `.S` example implements. 409 | 410 | This allows us to easily use the C standard library portably: from the point of view of GCC, everything looks like a regular C program, which does the required glibc initialization before `main()`. 411 | 412 | == CONTRIBUTING 413 | 414 | === Update QEMU 415 | 416 | https://stackoverflow.com/questions/816370/how-do-you-force-a-makefile-to-rebuild-a-target 417 | 418 | .... 419 | git -C qemu pull 420 | make -B -C v7 qemu 421 | make -B -C v8 qemu 422 | .... 423 | 424 | If the build fails due to drastic QEMU changes, first do: 425 | 426 | .... 427 | make qemu-clean 428 | .... 429 | 430 | Then make sure that the tests still pass: 431 | 432 | .... 433 | make test 434 | .... 435 | 436 | === Bare metal 437 | 438 | This tutorial only covers userland concepts. 439 | 440 | However, certain instructions can only be used in higher privilege levels from an operating system itself. 441 | 442 | Here is a base setup ARM programming without an operating system, also known as "Bare Metal Programming": https://github.com/cirosantilli/linux-kernel-module-cheat/tree/7d6f8c3884a4b4170aa274b986caae55b1bebaaf#baremetal-setup 443 | 444 | Features: 445 | 446 | * clean crosstool-NG build for GCC 447 | * C standard library powevered by Newlib 448 | * works on both QEMU and gem5 449 | 450 | Here are further links: 451 | 452 | * generic: 453 | ** https://stackoverflow.com/questions/38914019/how-to-make-bare-metal-arm-programs-and-run-them-on-qemu/50981397#50981397 generic QEMU question 454 | ** link:https://github.com/freedomtan/aarch64-bare-metal-qemu/tree/2ae937a2b106b43bfca49eec49359b3e30eac1b1[]: `-M virt` UART bare metal hello world, nothing else, just works 455 | ** https://github.com/bravegnu/gnu-eprog Not tested. 456 | ** https://stackoverflow.com/questions/29837892/how-to-run-a-c-program-with-no-os-on-the-raspberry-pi/40063032#40063032 no QEMU restriction 457 | ** https://github.com/cirosantilli/raspberry-pi-bare-metal-blinker minimal, but not very QEMU friendly however because hard to observe LED: https://raspberrypi.stackexchange.com/questions/56373/is-it-possible-to-get-the-state-of-the-leds-and-gpios-in-a-qemu-emulation-like-t 458 | * raspberry PI: 459 | ** https://raspberrypi.stackexchange.com/questions/34733/how-to-do-qemu-emulation-for-bare-metal-raspberry-pi-images/85135#85135 RPI3 specific 460 | ** link:https://github.com/bztsrc/raspi3-tutorial[], getting started: https://raspberrypi.stackexchange.com/questions/34733/how-to-do-qemu-emulation-for-bare-metal-raspberry-pi-images/85135#85135 461 | ** https://github.com/dwelch67/raspberrypi 462 | ** https://github.com/BrianSidebotham/arm-tutorial-rpi 463 | * gem5: 464 | ** https://github.com/tukl-msd/gem5.bare-metal bare metal UART example. Tested with: https://stackoverflow.com/questions/43682311/uart-communication-in-gem5-with-arm-bare-metal/50983650#50983650 465 | * games: 466 | ** https://github.com/kcsongor/arm-doom PI 1 model B https://www.youtube.com/watch?v=jeHtktKtGYQ 467 | ** https://github.com/Tetris-Duel-Team/Tetris-Duel Demo: https://www.youtube.com/watch?v=hTqKRdcKZ9k 468 | ** https://github.com/ICTeam28/PiFox rail shooter https://www.youtube.com/watch?v=-5n9IxSQH1M 469 | 470 | x86 bare metal tutorial at: https://github.com/cirosantilli/x86-bare-metal-examples 471 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | /* We define in this header only macros that are the same on all archs. */ 5 | 6 | /* common_arch.h contains arch specific macros. */ 7 | #include "common_arch.h" 8 | 9 | .extern \ 10 | exit, \ 11 | printf, \ 12 | puts \ 13 | ; 14 | 15 | /* Assert that the given branch instruction is taken. */ 16 | #define ASSERT(branch_if_pass) \ 17 | branch_if_pass 1f; \ 18 | FAIL; \ 19 | 1: \ 20 | ; 21 | 22 | /* Assert that a register equals another register. */ 23 | #define ASSERT_EQ_REG(reg1, reg2) \ 24 | cmp reg1, reg2; \ 25 | ASSERT(beq); \ 26 | ; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /download-dependencies: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | if [ $# -gt 0 ]; then 4 | docker=true 5 | else 6 | docker=false 7 | fi 8 | if "$docker"; then 9 | export DEBIAN_FRONTEND=noninteractive 10 | sed -Ei 's/^# deb-src/deb-src/' /etc/apt/sources.list 11 | sudo= 12 | y=-y 13 | else 14 | sudo=sudo 15 | y= 16 | fi 17 | $sudo apt update 18 | # libexpat-dev is required for GDB: 19 | # https://stackoverflow.com/questions/27815416/warning-can-not-parse-xml-library-list-xml-support-was-disabled-at-compile-tim/52929017#52929017 20 | $sudo apt install $y \ 21 | asciidoctor \ 22 | gcc-aarch64-linux-gnu \ 23 | gcc-arm-linux-gnueabihf \ 24 | gdb-multiarch \ 25 | libexpat-dev \ 26 | screen \ 27 | ; 28 | # Using just binutils here instead of aarch64-* version 29 | # because that one downloads 4x more packages and this one 30 | # seems to work... 31 | $sudo apt build-dep $y binutils qemu 32 | -------------------------------------------------------------------------------- /fail.S: -------------------------------------------------------------------------------- 1 | /* See what happens on test failure. */ 2 | 3 | #include "common.h" 4 | 5 | ENTRY 6 | /* Uncomment this to see it fail. */ 7 | /*FAIL*/ 8 | EXIT 9 | -------------------------------------------------------------------------------- /hello_common.S: -------------------------------------------------------------------------------- 1 | /* https://github.com/cirosantilli/arm-assembly-cheat#about */ 2 | 3 | #include "common.h" 4 | 5 | ENTRY 6 | EXIT 7 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "stdint.h" 3 | 4 | int asm_main(uint32_t *line); 5 | 6 | int main(void) { 7 | uint32_t ret, line; 8 | ret = asm_main(&line); 9 | if (ret) { 10 | printf("error %d at line %d\n", ret, line); 11 | } 12 | return ret; 13 | } 14 | -------------------------------------------------------------------------------- /params.mk: -------------------------------------------------------------------------------- 1 | AS = 2 | INS_ASM = 3 | INS_C = 4 | RECURSE = $(QEMU_RECURSE) 5 | QEMU_EXE = 6 | QEMU_RECURSE = v7 v8 7 | -------------------------------------------------------------------------------- /t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | action=test 3 | gdb_break= 4 | gdb_expert=n 5 | OPTIND=1 6 | while getopts b:c:Gg OPT; do 7 | case "$OPT" in 8 | b) 9 | gdb_break="$OPTARG" 10 | ;; 11 | c) 12 | cflags_cli="$OPTARG" 13 | ;; 14 | g) 15 | action=gdb 16 | ;; 17 | G) 18 | action=gdb 19 | gdb_expert=y 20 | ;; 21 | esac 22 | done 23 | shift "$(($OPTIND - 1))" 24 | target="${1%%.*}" 25 | if [ -n "$gdb_break" ]; then 26 | gdb_break_cmd="GDB_BREAK=${gdb_break}" 27 | else 28 | gdb_break_cmd= 29 | fi 30 | make $gdb_break_cmd CCFLAGS_CLI="$cflags_cli" GDB_EXPERT="$gdb_expert" "${action}-${target}" 31 | -------------------------------------------------------------------------------- /v7/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /v7/c/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /v7/c/README.adoc: -------------------------------------------------------------------------------- 1 | https://github.com/cirosantilli/arm-assembly-cheat#inline-assembly 2 | -------------------------------------------------------------------------------- /v7/c/add.c: -------------------------------------------------------------------------------- 1 | /* 1 + 2 == 3 */ 2 | 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | uint32_t in0 = 1, in1 = 2, out; 8 | __asm__ ( 9 | "add %[out], %[in0], %[in1];" 10 | : [out] "=r" (out) 11 | : [in0] "r" (in0), 12 | [in1] "r" (in1) 13 | ); 14 | assert(in0 == 1); 15 | assert(in1 == 2); 16 | assert(out == 3); 17 | } 18 | -------------------------------------------------------------------------------- /v7/c/linux/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /v7/c/linux/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void _start(void) { 4 | uint32_t exit_status; 5 | 6 | /* write */ 7 | { 8 | char msg[] = "hello syscall v7\n"; 9 | uint32_t syscall_return; 10 | register uint32_t r0 __asm__ ("r0") = 1; /* stdout */ 11 | register char *r1 __asm__ ("r1") = msg; 12 | register uint32_t r2 __asm__ ("r2") = sizeof(msg); 13 | register uint32_t r8 __asm__ ("r7") = 4; /* syscall number */ 14 | __asm__ __volatile__ ( 15 | "svc 0;" 16 | : "+r" (r0) 17 | : "r" (r1), "r" (r2), "r" (r8) 18 | : "memory" 19 | ); 20 | syscall_return = r0; 21 | exit_status = (syscall_return != sizeof(msg)); 22 | } 23 | 24 | /* exit */ 25 | { 26 | register uint32_t r0 __asm__ ("r0") = exit_status; 27 | register uint32_t r7 __asm__ ("r7") = 1; 28 | __asm__ __volatile__ ( 29 | "svc 0;" 30 | : "+r" (r0) 31 | : "r" (r7) 32 | : 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /v7/c/linux/params.mk: -------------------------------------------------------------------------------- 1 | -include ../../params.mk 2 | DRIVER_OBJ = 3 | GDB_BREAK = main 4 | FREESTAND = y 5 | -------------------------------------------------------------------------------- /v7/c/linux/t: -------------------------------------------------------------------------------- 1 | ../t -------------------------------------------------------------------------------- /v7/c/params.mk: -------------------------------------------------------------------------------- 1 | -include ../params.mk 2 | DRIVER_OBJ = 3 | GDB_BREAK = main 4 | -------------------------------------------------------------------------------- /v7/c/params_private.mk: -------------------------------------------------------------------------------- 1 | RECURSE = linux 2 | -------------------------------------------------------------------------------- /v7/c/t: -------------------------------------------------------------------------------- 1 | ../t -------------------------------------------------------------------------------- /v7/common.h: -------------------------------------------------------------------------------- 1 | ../common.h -------------------------------------------------------------------------------- /v7/common_arch.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_ARCH_H 2 | #define COMMON_ARCH_H 3 | 4 | .syntax unified 5 | 6 | /* Assert that a register equals a constant. 7 | * * reg: the register to check. Can be r0-r10, but not r11. r11 is overwritten. 8 | * * const: the constant to compare to. Only works for literals or labels, not for registers. 9 | * For register / register comparision, use ASSERT_EQ_REG. 10 | */ 11 | #define ASSERT_EQ(reg, const) \ 12 | ldr r11, =const; \ 13 | cmp reg, r11; \ 14 | ASSERT(beq); \ 15 | ; 16 | 17 | /* Assert that two arrays are the same. */ 18 | #define ASSERT_MEMCMP(s1, s2, n) \ 19 | MEMCMP(s1, s2, n); \ 20 | ASSERT_EQ(r0, 0); \ 21 | ; 22 | 23 | /* Store all callee saved registers, and LR in case we make further BL calls. 24 | * 25 | * Also save the input arguments r0-r3 on the stack, so we can access them later on, 26 | * despite those registers being overwritten. 27 | */ 28 | #define ENTRY \ 29 | .text; \ 30 | .global asm_main; \ 31 | asm_main: \ 32 | stmdb sp!, {r0-r12, lr}; \ 33 | asm_main_after_prologue: \ 34 | ; 35 | 36 | /* Meant to be called at the end of ENTRY.* 37 | * 38 | * Branching to "fail" makes tests fail with exit status 1. 39 | * 40 | * If EXIT is reached, the program ends successfully. 41 | * 42 | * Restore LR and bx jump to it to return from asm_main. 43 | */ 44 | #define EXIT \ 45 | mov r0, 0; \ 46 | mov r1, 0; \ 47 | b pass; \ 48 | fail: \ 49 | ldr r1, [sp]; \ 50 | str r0, [r1]; \ 51 | mov r0, 1; \ 52 | pass: \ 53 | add sp, 16; \ 54 | ldmia sp!, {r4-r12, lr}; \ 55 | bx lr; \ 56 | ; 57 | 58 | /* Always fail. */ 59 | #define FAIL \ 60 | ldr r0, =__LINE__; \ 61 | b fail; \ 62 | ; 63 | 64 | #define MEMCMP(s1, s2, n) \ 65 | ldr r0, =s1; \ 66 | ldr r1, =s2; \ 67 | ldr r2, =n; \ 68 | bl memcmp; \ 69 | ; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /v7/linux/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /v7/linux/README.adoc: -------------------------------------------------------------------------------- 1 | https://github.com/cirosantilli/arm-assembly-cheat#linux-system-calls 2 | -------------------------------------------------------------------------------- /v7/linux/hello.S: -------------------------------------------------------------------------------- 1 | .syntax unified 2 | .text 3 | .global _start 4 | _start: 5 | asm_main_after_prologue: 6 | /* write */ 7 | mov r0, 1 /* stdout */ 8 | adr r1, msg /* buffer */ 9 | ldr r2, =len /* len */ 10 | mov r7, 4 /* syscall id. */ 11 | svc 0 12 | 13 | /* exit */ 14 | mov r0, 0 /* exit status */ 15 | mov r7, 1 /* syscall id */ 16 | svc 0 17 | msg: 18 | .ascii "hello syscall v7\n" 19 | len = . - msg 20 | -------------------------------------------------------------------------------- /v7/linux/params.mk: -------------------------------------------------------------------------------- 1 | -include ../params.mk 2 | FREESTAND = y 3 | -------------------------------------------------------------------------------- /v7/linux/t: -------------------------------------------------------------------------------- 1 | ../t -------------------------------------------------------------------------------- /v7/main.c: -------------------------------------------------------------------------------- 1 | ../main.c -------------------------------------------------------------------------------- /v7/params.mk: -------------------------------------------------------------------------------- 1 | ARCH = arm 2 | # -mfpu=crypto-neon-fp-armv8.1 3 | # vfp.S: Error: selected processor does not support in ARM mode 4 | # https://stackoverflow.com/questions/41131432/cross-compiling-error-selected-processor-does-not-support-fmrx-r3-fpexc-in/52875732#52875732 5 | # We aim to take the most extended mode currently available that works on QEMU. 6 | # 7 | # TODO: GCC does not recognize neon-vfpv3, man gcc 8.2.0 on Ubuntu 18.10 says: 8 | # > The Advanced SIMD (Neon) v1 and the VFPv3 floating-point instructions. 9 | # > The extension +neon-vfpv3 can be used as an alias for this extension. 10 | ASFLAGS_EXTRA = -mcpu=cortex-a76 -mfpu=crypto-neon-fp-armv8.1 -meabi=5 11 | # -marm: the opposite of -mthumb. 12 | # -masm-syntax-unified: make gcc generate .syntax unified for inline assembly. 13 | # However, it gets ignored if -marm is given, which might be a GCC bug: 14 | # https://stackoverflow.com/questions/54078112/how-to-write-syntax-unified-ual-armv7-inline-assembly-in-gcc 15 | # So we just write divided inline assembly for now. 16 | CCFLAGS_EXTRA = -marm -masm-syntax-unified 17 | MARCH = armv7-a 18 | PREFIX = arm-linux-gnueabihf 19 | ifeq ($(NATIVE),y) 20 | # TODO understand why it is failing. 21 | SKIP_TESTS = vfp 22 | endif 23 | -------------------------------------------------------------------------------- /v7/params_private.mk: -------------------------------------------------------------------------------- 1 | RECURSE = c linux 2 | -------------------------------------------------------------------------------- /v7/t: -------------------------------------------------------------------------------- 1 | ../t -------------------------------------------------------------------------------- /v8/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /v8/add.S: -------------------------------------------------------------------------------- 1 | /* https://github.com/cirosantilli/arm-assembly-cheat#data-processing-instructions */ 2 | 3 | #include "common.h" 4 | 5 | ENTRY 6 | mov x0, 1 7 | add x1, x0, 2 8 | ASSERT_EQ(x1, 3) 9 | EXIT 10 | -------------------------------------------------------------------------------- /v8/c/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /v8/c/inc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) { 5 | uint64_t io = 1; 6 | __asm__ ( 7 | "add %[io], %[io], 1;" 8 | : [io] "+r" (io) 9 | : 10 | : 11 | ); 12 | assert(io == 2); 13 | } 14 | -------------------------------------------------------------------------------- /v8/c/linux/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /v8/c/linux/hello.c: -------------------------------------------------------------------------------- 1 | /* https://github.com/cirosantilli/arm-assembly-cheat#freestanding-linux-inline-assembly-system-calls */ 2 | 3 | #include 4 | 5 | void _start(void) { 6 | uint64_t exit_status; 7 | 8 | /* write */ 9 | { 10 | char msg[] = "hello syscall v8\n"; 11 | uint64_t syscall_return; 12 | register uint64_t x0 __asm__ ("x0") = 1; /* stdout */ 13 | register char *x1 __asm__ ("x1") = msg; 14 | register uint64_t x2 __asm__ ("x2") = sizeof(msg); 15 | register uint64_t x8 __asm__ ("x8") = 64; /* syscall number */ 16 | __asm__ __volatile__ ( 17 | "svc 0;" 18 | : "+r" (x0) 19 | : "r" (x1), "r" (x2), "r" (x8) 20 | : "memory" 21 | ); 22 | syscall_return = x0; 23 | exit_status = (syscall_return != sizeof(msg)); 24 | } 25 | 26 | /* exit */ 27 | { 28 | register uint64_t x0 __asm__ ("x0") = exit_status; 29 | register uint64_t x8 __asm__ ("x8") = 93; 30 | __asm__ __volatile__ ( 31 | "svc 0;" 32 | : "+r" (x0) 33 | : "r" (x8) 34 | : 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /v8/c/linux/params.mk: -------------------------------------------------------------------------------- 1 | -include ../../params.mk 2 | DRIVER_OBJ = 3 | GDB_BREAK = main 4 | FREESTAND = y 5 | -------------------------------------------------------------------------------- /v8/c/linux/t: -------------------------------------------------------------------------------- 1 | ../t -------------------------------------------------------------------------------- /v8/c/multiline.cpp: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/3666013/how-to-write-multiline-inline-assembly-code-in-gcc-c/54575948#54575948 2 | 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | uint64_t io = 0; 8 | __asm__ ( 9 | R"( 10 | add %[io], %[io], #1 11 | add %[io], %[io], #1 12 | )" 13 | : [io] "+r" (io) 14 | : 15 | : 16 | ); 17 | assert(io == 2); 18 | } 19 | -------------------------------------------------------------------------------- /v8/c/params.mk: -------------------------------------------------------------------------------- 1 | -include ../params.mk 2 | DRIVER_OBJ = 3 | GDB_BREAK = main 4 | -------------------------------------------------------------------------------- /v8/c/params_private.mk: -------------------------------------------------------------------------------- 1 | RECURSE = linux 2 | -------------------------------------------------------------------------------- /v8/c/t: -------------------------------------------------------------------------------- 1 | ../t -------------------------------------------------------------------------------- /v8/common.h: -------------------------------------------------------------------------------- 1 | ../common.h -------------------------------------------------------------------------------- /v8/common_arch.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_ARCH_H 2 | #define COMMON_ARCH_H 3 | 4 | #define ASSERT_EQ(reg, const) \ 5 | ldr x11, =const; \ 6 | cmp reg, x11; \ 7 | ASSERT(beq); \ 8 | ; 9 | 10 | #define ASSERT_MEMCMP(s1, s2, n) \ 11 | MEMCMP(s1, s2, n); \ 12 | ASSERT_EQ(x0, 0); \ 13 | ; 14 | 15 | #define ENTRY \ 16 | .text; \ 17 | .global asm_main; \ 18 | asm_main: \ 19 | sub sp, sp, 0xA0; \ 20 | stp x29, x30, [sp]; \ 21 | stp x27, x28, [sp, 0x10]; \ 22 | stp x25, x26, [sp, 0x20]; \ 23 | stp x23, x24, [sp, 0x30]; \ 24 | stp x21, x22, [sp, 0x40]; \ 25 | stp x19, x20, [sp, 0x50]; \ 26 | stp x6, x7, [sp, 0x60]; \ 27 | stp x4, x5, [sp, 0x70]; \ 28 | stp x2, x3, [sp, 0x80]; \ 29 | stp x0, x1, [sp, 0x90]; \ 30 | asm_main_after_prologue: \ 31 | ; 32 | 33 | #define EXIT \ 34 | mov w0, 0; \ 35 | mov w1, 0; \ 36 | b pass; \ 37 | fail: \ 38 | ldr x1, [sp, 0x90]; \ 39 | str w0, [x1]; \ 40 | mov w0, 1; \ 41 | pass: \ 42 | ldp x19, x20, [sp, 0x50]; \ 43 | ldp x21, x22, [sp, 0x40]; \ 44 | ldp x23, x24, [sp, 0x30]; \ 45 | ldp x25, x26, [sp, 0x20]; \ 46 | ldp x27, x28, [sp, 0x10]; \ 47 | ldp x29, x30, [sp]; \ 48 | add sp, sp, 0xA0; \ 49 | ret; \ 50 | ; 51 | 52 | #define FAIL \ 53 | ldr w0, =__LINE__; \ 54 | b fail; \ 55 | ; 56 | 57 | #define MEMCMP(s1, s2, n) \ 58 | adr x0, s1; \ 59 | adr x1, s2; \ 60 | ldr x2, =n; \ 61 | bl memcmp; \ 62 | ; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /v8/linux/Makefile: -------------------------------------------------------------------------------- 1 | ../Makefile -------------------------------------------------------------------------------- /v8/linux/README.adoc: -------------------------------------------------------------------------------- 1 | https://github.com/cirosantilli/arm-assembly-cheat#linux-system-calls 2 | -------------------------------------------------------------------------------- /v8/linux/hello.S: -------------------------------------------------------------------------------- 1 | .text 2 | .global _start 3 | _start: 4 | asm_main_after_prologue: 5 | /* write */ 6 | mov x0, 1 7 | adr x1, msg 8 | ldr x2, =len 9 | mov x8, 64 10 | svc 0 11 | 12 | /* exit */ 13 | mov x0, 0 14 | mov x8, 93 15 | svc 0 16 | msg: 17 | .ascii "hello syscall v8\n" 18 | len = . - msg 19 | -------------------------------------------------------------------------------- /v8/linux/params.mk: -------------------------------------------------------------------------------- 1 | -include ../params.mk 2 | FREESTAND = y 3 | -------------------------------------------------------------------------------- /v8/linux/t: -------------------------------------------------------------------------------- 1 | ../t -------------------------------------------------------------------------------- /v8/main.c: -------------------------------------------------------------------------------- 1 | ../main.c -------------------------------------------------------------------------------- /v8/params.mk: -------------------------------------------------------------------------------- 1 | ARCH = aarch64 2 | DEFAULT_SYSROOT = /usr/aarch64-linux-gnu 3 | # armv8.5-a not in Ubuntu 16.04. 4 | # armv8.5-a+sve not in Ubuntu 18.04. 5 | MARCH = armv8-a 6 | MARCH_AS = armv8.5-a+sve 7 | PREFIX = aarch64-linux-gnu 8 | -------------------------------------------------------------------------------- /v8/params_private.mk: -------------------------------------------------------------------------------- 1 | RECURSE = c linux 2 | -------------------------------------------------------------------------------- /v8/t: -------------------------------------------------------------------------------- 1 | ../t --------------------------------------------------------------------------------