Make this repo good enough to move in cpp-cheat, x86-assembly-cheat and arm-assembly-cheat in

This commit is a large squash, the full development branch is available at:
https://github.com/cirosantilli/linux-kernel-module-cheat/tree/asm

This notably means a refactor of the userland build and testing, to support:

- improved assembly infrastructure unified across arm and x86
- native in-tree build and test helpers
- parallel building and testing, which implies thread_pool.py
- selection of what to build and test from the CLI
- path_properties.py to indicate how to build and run different examples
- in full system, move all userland stuff into /lkmc
- prefix everything that we defined across files with LKMC
- --gdb uber convenient helper
- remove import imp which was deprecated

Full commit messages from the branch follow:

1:

userland: add assembly support

Move arm assembly cheat here, and start some work on x86 cheat as well.

2:

document userland asm syscall interfaces

3:

userland assembly: structure readme

4:

x86 fail works

5:

asm: more links

6:

userland: add ported to all archs

7:

move all our stuff into /lkmc in guest

Motivation: userland is getting several new subdirectories, it would be
too insane to just dump all of that in the guest root filesystem.

To alleviate the cd pain, .profile puts user inside /lkmc by default.

8:

start the big userland migration

9:

migrate all

10:

bak

11:

build-userland-in-tree is now a Python command

./build calls it, we did this to allow --download-dependencies to work
perfectly.

12:

rename include to lkmc

13:

mtops.h is perfect now

14:

userland: make build perfect

15:

preparing test_user_mode, need to generalize stuff as usual

16:

asm: prefix every linux specific with linux/

17:

userland: maybe it really works

18:

userland: fix kernel version to work on older ubuntu

Expose --kernel-version to allow customization.

Update LTP info.

19:

userland: build really truly working now

userland test: start work, in a working state, but no features

20:

test-user-mode: make perfect like build-userland

Multithreading and target selection.

21:

userland: get a bit closer to perfection

22:

thread_pool: support passing thread IDs

Then use that to fix gem5 error log read race.

23:

userland: native testing

24:

userland: path properties getting nice!

25:

userland: move posix/environ from cpp-cheat

26:

gem5: --debug-flags without =, looks nicer whenever it can be done

27:

run: rename --wait-gdb in --gdb-wait, --gdb prefix might become a thing

28:

run: create --tmux-program gdb to open gem5 GDB

29:

run: create the uber convenient --gdb option

30:

userland: move getchar from cpp-cheat

31:

prebuilt: kernel boot aarch64 does not work on Ubuntu 16.04

32:

userland: x86_64 linux hello world make PIE

33:

userland: try to make userland executable selection saner

Only allow existing files to be built, stop extension expansion madness.

cli_function: get_cli print booleans properly, was printing without --no-
for negations.

34:

userland: only link to lkmc.o if needed

35:

path_properties: make data very compact with only tuples and dicts

Spend 2 hours of my life thinking about low value tree walks ;-)

36:

userland: move more userland/arch/ logic into property tree

37:

userland: make libs work

Working for build, but now test-user-mode-in-tree is not using --in-tree,
TODO fix later on.

38:

userland: make libs really work

39:

userland: document path_properties

40:

userland: classify linux

41:

waste your life

42:

common: fix absolute path runs

--gdb: allow running from arbitrary directory

43:

baremetal: arm allow using floating point instructions

44:

baremetal: stat preparing to make perfect like userland/

45:

run: fix image check logic accounting for userland

Was failing if I try to run userland (with abspath) when out/
directory is not present.

46:

cli-function: raise if the config file is given and does not exist

47:

common: define missing 'ld' variable, this broke m5 build

48:

rum: --qemu-which host now works for user mode as well as system

Don't fall back on host QEMU automatically, too much insanity.

49:

userland: refix silly mistakes

50:

userland: use path_properties flags for all builds, including lkmc. and userland/arch/main.c

Without this in particular, --gdb fails on assembly because main.c
was not being built with -ggdb3.

51:

userland: start refactor to show failing values on failure!

aarch64 basically done, but missing:

- other archs
- maybe convert main.c into C++ to use templates?
- full review of ASSERT_EQ calling convention issues not seen by tests
  by chance
- documentation

52:

readme: releases are more stable...

53:

submodules: sort gitmodules

54:

test-baremetal: same interface as test-user-mode

In particular, runs tests in parallel, and allows selecting given tests

55:

baremetal: allow arbitrary exit status with the magic string

test-baremetal: fix missing setting x0 return value

Examples were just returning on ret without setting x0, which led to
failures... those were not noticed because of how broken the testing system
was ;-)

56:

baremetal: ah, actually nope, it didn't work :-(

Workaround for now. Works on asserts, but not on exit 1.

Some other day, maybe.

https://github.com/cirosantilli/linux-kernel-module-cheat/issues/59

57:

panic on panic: improve behaviour description

58:

baremetal: get exit status working with on_exit :-)

59:

baremetal: implement C assert

60:

test-baremetal: remove commented out exit status workaround

61:

test-user-mode: handle exit status for signals. Fix #61.

62:

aarch64: fix ASSERT_EQ_REG tests on gem5

Was doing an 8-byte aligned store, which gem5 dislikes.

But the ARMARM says bad things may happen there, notably a signal:
"D1.8.2 SP alignment checking" so gem5 is not really too wrong,
QEMU just happens to work by chance.

63:

userland assembly: build empty.S and fail.S to toplevel and run fail.S with path_properties exit_status

They were just duplicating stuff needlessly while we don't support non-native in-tree builds,
which leads to executable conflicts for C file anyways.

64:

gem5: use a single build tree for all build types

gem5 already has different object names for each build type it seems, so
let's just make sure that works and save some disk space.

65:

userland x86_64: ASSERT_EQ show actual and expected values

66:

assert_fail.c: add to readme index

67:

userland x86_64: implement ASSERT_MEMCMP

68:

userland x86_64: allow ASSERT_EQ to take just about anything

69:

gas data sizes

70:

gas_data_sizes.S: make PIE for all ISAs

71:

x86: paddq

72:

x86 paddq: test entire family

73:

Get rid of imp, started giving deprecation warning every time in Python 3.7 in Ubuntu 19.04.

Please python stop torturing me with refactors.

Make ./run -u blow up if executable not found, otherwise I go crazy.

Get ./test-gdb back to life after the ./run relative path refactor, forgot to test this.

74:

fix run-toolchain, qemu-monitor, trace-boot, trace2line, bisect-linux-boot-gem5. Fixes part of #63

I'm sad no one reported qemu-monitor break, that one is kind of important.

count.out arguments broke it as an init program, since the kernel adds trash
parameters to every init.

Is anyone using this repo, I wonder? Keep pushing, keep pushing.
One day it gets good enough, and the whole world will see.

75:

x86 assembly: addpd

76:

Fix import_path circular dependency by splitting it out.

Use import thread_pool instead from, from is evil.

Fix poweroff.out path for ./trace-boot.

77:

run: rename cryptic tmu to tmux-split, ./run is good now so I never use it anymore explicitly

78:

assembly SIMD add: make uniform for all ISAs, mark as entry point to learning SIMD

79:

start moving arm-assembly-cheat readme in here

80:

arm assembly: move some more in

81:

move more arm in

82:

userland: attempt to fix all assembly example links to README

83:

assembly: improve organization of simd add

84:

ld2 move in

85:

Make userland / assembly getting started more uniform / visible

Forward --gcc-which to ./run --tmux.

Use gdb-multiarch for --gcc-which host.

86:

userland: disable PIE explicitly on command line for all executables

87:

userland: make userland content a better landing page

88:

build: check git version from --version and degrade gracefully

89:

build: make --dry-run work again on all

90:

import_path: importlib explicit for Ubuntu 16.04

91:

make all submodules point to my forks

git servers are insane, submodule implementation is crap, what can you do

92:

build: log warning on git too old for --update

93:

build-linux: do olddefconfig even if no fragments

In particular, gem5 kernel 4.15 needs it

94:

userland content: improve a bit landing page for cpp-cheat
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-05-16 00:00:00 +00:00
parent 7a5ca339a3
commit d1003f1cb2
388 changed files with 9972 additions and 3432 deletions

View File

@@ -1 +0,0 @@
https://github.com/cirosantilli/linux-kernel-module-cheat#ansi-c

View File

@@ -1 +0,0 @@
../baremetal/add.c

View File

@@ -1 +0,0 @@
../baremetal/add.py

View File

@@ -1,46 +0,0 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#anonymous-inode */
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h> /* sleep */
#include "../include/anonymous_inode.h"
int main(int argc, char **argv)
{
char buf[1024];
int fd_ioctl, fd_ioctl_anon, ret;
size_t i, nreads;
if (argc < 2) {
puts("Usage: ./prog <ioctl-file> [<nreads>]");
return EXIT_FAILURE;
} else if (argc > 2) {
nreads = strtol(argv[2], NULL, 10);
} else {
nreads = 3;
}
fd_ioctl = open(argv[1], O_RDONLY);
if (fd_ioctl == -1) {
perror("open");
return EXIT_FAILURE;
}
ret = ioctl(fd_ioctl, LKMC_ANONYMOUS_INODE_GET_FD, &fd_ioctl_anon);
if (ret == -1) {
perror("ioctl");
return EXIT_FAILURE;
}
for (i = 0; i < nreads; ++i) {
ret = read(fd_ioctl_anon, buf, sizeof(buf));
printf("%.*s\n", ret, buf);
}
close(fd_ioctl_anon);
close(fd_ioctl);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,9 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#userland-assembly */
#include "common.h"
ENTRY
mov x0, 1
add x1, x0, 2
ASSERT_EQ(x1, 3)
EXIT

View File

@@ -0,0 +1,32 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#armv8-aarch64-add-vector-instruction
*
* Add a bunch of integers in one go.
*/
#include "common.h"
ENTRY
.data
input0: .long 0xF1F1F1F1, 0xF2F2F2F2, 0xF3F3F3F3, 0xF4F4F4F4
input1: .long 0x12121212, 0x13131313, 0x14141414, 0x15151515
expect_4s: .long 0x04040403, 0x06060605, 0x08080807, 0x0A0A0A09
expect_2d: .long 0x04040403, 0x06060606, 0x08080807, 0x0A0A0A0A
.bss
output: .skip 16
.text
#define TEST(size) \
adr x0, input0; \
ld1 {v0. ## size}, [x0]; \
adr x1, input1; \
ld1 {v1. ## size}, [x1]; \
add v2. ## size, v0. ## size, v1. ## size; \
adr x0, output; \
st1 {v2. ## size}, [x0]; \
ASSERT_MEMCMP(output, expect_ ## size, 0x10)
/* 4x 32-bit */
TEST(4s)
/* 2x 64-bit */
TEST(2d)
#undef TEST
EXIT

View File

@@ -0,0 +1,21 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-adr-instruction */
#include "common.h"
.data
data_label:
.word 0x1234678
ENTRY
/* This is not possible in v7 because the label is in another section.
* objdump says that this generates a R_AARCH64_ADR_PRE relocation.
* which looks specific to ADR, and therefore makes it more likely
* that there was no such relocation in v7.
*
* This relocation is particularly important because str does not have a
* pc-relative mode in ARMv8.
*/
adr x0, data_label
ldr x1, =data_label
label:
ASSERT_EQ_REG(x0, x1)
EXIT

View File

@@ -0,0 +1,13 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-adr-instruction */
#include "common.h"
ENTRY
adrp x0, label
adr x1, label
label:
/* Clear the lower 12 bits. */
bic x1, x1, 0xFF
bic x1, x1, 0xF00
ASSERT_EQ_REG(x0, x1)
EXIT

View File

@@ -1,13 +0,0 @@
#include <assert.h>
#include <inttypes.h>
int main(void) {
uint32_t myvar = 1;
__asm__ (
"add %[myvar], %[myvar], 1;"
: [myvar] "=r" (myvar)
:
:
);
assert(myvar == 2);
}

View File

@@ -0,0 +1,33 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-beq-instruction */
#include "common.h"
ENTRY
/* cbz == 0 */
mov x0, 0
cbz x0, 1f
FAIL
1:
/* cbz != 0 */
mov x0, 1
cbz x0, 1f
b 2f
1:
FAIL
2:
/* cbnz != 0 */
mov x0, 1
cbnz x0, 1f
FAIL
1:
/* cbnz == 0 */
mov x0, 0
cbnz x0, 1f
b 2f
1:
FAIL
2:
EXIT

View File

@@ -0,0 +1,11 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-bfi-instruction */
#include "common.h"
ENTRY
ldr x0, =0x1122334455667788
ldr x1, =0xFFFFFFFFFFFFFFFF
bfi x1, x0, 16, 32
ASSERT_EQ(x1, 0xFFFF55667788FFFF)
EXIT

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1,17 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly-early-clobbers */
#include <assert.h>
#include <inttypes.h>
int main(void) {
uint64_t in = 1;
uint64_t out;
__asm__ (
"add %[out], %[in], 1;"
"add %[out], %[in], 1;"
: [out] "=&r" (out)
: [in] "r" (in)
:
);
assert(out == 2);
}

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1,39 @@
/* aarch64 freestanding C inline assemby Linux hello world
* https://github.com/cirosantilli/linux-kernel-module-cheat#linux-system-calls
*/
#include <inttypes.h>
void _start(void) {
uint64_t exit_status;
/* write */
{
char msg[] = "hello\n";
uint64_t syscall_return;
register uint64_t x0 __asm__ ("x0") = 1; /* stdout */
register char *x1 __asm__ ("x1") = msg;
register uint64_t x2 __asm__ ("x2") = sizeof(msg);
register uint64_t x8 __asm__ ("x8") = 64; /* syscall number */
__asm__ __volatile__ (
"svc 0;"
: "+r" (x0)
: "r" (x1), "r" (x2), "r" (x8)
: "memory"
);
syscall_return = x0;
exit_status = (syscall_return != sizeof(msg));
}
/* exit */
{
register uint64_t x0 __asm__ ("x0") = exit_status;
register uint64_t x8 __asm__ ("x8") = 93;
__asm__ __volatile__ (
"svc 0;"
: "+r" (x0)
: "r" (x8)
:
);
}
}

View File

@@ -0,0 +1,42 @@
/* Like hello.c trying to do it without named register variables.
* The code is more complicated, and I was not able to get as efficient,
* so better just stick to named register variables.
*
* https://github.com/cirosantilli/linux-kernel-module-cheat#linux-system-calls
*/
#include <inttypes.h>
void _start(void) {
uint64_t exit_status;
/* write */
{
char msg[] = "hello\n";
uint64_t syscall_return;
__asm__ (
"mov x0, 1;" /* stdout */
"mov x1, %[msg];"
"mov x2, %[len];"
"mov x8, 64;" /* syscall number */
"svc 0;"
"mov %[syscall_return], x0;"
: [syscall_return] "=r" (syscall_return)
: [msg] "p" (msg),
[len] "i" (sizeof(msg))
: "x0", "x1", "x2", "x8", "memory"
);
exit_status = (syscall_return != sizeof(msg));
}
/* exit */
__asm__ (
"mov x0, %[exit_status];"
"mov x8, 93;" /* syscall number */
"svc 0;"
:
: [exit_status] "r" (exit_status)
: "x0", "x8"
);
}

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1,18 @@
/* Increment a variable in inline assembly.
*
* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly
*/
#include <assert.h>
#include <inttypes.h>
int main(void) {
uint64_t io = 1;
__asm__ (
"add %[io], %[io], 1;"
: [io] "+r" (io)
:
:
);
assert(io == 2);
}

View File

@@ -0,0 +1,25 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly-floating-point-arm */
#include <assert.h>
int main(void) {
float my_float = 1.5;
__asm__ (
"fmov s0, 1.0;"
"fadd %s[my_float], %s[my_float], s0;"
: [my_float] "+w" (my_float)
:
: "s0"
);
assert(my_float == 2.5);
double my_double = 1.5;
__asm__ (
"fmov d0, 1.0;"
"fadd %d[my_double], %d[my_double], d0;"
: [my_double] "+w" (my_double)
:
: "d0"
);
assert(my_double == 2.5);
}

View File

@@ -0,0 +1,39 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-calling-convention */
#include <assert.h>
#include <inttypes.h>
uint64_t my_asm_func(void);
/* { return 42; } */
__asm__(
".global my_asm_func;"
"my_asm_func:"
"mov x0, 42;"
"ret;"
);
/* Now a more complex example that also calls a C function.
* We have to store the return value x30 for later because bl modifies it.
* https://stackoverflow.com/questions/27941220/push-lr-and-pop-lr-in-arm-arch64/34504752#34504752
* We are not modifying any other callee saved register in this function,
* since my_c_func is not either (unless GCC has a bug ;-)), so everything else if fine.
*/
uint64_t my_asm_func_2(void);
/* { return my_c_func(); } */
__asm__(
".global my_asm_func_2;"
"my_asm_func_2:"
"str x30, [sp, -16]!;"
"bl my_c_func;"
"ldr x30, [sp], 16;"
"ret;"
);
uint64_t my_c_func(void) {
return 42;
}
int main(void) {
assert(my_asm_func() == 42);
assert(my_asm_func_2() == 42);
}

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1,18 @@
// https://stackoverflow.com/questions/3666013/how-to-write-multiline-inline-assembly-code-in-gcc-c/54575948#54575948
#include <assert.h>
#include <inttypes.h>
int main(void) {
uint64_t io = 0;
__asm__ (
R"(
add %[io], %[io], #1
add %[io], %[io], #1
)"
: [io] "+r" (io)
:
:
);
assert(io == 2);
}

View File

@@ -0,0 +1,27 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly-register-variables */
#include <assert.h>
#include <inttypes.h>
int main(void) {
register uint32_t x0 __asm__ ("x0");
register uint32_t x1 __asm__ ("x1");
uint32_t new_x0;
uint32_t new_x1;
{
x0 = 1;
x1 = 2;
__asm__ (
"add %[x0], x0, #1;"
"add %[x1], x1, #1;"
: [x0] "+r" (x0),
[x1] "+r" (x1)
:
:
);
new_x0 = x0;
new_x1 = x1;
}
assert(new_x0 == 2);
assert(new_x1 == 3);
}

View File

@@ -0,0 +1,28 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly-register-variables */
#include <assert.h>
#include <inttypes.h>
int main(void) {
register double d0 __asm__ ("d0");
register double d1 __asm__ ("d1");
double new_d0;
double new_d1;
{
d0 = 1.5;
d1 = 2.5;
__asm__ (
"fmov d2, 1.5;"
"fadd %d[d0], d0, d2;"
"fadd %d[d1], d1, d2;"
: [d0] "+w" (d0),
[d1] "+w" (d1)
:
: "d2"
);
new_d0 = d0;
new_d1 = d1;
}
assert(new_d0 == 3.0);
assert(new_d1 == 4.0);
}

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1,19 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-cbz-instruction */
#include "common.h"
ENTRY
/* Branch. */
mov x0, 0x0
cbz x0, ok
FAIL
ok:
/* Don't branch. */
mov x0, 0x1
cbz x0, ko
EXIT
ko:
FAIL

View File

@@ -0,0 +1,17 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gnu-gas-assembler-comments */
#include "common.h"
ENTRY
# mycomment
/* ARMv8 has // instead of @ as for comments. */
// mycomment
nop // mycomment
/* All these fail. Lol, different than v7, no consistency. */
#if 0
nop # mycomment
@ mycomment
nop @ mycomment
#endif
EXIT

View File

@@ -0,0 +1,83 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#userland-assembly-c-standard-library */
#ifndef COMMON_ARCH_H
#define COMMON_ARCH_H
#define ASSERT_EQ(reg, const) \
mov x0, reg; \
ldr x1, =const; \
ASSERT_EQ_DO(64); \
;
#define ASSERT_EQ_DO(bits) \
bl assert_eq_ ## bits; \
cmp x0, 0; \
ASSERT(beq); \
;
#define ASSERT_EQ_REG(reg1, reg2) \
str reg2, [sp, -16]!; \
mov x0, reg1; \
ldr x1, [sp], 16; \
ASSERT_EQ_DO(64); \
;
#define ASSERT_EQ_REG_32(reg1, reg2) \
str reg2, [sp, -4]!; \
mov w0, reg1; \
ldr w1, [sp], 4; \
ASSERT_EQ_DO(32); \
;
#define ASSERT_MEMCMP(label1, label2, const_size) \
adr x0, label1; \
adr x1, label2; \
ldr x2, =const_size; \
bl assert_memcmp; \
cmp x0, 0; \
ASSERT(beq); \
;
#define ENTRY \
.text; \
.global asm_main; \
asm_main: \
sub sp, sp, 0xA0; \
stp x29, x30, [sp]; \
stp x27, x28, [sp, 0x10]; \
stp x25, x26, [sp, 0x20]; \
stp x23, x24, [sp, 0x30]; \
stp x21, x22, [sp, 0x40]; \
stp x19, x20, [sp, 0x50]; \
stp x6, x7, [sp, 0x60]; \
stp x4, x5, [sp, 0x70]; \
stp x2, x3, [sp, 0x80]; \
stp x0, x1, [sp, 0x90]; \
asm_main_after_prologue: \
;
#define EXIT \
mov w0, 0; \
mov w1, 0; \
b pass; \
fail: \
ldr x1, [sp, 0x90]; \
str w0, [x1]; \
mov w0, 1; \
pass: \
ldp x19, x20, [sp, 0x50]; \
ldp x21, x22, [sp, 0x40]; \
ldp x23, x24, [sp, 0x30]; \
ldp x25, x26, [sp, 0x20]; \
ldp x27, x28, [sp, 0x10]; \
ldp x29, x30, [sp]; \
add sp, sp, 0xA0; \
ret; \
;
#define FAIL \
ldr w0, =__LINE__; \
b fail; \
;
#endif

View File

@@ -0,0 +1,28 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-cset-instruction */
#include "common.h"
ENTRY
/* Test values. */
mov x19, 0
mov x20, 1
/* eq is true, set x21 = 1. */
cmp x19, x19
cset x21, eq
ASSERT_EQ(x21, 1)
/* eq is false, set x21 = 0. */
cmp x19, x20
cset x21, eq
ASSERT_EQ(x21, 0)
/* Same for ne. */
cmp x19, x19
cset x21, ne
ASSERT_EQ(x21, 0)
cmp x19, x20
cset x21, ne
ASSERT_EQ(x21, 1)
EXIT

View File

@@ -0,0 +1,60 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#advanced-simd-instructions */
#include "common.h"
ENTRY
/* 1.5 + 2.5 == 4.0
* using 64-bit double immediates.
*/
fmov d0, 1.5
fmov d1, 2.5
fadd d2, d0, d1
fmov d3, 4.0
/* Unlike VFP vcmp, this stores the status
* automatically in the main CPSR.
*/
fcmp d2, d3
ASSERT(beq)
/* Now with a memory stored value. */
.data
my_double_0:
.double 1.5
my_double_1:
.double 2.5
my_double_sum_expect:
.double 4.0
.text
ldr d0, my_double_0
ldr d1, my_double_1
fadd d2, d0, d1
ldr d3, my_double_sum_expect
fcmp d2, d3
ASSERT(beq)
/* Now in 32-bit. */
fmov s0, 1.5
fmov s1, 2.5
fadd s2, s0, s1
fmov s3, 4.0
fcmp s2, s3
ASSERT(beq)
/* TODO why? What's the point of q then?
* Error: operand mismatch -- `fmov q0,1.5'
*/
#if 0
fmov q0, 1.5
#endif
/* Much like integers, immediates are constrained to
* fit in 32-byte instructions. TODO exact rules.
*
* Assembly here would fail with:
*
* Error: invalid floating-point constant at operand 2
*/
#if 0
fmov d0, 1.23456798
#endif
EXIT

View File

@@ -0,0 +1,34 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#armv8-aarch64-fadd-vector-instruction
*
* Add a bunch of floating point numbers in one go.
*/
#include "common.h"
ENTRY
.data
input0_4s: .float 1.5, 2.5, 3.5, 4.5
input1_4s: .float 5.5, 6.5, 7.5, 8.5
expect_4s: .float 7.0, 9.0, 11.0, 13.0
input0_2d: .double 1.5, 2.5
input1_2d: .double 5.5, 6.5
expect_2d: .double 7.0, 9.0
.bss
output: .skip 16
.text
#define TEST(size) \
adr x0, input0_ ## size; \
ld1 {v0. ## size}, [x0]; \
adr x1, input1_ ## size; \
ld1 {v1. ## size}, [x1]; \
fadd v2. ## size, v0. ## size, v1. ## size; \
adr x0, output; \
st1 {v2. ## size}, [x0]; \
ASSERT_MEMCMP(output, expect_ ## size, 0x10)
/* 4x 32-bit */
TEST(4s)
/* 2x 64-bit */
TEST(2d)
#undef TEST
EXIT

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1,22 @@
/* aarch64 freestanding Linux hello world
* https://github.com/cirosantilli/linux-kernel-module-cheat#linux-system-calls
*/
.text
.global _start
_start:
asm_main_after_prologue:
/* write */
mov x0, 1 /* stdout */
adr x1, msg /* buffer */
ldr x2, =len /* len */
mov x8, 64 /* syscall number */
svc 0
/* exit */
mov x0, 0 /* exit status */
mov x8, 93 /* syscall number */
svc 0
msg:
.ascii "hello\n"
len = . - msg

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1,29 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gnu-gas-assembler data sizes */
#include "common.h"
ENTRY
#define ASSERT_DIFF(label1, label2, result) \
adr x0, label1; \
adr x1, label2; \
sub x0, x1, x0; \
ASSERT_EQ(x0, result)
ASSERT_DIFF(mybyte, myword, 1)
ASSERT_DIFF(myword, mylong, 4)
ASSERT_DIFF(mylong, myquad, 4)
ASSERT_DIFF(myquad, myocta, 8)
ASSERT_DIFF(myocta, theend, 16)
#undef ASSERT_DIF
EXIT
mybyte:
.byte 0x12
myword:
.word 0x1234
mylong:
.long 0x12345678
myquad:
.quad 0x123456789ABCDEF0
myocta:
.octa 0x123456789ABCDEF0123456789ABCDEF0
theend:

View File

@@ -0,0 +1,9 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gnu-gas-assembler-immediates */
#include "common.h"
ENTRY
mov x0, 1
mov x0, 0x1
mov x0, 1
mov x0, 0x1
EXIT

View File

@@ -0,0 +1,26 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-ld2-instruction */
#include "common.h"
ENTRY
.data
u32_interleave: .word \
0x11111111, 0x55555555, \
0x22222222, 0x66666666, \
0x33333333, 0x77777777, \
0x44444444, 0x88888888
u32_interleave_sum_expect: .word \
0x66666666, \
0x88888888, \
0xAAAAAAAA, \
0xCCCCCCCC
.bss
u32_interleave_sum: .skip 16
.text
adr x0, u32_interleave
ld2 {v0.4s, v1.4s}, [x0]
add v2.4s, v0.4s, v1.4s
adr x0, u32_interleave_sum
st1 {v2.4s}, [x0]
ASSERT_MEMCMP(u32_interleave_sum, u32_interleave_sum_expect, 0x10)
EXIT

View File

@@ -0,0 +1,26 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#armv8-aarch64-movk-instruction */
#include "common.h"
ENTRY
movk x0, 0x4444, lsl 0
movk x0, 0x3333, lsl 16
movk x0, 0x2222, lsl 32
movk x0, 0x1111, lsl 48
ASSERT_EQ(x0, 0x1111222233334444)
/* Set a label (addresses are 48-bit) with immediates:
*
* * https://stackoverflow.com/questions/38570495/aarch64-relocation-prefixes
* * https://sourceware.org/binutils/docs-2.26/as/AArch64_002dRelocations.html
*
* This could be used if the label is too far away for
* adr relative addressing.
*/
movz x0, :abs_g2:label /* bits 32-47, overflow check */
movk x0, :abs_g1_nc:label /* bits 16-31, no overflow check */
movk x0, :abs_g0_nc:label /* bits 0-15, no overflow check */
adr x1, label
label:
ASSERT_EQ_REG(x0, x1)
EXIT

View File

@@ -0,0 +1,9 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#armv8-aarch64-movn-instruction */
#include "common.h"
ENTRY
ldr x0, =0x123456789ABCDEF0
movn x0, 0x8888, lsl 16
ASSERT_EQ(x0, 0xFFFFFFFF7777FFFF)
EXIT

View File

@@ -0,0 +1,78 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#assembly-registers */
#include "common.h"
ENTRY
#if 0
/* Unlike v7, we can't use PC like any other register in ARMv8,
* since it is not a general purpose register anymore.
*
* Only branch instructions can modify the PC.
*
* B1.2.1 "Registers in AArch64 state" says:
*
* Software cannot write directly to the PC. It
* can only be updated on a branch, exception entry or
* exception return.
*/
ldr pc, =10f
FAIL
10:
#endif
#if 0
mov x0, pc
#endif
/* LDR PC-relative loads exist in ARMv8, but they have a separate encoding
* "LDR (literal)" instead of "LDR (immediate)":
* https://stackoverflow.com/questions/28638981/howto-write-pc-relative-adressing-on-arm-asm/54480999#54480999
*/
ldr x0, pc_relative_ldr
b 1f
pc_relative_ldr:
.quad 0x123456789ABCDEF0
1:
ASSERT_EQ(x0, 0x123456789ABCDEF0)
/* Just for fun, we can also use relative numbers instead of labels.
* https://reverseengineering.stackexchange.com/questions/17666/how-does-the-ldr-instruction-work-on-arm/20567#20567
*/
ldr x0, 0x8
b 1f
.quad 0x123456789ABCDEF0
1:
ASSERT_EQ(x0, 0x123456789ABCDEF0)
/* Analogous for b with PC. */
mov x0, 0
/* Jumps over mov to ASSERT_EQ. */
b 8
mov x0, 1
ASSERT_EQ(x0, 0)
/* Trying to use the old "LDR (immediate)" PC-relative
* syntax does not work.
*/
#if 0
/* 64-bit integer or SP register expected at operand 2 -- `ldr x0,[pc]' */
ldr x0, [pc]
#endif
/* There is however no analogue for str. TODO rationale? */
#if 0
/* Error: invalid addressing mode at operand 2 -- `str x0,pc_relative_str' */
str x0, pc_relative_str
#endif
/* You just have to use adr + "STR (register)". */
ldr x0, pc_relative_str
ASSERT_EQ(x0, 0x0)
adr x1, pc_relative_str
ldr x0, pc_relative_ldr
str x0, [x1]
ldr x0, pc_relative_str
ASSERT_EQ(x0, 0x123456789ABCDEF0)
EXIT
.data
pc_relative_str:
.quad 0x0000000000000000

View File

@@ -0,0 +1,47 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#assembly-registers */
#include "common.h"
ENTRY
/* 31 64-bit eXtended general purpose registers. */
mov x0, 0
mov x1, 1
mov x2, 2
mov x3, 3
mov x4, 4
mov x5, 5
mov x6, 6
mov x7, 7
mov x8, 8
mov x9, 9
mov x10, 10
mov x11, 11
mov x12, 12
mov x13, 13
mov x14, 14
mov x15, 15
mov x16, 16
mov x17, 17
mov x18, 18
mov x19, 19
mov x20, 20
mov x21, 21
mov x22, 22
mov x23, 23
mov x24, 24
mov x25, 25
mov x26, 26
mov x27, 27
mov x28, 28
mov x29, 29
/* x30 is the link register. BL stores the return address here. */
/*mov x30, 30*/
/* W form addresses the lower 4 bytes word, and zeroes the top. */
ldr x0, =0x1111222233334444
ldr x1, =0x5555666677778888
mov w0, w1
ASSERT_EQ(x0, 0x0000000077778888)
EXIT

View File

@@ -0,0 +1,28 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#armv8-aarch64-ret-instruction */
#include "common.h"
ENTRY
mov x19, 1
bl inc
ASSERT_EQ(x19, 2)
bl inc2
ASSERT_EQ(x19, 3)
bl inc3
ASSERT_EQ(x19, 4)
EXIT
/* void inc(uint64_t *i) { (*i)++ } */
inc:
add x19, x19, 1
ret
/* Same but explicit return register. */
inc2:
add x19, x19, 1
ret x30
/* Same but with br. */
inc3:
add x19, x19, 1
br x30

View File

@@ -0,0 +1,13 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#armv8-aarch64-str-instruction */
#include "common.h"
ENTRY
ldr x0, myvar
ASSERT_EQ(x0, 0x12346789ABCDEF0)
#if 0
/* Error: invalid addressing mode at operand 2 -- `str x0,myvar' */
str x0, myvar
#endif
EXIT
myvar: .quad 0x12346789ABCDEF0

1
userland/arch/aarch64/test Symbolic link
View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1,17 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-ubfm-instruction */
#include "common.h"
ENTRY
ldr x19, =0x1122334455667788
// lsr alias: imms == 63
ldr x20, =0xFFFFFFFFFFFFFFFF
ubfm x20, x19, 16, 63
ASSERT_EQ(x20, 0x0000112233445566)
ldr x20, =0xFFFFFFFFFFFFFFFF
ubfm x20, x19, 32, 63
ASSERT_EQ(x20, 0x0000000011223344)
EXIT

View File

@@ -0,0 +1,15 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-ubfx-instruction */
#include "common.h"
ENTRY
ldr x19, =0x1122334455667788
ldr x20, =0xFFFFFFFFFFFFFFFF
ubfx x20, x19, 8, 16
ASSERT_EQ(x20, 0x0000000000006677)
ldr x20, =0xFFFFFFFFFFFFFFFF
ubfx x20, x19, 8, 32
ASSERT_EQ(x20, 0x0000000044556677)
EXIT

View File

@@ -0,0 +1,51 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#armv8-aarch64-x31-register */
#include "common.h"
ENTRY
/* ERROR: can never use the name x31. */
#if 0
mov x31, 31
#endif
/* mov (register) is an alias for ORR, which accepts xzr. */
mov x19, 1
mov x19, xzr
ASSERT_EQ(x19, 0)
/* Same encoding as the mov version. */
mov x19, 1
orr x19, xzr, xzr
ASSERT_EQ(x19, 0)
/* So, orr, which is not an alias, can only take xzr, not sp. */
#if 0
orr sp, sp, sp
#endif
/* Zero register discards result if written to. */
mov x19, 1
orr xzr, x19, x19
ASSERT_EQ(xzr, 0)
/* MOV (to/from SP) is an alias for ADD (immediate). */
mov x19, sp
mov sp, 1
/* Alias to add. */
mov x20, sp
/* Exact same encoding as above. */
add x20, sp, 0
mov sp, x19
ASSERT_EQ(x20, 1)
/* So, ADD (immediate), which is not an alias, can only take sp, not xzr. */
#if 0
/* Error: integer register expected in the extended/shifted operand register at operand 3 -- `add xzr,xzr,1' */
add xzr, xzr, 1
#endif
/* Note however that ADD (register), unlike ADD (immediate),
* does not say anything about SP, and so does accept xzr just fine.
*/
add xzr, xzr, xzr
EXIT

58
userland/arch/arm/add.S Normal file
View File

@@ -0,0 +1,58 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-data-processing-instructions */
#include "common.h"
ENTRY
/* Immediate encoding.
*
* r1 = r0 + 2
*/
mov r0, 1
/* r1 = r0 + 2 */
add r1, r0, 2
ASSERT_EQ(r1, 3)
/* If src == dest, we can omit one of them.
*
* r0 = r0 + 2
*/
mov r0, 1
add r0, 2
ASSERT_EQ(r0, 3)
/* Same as above but explicit. */
mov r0, 1
add r0, r0, 2
ASSERT_EQ(r0, 3)
#if 0
/* But we cannot omit the register if there is a shift when using .syntx unified:
* https://github.com/cirosantilli/linux-kernel-module-cheat#shift-suffixes
*/
.syntax unified
/* Error: garbage following instruction */
add r0, r1, lsl 1
/* OK */
add r0, r0, r1, lsl 1
#endif
/* Register encoding.
*
* r2 = r0 + r1
*/
mov r0, 1
mov r1, 2
add r2, r0, r1
ASSERT_EQ(r2, 3)
/* Register encoding, omit implicit register.
*
* r1 = r1 + r0
*/
mov r0, 1
mov r1, 2
add r1, r0
ASSERT_EQ(r1, 3)
EXIT

View File

@@ -0,0 +1,51 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-addressing-modes */
#include "common.h"
ENTRY
/* Offset mode with immediate. Add 4 to the address register,
* which ends up * reading myvar6 instead of myvar.
*/
adr r4, myvar
ldr r5, [r4, 4]
ASSERT_EQ(r5, 0x9ABCDEF0)
/* r4 was not modified. */
ASSERT_EQ(r4, myvar)
/* Pre-indexed mode: modify register, then use it. */
adr r4, myvar
ldr r5, [r4, 4]!
ASSERT_EQ(r5, 0x9ABCDEF0)
/* r4 was modified. */
ASSERT_EQ(r4, myvar6)
/* Post-indexed mode: use register, then modify it. */
adr r4, myvar
ldr r5, [r4], 4
ASSERT_EQ(r5, 0x12345678)
/* r4 was modified. */
ASSERT_EQ(r4, myvar6)
/* Offset in register. */
adr r4, myvar
mov r5, 4
ldr r6, [r4, r5]
ASSERT_EQ(r6, 0x9ABCDEF0)
/* Offset in shifted register:
* r6 =
* (r4 + (r5 << 1))
* == *(myvar + (2 << 1))
* == *(myvar + 4)
*/
adr r4, myvar
mov r5, 2
ldr r6, [r4, r5, lsl 1]
ASSERT_EQ(r6, 0x9ABCDEF0)
EXIT
myvar:
.word 0x12345678
myvar6:
.word 0x9ABCDEF0

33
userland/arch/arm/adr.S Normal file
View File

@@ -0,0 +1,33 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-adr-instruction */
#include "common.h"
.data
data_label:
.word 0x1234678
ENTRY
adr r4, label
/* objdump tells us that this uses the literal pool,
* it does not get converted to adr, which is the better
* alternative here.
*/
ldr r5, =label
adrl r6, label
label:
ASSERT_EQ_REG(r4, r5)
ASSERT_EQ_REG(r4, r6)
#if 0
/* Error: symbol .data is in a different section.
*
* It works however in ARMv8.
* I think this means that there is no relocation type
* that takes care of this encoding in ARMv8, but there
* is one in ARMv8.
*
* If you have no idea what I'm talking about, read this:
* https://stackoverflow.com/questions/3322911/what-do-linkers-do/33690144#33690144
*/
adr r5, data_label
#endif
EXIT

27
userland/arch/arm/and.S Normal file
View File

@@ -0,0 +1,27 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-bitwise-instructions */
#include "common.h"
ENTRY
/* 0x00 && 0xFF == 0x00 */
mov r0, 0x00
and r0, 0xFF
ASSERT_EQ(r0, 0x00)
/* 0x0F && 0xF0 == 0x00 */
mov r0, 0x0F
and r0, 0xF0
ASSERT_EQ(r0, 0x00)
/* 0x0F && 0xFF == 0x0F */
mov r0, 0x0F
and r0, 0xFF
ASSERT_EQ(r0, 0x0F)
/* 0xF0 && 0xFF == 0xF0 */
mov r0, 0xF0
and r0, 0xFF
ASSERT_EQ(r0, 0xF0)
EXIT

9
userland/arch/arm/b.S Normal file
View File

@@ -0,0 +1,9 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-b-instruction */
#include "common.h"
ENTRY
/* Jump over the fail. 26-bit PC-relative. */
b ok
FAIL
ok:
EXIT

28
userland/arch/arm/beq.S Normal file
View File

@@ -0,0 +1,28 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-beq-instruction */
#include "common.h"
ENTRY
/* Smaller*/
mov r0, 1
cmp r0, 2
ASSERT(ble)
ASSERT(blt)
ASSERT(bne)
/* Equal. */
mov r1, 0
cmp r1, 0
ASSERT(beq)
ASSERT(bge)
ASSERT(ble)
/* Greater. */
mov r0, 2
cmp r0, 1
ASSERT(bge)
ASSERT(bgt)
ASSERT(bne)
EXIT

10
userland/arch/arm/bfi.S Normal file
View File

@@ -0,0 +1,10 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-bfi-instruction */
#include "common.h"
ENTRY
ldr r0, =0x11223344
ldr r1, =0xFFFFFFFF
bfi r1, r0, 8, 16
ASSERT_EQ(r1, 0xFF3344FF)
EXIT

10
userland/arch/arm/bic.S Normal file
View File

@@ -0,0 +1,10 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-bic-instruction */
#include "common.h"
ENTRY
/* 0x0F & ~0x55 == 0x0F & 0xAA == 0x0A */
mov r0, 0x0F
bic r0, 0x55
ASSERT_EQ(r0, 0x0A)
EXIT

14
userland/arch/arm/bl.S Normal file
View File

@@ -0,0 +1,14 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-bl-instruction */
#include "common.h"
ENTRY
mov r0, 1
bl inc
ASSERT_EQ(r0, 2)
EXIT
/* void inc(int *i) { (*i)++ } */
inc:
add r0, 1
bx lr

1
userland/arch/arm/build Symbolic link
View File

@@ -0,0 +1 @@
../build

20
userland/arch/arm/c/add.c Normal file
View File

@@ -0,0 +1,20 @@
/* 1 + 2 == 3
*
* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly
*/
#include <assert.h>
#include <inttypes.h>
int main(void) {
uint32_t in0 = 1, in1 = 2, out;
__asm__ (
"add %[out], %[in0], %[in1];"
: [out] "=r" (out)
: [in0] "r" (in0),
[in1] "r" (in1)
);
assert(in0 == 1);
assert(in1 == 2);
assert(out == 3);
}

1
userland/arch/arm/c/build Symbolic link
View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1,40 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#linux-system-calls
*
* arm freestanding C inline assemby Linux hello world.
*/
#include <inttypes.h>
void _start(void) {
uint32_t exit_status;
/* write */
{
char msg[] = "hello\n";
uint32_t syscall_return;
register uint32_t r0 __asm__ ("r0") = 1; /* stdout */
register char *r1 __asm__ ("r1") = msg;
register uint32_t r2 __asm__ ("r2") = sizeof(msg);
register uint32_t r8 __asm__ ("r7") = 4; /* syscall number */
__asm__ __volatile__ (
"svc 0;"
: "+r" (r0)
: "r" (r1), "r" (r2), "r" (r8)
: "memory"
);
syscall_return = r0;
exit_status = (syscall_return != sizeof(msg));
}
/* exit */
{
register uint32_t r0 __asm__ ("r0") = exit_status;
register uint32_t r7 __asm__ ("r7") = 1;
__asm__ __volatile__ (
"svc 0;"
: "+r" (r0)
: "r" (r7)
:
);
}
}

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1 @@
../test

18
userland/arch/arm/c/inc.c Normal file
View File

@@ -0,0 +1,18 @@
/* Increment a variable in inline assembly.
*
* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly
*/
#include <assert.h>
#include <inttypes.h>
int main(void) {
uint32_t my_local_var = 1;
__asm__ (
"add %[my_local_var], %[my_local_var], #1;"
: [my_local_var] "+r" (my_local_var)
:
:
);
assert(my_local_var == 2);
}

View File

@@ -0,0 +1,28 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly-floating-point-arm */
#include <assert.h>
int main(void) {
float my_float = 1.5;
__asm__ (
"vmov s0, 1.0;"
"vadd.f32 %[my_float], %[my_float], s0;"
: [my_float] "+t" (my_float)
:
: "s0"
);
assert(my_float == 2.5);
/* Undocumented %P
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89482
*/
double my_double = 1.5;
__asm__ (
"vmov.f64 d0, 1.0;"
"vadd.f64 %P[my_double], %P[my_double], d0;"
: [my_double] "+w" (my_double)
:
: "d0"
);
assert(my_double == 2.5);
}

View File

@@ -0,0 +1,34 @@
/* Like inc.c but less good since we do more work ourselves.
*
* Just doing this to test out the "m" memory constraint.
*
* GCC 8.2.0 -O0 assembles ldr line to:
*
* ....
* ldr r0, [fp, #-12]
* ....
*
* and `-O3` assembles to:
*
* ....
* ldr r0, [sp]
* ....
*
* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly
*/
#include <assert.h>
#include <inttypes.h>
int main(void) {
uint32_t my_local_var = 1;
__asm__ (
"ldr r0, %[my_local_var];"
"add r0, r0, #1;"
"str r0, %[my_local_var];"
: [my_local_var] "+m" (my_local_var)
:
: "r0"
);
assert(my_local_var == 2);
}

View File

@@ -0,0 +1,27 @@
/* GCC 8.2.0 -O0 and -O3 assembles ldr line to:
*
* ....
* movw r3, #<lower address part>
* movt r3, #<higher address part>
* ldr r0, [r3]
* ....
*
* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly
*/
#include <assert.h>
#include <inttypes.h>
uint32_t my_global_var = 1;
int main(void) {
__asm__ (
"ldr r0, %[my_global_var];"
"add r0, r0, #1;"
"str r0, %[my_global_var];"
: [my_global_var] "+m" (my_global_var)
:
: "r0"
);
assert(my_global_var == 2);
}

View File

@@ -0,0 +1,38 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gcc-inline-assembly-register-variables */
#include <assert.h>
#include <inttypes.h>
int main(void) {
register uint32_t r0 __asm__ ("r0");
register uint32_t r1 __asm__ ("r1");
uint32_t new_r0;
uint32_t new_r1;
{
/* We must set the registers immediately before calling,
* without making any function calls in between.
*/
r0 = 1;
r1 = 2;
__asm__ (
/* We intentionally use an explicit r0 and r1 here,
* just to illustrate that we are certain that the
* r0 variable will go in r0. Real code would never do this.
*/
"add %[r0], r0, #1;"
"add %[r1], r1, #1;"
/* We have to specify r0 in the constraints.*/
: [r0] "+r" (r0),
[r1] "+r" (r1)
:
:
);
/* When we are done, we must immediatly assign
* the register variables to regular variables.
*/
new_r0 = r0;
new_r1 = r1;
}
assert(new_r0 == 2);
assert(new_r1 == 3);
}

1
userland/arch/arm/c/test Symbolic link
View File

@@ -0,0 +1 @@
../test

17
userland/arch/arm/clz.S Normal file
View File

@@ -0,0 +1,17 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#data-bitwise-instructions */
#include "common.h"
ENTRY
ldr r0, =0x7FFFFFFF
clz r1, r0
ASSERT_EQ(r1, 1)
ldr r0, =0x3FFFFFFF
clz r1, r0
ASSERT_EQ(r1, 2)
ldr r0, =0x1FFFFFFF
clz r1, r0
ASSERT_EQ(r1, 3)
EXIT

View File

@@ -0,0 +1,14 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gnu-gas-assembler-comments */
#include "common.h"
ENTRY
# mycomment
@ mycomment
/* # only works at the beginning of the line.
* Error: garbage following instruction -- `nop #comment'
*/
#if 0
nop # mycomment
#endif
nop @ mycomment
EXIT

View File

@@ -0,0 +1,90 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#userland-assembly-c-standard-library */
#ifndef COMMON_ARCH_H
#define COMMON_ARCH_H
.syntax unified
/* Assert that a register equals a constant.
* * reg: the register to check
* * const: the constant to compare to. Only works for literals or labels, not for registers.
* For register / register comparison, use ASSERT_EQ_REG.
*/
#define ASSERT_EQ(reg, const) \
mov r0, reg; \
ldr r1, =const; \
ASSERT_EQ_DO; \
;
#define ASSERT_EQ_DO \
bl assert_eq_32; \
cmp r0, 0; \
ASSERT(beq); \
;
#define ASSERT_EQ_REG(reg1, reg2) \
str reg2, [sp, -4]!; \
mov r0, reg1; \
ldr r1, [sp], 4; \
ASSERT_EQ_DO; \
;
/* Assert that two arrays are the same. */
#define ASSERT_MEMCMP(label1, label2, const_size) \
ldr r0, =label1; \
ldr r1, =label2; \
ldr r2, =const_size; \
bl assert_memcmp; \
cmp r0, 0; \
ASSERT(beq); \
;
/* Store all callee saved registers, and LR in case we make further BL calls.
*
* Also save the input arguments r0-r3 on the stack, so we can access them later on,
* despite those registers being overwritten.
*/
#define ENTRY \
.text; \
.global asm_main; \
asm_main: \
stmdb sp!, {r0-r12, lr}; \
asm_main_after_prologue: \
;
/* Meant to be called at the end of ENTRY.*
*
* Branching to "fail" makes tests fail with exit status 1.
*
* If EXIT is reached, the program ends successfully.
*
* Restore LR and bx jump to it to return from asm_main.
*/
#define EXIT \
mov r0, 0; \
mov r1, 0; \
b pass; \
fail: \
ldr r1, [sp]; \
str r0, [r1]; \
mov r0, 1; \
pass: \
add sp, 16; \
ldmia sp!, {r4-r12, lr}; \
bx lr; \
;
/* Always fail. */
#define FAIL \
ldr r0, =__LINE__; \
b fail; \
;
#define MEMCMP(s1, s2, n) \
ldr r0, =s1; \
ldr r1, =s2; \
ldr r2, =n; \
bl memcmp; \
;
#endif

16
userland/arch/arm/cond.S Normal file
View File

@@ -0,0 +1,16 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-conditional-execution */
#include "common.h"
ENTRY
mov r0, 0
mov r1, 1
cmp r0, 1
/* Previous cmp failed, skip this operation. */
addeq r1, 1
ASSERT_EQ(r1, 1)
cmp r0, 0
/* Previous passed, do this operation. */
addeq r1, 1
ASSERT_EQ(r1, 2)
EXIT

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1,23 @@
/* arm freestanding Linux hello world
* https://github.com/cirosantilli/linux-kernel-module-cheat#linux-system-calls
*/
.syntax unified
.text
.global _start
_start:
asm_main_after_prologue:
/* write */
mov r0, 1 /* stdout */
adr r1, msg /* buffer */
ldr r2, =len /* len */
mov r7, 4 /* syscall number */
svc 0
/* exit */
mov r0, 0 /* exit status */
mov r7, 1 /* syscall number */
svc 0
msg:
.ascii "hello\n"
len = . - msg

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1,30 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gnu-gas-assembler data sizes */
#include "common.h"
.data
mybyte:
.byte 0x12
myword:
.word 0x1234
mylong:
.long 0x12345678
myquad:
.quad 0x123456789ABCDEF0
myocta:
.octa 0x123456789ABCDEF0123456789ABCDEF0
theend:
ENTRY
#define ASSERT_DIFF(label1, label2, result) \
ldr r0, =label1; \
ldr r1, =label2; \
sub r0, r1, r0; \
ASSERT_EQ(r0, result)
ASSERT_DIFF(mybyte, myword, 1)
ASSERT_DIFF(myword, mylong, 4)
ASSERT_DIFF(mylong, myquad, 4)
ASSERT_DIFF(myquad, myocta, 8)
ASSERT_DIFF(myocta, theend, 16)
#undef ASSERT_DIF
EXIT

View File

@@ -0,0 +1,24 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-gnu-instruction-gas-assembler-immediates */
#include "common.h"
ENTRY
/* This is the default. We hack it in common.h however. */
.syntax divided
/* These fail. */
#if 0
mov r0, 1
mov r0, 0x1
#endif
mov r0, #1
mov r0, #0x1
mov r0, $1
mov r0, $0x1
.syntax unified
mov r0, 1
mov r0, 0x1
mov r0, 1
mov r0, 0x1
mov r0, $1
mov r0, $0x1
EXIT

View File

@@ -0,0 +1,27 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-loop-instruction-over-array */
#include "common.h"
#define NELEM 4
#define ELEM_SIZE 4
.data;
my_array:
.word 0x11111111, 0x22222222, 0x33333333, 0x44444444
my_array_expect:
.word 0x11111112, 0x22222223, 0x33333334, 0x44444445
ENTRY
/* Increment. */
ldr r0, =my_array
mov r1, NELEM
increment:
ldr r2, [r0]
add r2, 1
/* Post index usage. */
str r2, [r0], ELEM_SIZE
sub r1, 1
cmp r1, 0
bne increment
ASSERT_MEMCMP(my_array, my_array_expect, 0x10)
EXIT

61
userland/arch/arm/ldmia.S Normal file
View File

@@ -0,0 +1,61 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-loop-instruction-over-array */
#include "common.h"
#define NELEM 4
#define ELEM_SIZE 4
.data;
my_array_0:
.word 0x11111111, 0x22222222, 0x33333333, 0x44444444
my_array_1:
.word 0x55555555, 0x66666666, 0x77777777, 0x88888888
ENTRY
/* Load r5, r6, r7 and r8 starting from the address in r4. Don't change r4 */
ldr r4, =my_array_0
ldr r5, =0
ldr r6, =0
ldr r7, =0
ldr r8, =0
ldmia r4, {r5-r8}
ASSERT_EQ(r4, my_array_0)
ASSERT_EQ(r5, 0x11111111)
ASSERT_EQ(r6, 0x22222222)
ASSERT_EQ(r7, 0x33333333)
ASSERT_EQ(r8, 0x44444444)
/* Swapping the order of r5 and r6 on the mnemonic makes no difference to load order.
*
* But it gives an assembler warning, so we won't do it by default:
*
* ldmia.S: Assembler messages:
* ldmia.S:32: Warning: register range not in ascending order
*/
#if 0
ldr r4, =my_array_0
ldr r5, =0
ldr r6, =0
ldmia r4, {r6,r5}
ASSERT_EQ(r5, 0x11111111)
ASSERT_EQ(r6, 0x22222222)
#endif
/* Modify the array */
ldr r4, =my_array_1
ldr r5, =0x55555555
ldr r6, =0x66666666
ldr r7, =0x77777777
ldr r8, =0x88888888
stmdb r4, {r5-r8}
/* Verify that my_array_0 changed and is equal to my_array_1. */
ASSERT_MEMCMP(my_array_0, my_array_1, 0x10)
/* Load registers and increment r4. */
ldr r4, =my_array_0
ldmia r4!, {r5-r8}
ASSERT_EQ(r4, my_array_1)
EXIT

View File

@@ -0,0 +1,65 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-ldr-instruction-pseudo-instruction */
#include "common.h"
ENTRY
/* Mnemonic for a PC relative load:
*
* ....
* ldr r0, [pc, offset]
* r0 = myvar
* ....
*/
ldr r0, myvar
ASSERT_EQ(r0, 0x12345678)
/* Mnemonic PC relative load with an offset.
* Load myvar2 instead of myvar.
*/
ldr r0, myvar + 4
ASSERT_EQ(r0, 0x9ABCDEF0)
/* First store the address in r0 using a magic =myvar, which creates
* a new variable containing the address and PC-relative addresses it
* https://stackoverflow.com/questions/17214962/what-is-the-difference-between-label-equals-sign-and-label-brackets-in-ar
*
* Use the adr instruction would likely be better for this application however.
*
* ....
* r0 = &myvar
* r1 = *r0
* ....
*/
ldr r0, =myvar
ldr r1, [r0]
ASSERT_EQ(r1, 0x12345678)
/* More efficiently, use r0 as the address to read, and write to r0 itself. */
ldr r0, =myvar
ldr r0, [r0]
ASSERT_EQ(r0, 0x12345678)
/* Same as =myvar but store a constant to a register.
* Can also be done with movw and movt. */
ldr r0, =0x11112222
ASSERT_EQ(r0, 0x11112222)
/* We can also use GAS tolower16 and topper16 and movw and movt
* to load the address of myvar into r0 with two immediates.
*
* This results in one extra 4 byte instruction read from memory,
* and one less data read, so it is likely more cache efficient.
*
* https://sourceware.org/binutils/docs-2.19/as/ARM_002dRelocations.html
*/
movw r0, #:lower16:myvar
movt r0, #:upper16:myvar
ldr r1, [r0]
ASSERT_EQ(r1, 0x12345678)
EXIT
myvar:
.word 0x12345678
myvar2:
.word 0x9ABCDEF0

12
userland/arch/arm/ldrb.S Normal file
View File

@@ -0,0 +1,12 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-ldrh-instruction-and-ldrb */
#include "common.h"
ENTRY
ldr r0, =myvar
mov r1, 0x0
ldrb r1, [r0]
ASSERT_EQ(r1, 0x00000078)
EXIT
myvar:
.word 0x12345678

12
userland/arch/arm/ldrh.S Normal file
View File

@@ -0,0 +1,12 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-ldrh-instruction-and-ldrb */
#include "common.h"
ENTRY
ldr r0, =myvar
mov r1, 0x0
ldrh r1, [r0]
ASSERT_EQ(r1, 0x00005678)
EXIT
myvar:
.word 0x12345678

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1,59 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#calling-convention */
#include "common.h"
.data
puts_s:
.asciz "hello puts"
printf_format:
.asciz "hello printf %x\n"
my_array_0:
.word 0x11111111, 0x22222222, 0x33333333, 0x44444444
my_array_1:
.word 0x55555555, 0x66666666, 0x77777777, 0x88888888
ENTRY
/* puts("hello world") */
/* r0 is first argument. */
ldr r0, =puts_s
bl puts
/* Check exit status >= 0 for success. */
cmp r0, 0
ASSERT(bge)
/* printf */
ldr r0, =printf_format
ldr r1, =0x12345678
bl printf
cmp r0, 0
ASSERT(bge)
/* memcpy and memcmp. */
/* Smaller. */
ldr r0, =my_array_0
ldr r1, =my_array_1
ldr r2, =0x10
bl memcmp
cmp r0, 0
ASSERT(blt)
/* Copy. */
ldr r0, =my_array_0
ldr r1, =my_array_1
ldr r2, =0x10
bl memcpy
/* Equal. */
ldr r0, =my_array_0
ldr r1, =my_array_1
ldr r2, =0x10
bl memcmp
ASSERT_EQ(r0, 0)
/* exit(0) */
mov r0, 0
bl exit
/* Never reached, just for the fail symbol. */
EXIT

View File

@@ -0,0 +1 @@
../test

19
userland/arch/arm/mov.S Normal file
View File

@@ -0,0 +1,19 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-mov-instruction */
#include "common.h"
ENTRY
/* Immediate. */
mov r0, 0
ASSERT_EQ(r0, 0)
mov r0, 1
ASSERT_EQ(r0, 1)
/* Register. */
mov r0, 0
mov r1, 1
mov r1, r0
ASSERT_EQ(r1, 0)
EXIT

27
userland/arch/arm/movw.S Normal file
View File

@@ -0,0 +1,27 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-movw-and-movt-instructions */
#include "common.h"
ENTRY
/* movt (top) and movw (TODO what is w) set the higher
* and lower 16 bits of the register.
*/
movw r0, 0xFFFF
movt r0, 0x1234
add r0, 1
ASSERT_EQ(r0, 0x12350000)
/* movw also zeroes out the top bits, allowing small 16-bit
* C constants to be assigned in a single instruction.
*
* It differs from mov because mov can only encode 8 bits
* at a time, while movw can encode 16.
*
* movt does not modify the lower bits however.
*/
ldr r0, =0x12345678
movw r0, 0x1111
ASSERT_EQ(r0, 0x00001111)
EXIT

15
userland/arch/arm/mul.S Normal file
View File

@@ -0,0 +1,15 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-data-processing-instructions
*
* Multiplication.
*/
#include "common.h"
ENTRY
/* 2 * 3 = 6 */
mov r0, 0
mov r1, 2
mov r2, 3
mul r1, r2
ASSERT_EQ(r1, 6)
EXIT

32
userland/arch/arm/nop.S Normal file
View File

@@ -0,0 +1,32 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-nop-instruction */
#include "common.h"
ENTRY
/* Disassembles as:
*
* ....
* nop {0}
* ....
*
* TODO what is the `{0}`?
*/
nop
/* Disassembles as:
*
* ....
* nop ; (mov r0, r0)
* ....
*/
mov r0, r0
/* Disassemble as mov. TODO Why not as nop as in `mov r0, r0`?
* Do they have any effect?
*/
mov r1, r1
mov r8, r8
/* And there are other nops as well? Disassembles as `and`. */
and r0, r0, r0
EXIT

31
userland/arch/arm/push.S Normal file
View File

@@ -0,0 +1,31 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-ldmia-instruction */
#include "common.h"
ENTRY
/* Save sp before push. */
mov r4, sp
/* Push. */
mov r5, 1
mov r6, 2
push {r5, r6}
/* Save sp after push. */
mov r5, sp
/* Restore. */
mov r7, 0
mov r8, 0
pop {r7, r8}
ASSERT_EQ(r7, 1)
ASSERT_EQ(r8, 2)
/* Check that stack pointer moved down by 8 bytes
* (2 registers x 4 bytes each).
*/
sub r4, r5
ASSERT_EQ(r4, 8)
EXIT

12
userland/arch/arm/rbit.S Normal file
View File

@@ -0,0 +1,12 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#arm-data-processing-instructions
*
* Reverse bit order.
*/
#include "common.h"
ENTRY
ldr r0, =0b00000001001000110100010101100101
rbit r1, r0
ASSERT_EQ(r1, 0b10100110101000101100010010000000)
EXIT

Some files were not shown because too many files have changed in this diff Show More