Skip to content

Commit 9917b0e

Browse files
committed
x86 asm: move stack instructions in from x86-assembly-cheat
1 parent 8efd4f8 commit 9917b0e

File tree

6 files changed

+214
-3
lines changed

6 files changed

+214
-3
lines changed

README.adoc

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12136,6 +12136,7 @@ The following <<userland-setup>> programs illustrate how to make system calls:
1213612136

1213712137
* x86_64
1213812138
** link:userland/arch/x86_64/freestanding/linux/hello.S[]
12139+
** link:userland/arch/x86_64/freestanding/linux/int_system_call.S[]
1213912140
** link:userland/arch/x86_64/inline_asm/freestanding/linux/hello.c[]
1214012141
** link:userland/arch/x86_64/inline_asm/freestanding/linux/hello_regvar.c[]
1214112142
* arm
@@ -12391,9 +12392,15 @@ For the newer x86_64 registers, the naming convention is somewhat saner:
1239112392

1239212393
Most of the 8 older x86 general purpose registers are not "really" general purpose in the sense that a few instructions magically use them without an explicit encoding. This is reflected in their names:
1239312394

12394-
* RAX: Accumulator. The general place where you add, subtract and otherwise manipulate results in-place. Magic for example for <<MUL,x86 binary arithmetic instructions>>
12395+
* RAX: Accumulator. The general place where you add, subtract and otherwise manipulate results in-place. Magic for example for <<x86-binary-arithmetic-instructions,MUL>>.
1239512396
* RCX, RSI, RDI: Counter, Source and Destination. Used in <<x86-string-instructions>>
1239612397

12398+
==== x86 FLAGS registers
12399+
12400+
https://en.wikipedia.org/wiki/FLAGS_register
12401+
12402+
TODO: add some more info here. Just need a link placeholder for now.
12403+
1239712404
=== x86 addressing modes
1239812405

1239912406
Example: link:userland/arch/x86_64/address_modes.S[]
@@ -12438,6 +12445,27 @@ Bibliography:
1243812445
** link:userland/arch/x86_64/movzx.S[]: MOVZX
1243912446
** link:userland/arch/x86_64/movsx.S[]: MOVSX
1244012447
* link:userland/arch/x86_64/bswap.S[]: BSWAP: convert between little endian and big endian
12448+
* link:userland/arch/x86_64/pushf.S[] PUSHF: <<x86-push-and-pop-instructions,push and pop>> the <<x86-flags-registers>> to / from the stack
12449+
12450+
==== x86 PUSH and POP instructions
12451+
12452+
link:userland/arch/x86_64/push.S[]
12453+
12454+
`push %rax` is basically equivalent to:
12455+
12456+
....
12457+
sub $8, %rsp
12458+
mov %rax, (%rsp)
12459+
....
12460+
12461+
and `pop %rax`:
12462+
12463+
....
12464+
mov (%rsp), %rax
12465+
add $8, %rsp
12466+
....
12467+
12468+
Why do those instructions exist at all vs MOV / ADD / SUB: http://stackoverflow.com/questions/4584089/what-is-the-function-of-push-pop-registers-in-x86-assembly/33583134#33583134
1244112469

1244212470
==== x86 CQTO and CLTQ instructions
1244312471

@@ -12450,7 +12478,7 @@ Instructions without E suffix: sign extend RAX into RDX:RAX.
1245012478

1245112479
Instructions E suffix: sign extend withing RAX itself.
1245212480

12453-
Common combo with idiv 32-bit, which takes the input from `edx:eax`: so you need to set up `edx` before calling it.
12481+
Common combo with IDIV 32-bit, which takes the input from EDX:EAX: so you need to set up EDX before calling it.
1245412482

1245512483
Has some Intel vs AT&T name overload hell:
1245612484

@@ -12697,6 +12725,38 @@ REP and REPZ also additionally stop if the comparison operation they repeat fail
1269712725
* REP: INS, OUTS, MOVS, LODS, and STOS
1269812726
* REPZ: CMPS and SCAS
1269912727

12728+
==== x86 ENTER and LEAVE instructions
12729+
12730+
link:userland/arch/x86_64/enter.S[]
12731+
12732+
These instructions were designed to allocate and deallocate function stack frames in the prologue and epilogue: http://stackoverflow.com/questions/5959890/enter-vs-push-ebp-mov-ebp-esp-sub-esp-imm-and-leave-vs-mov-esp-ebp
12733+
12734+
ENTER appears obsolete and is kept mostly for backwards compatibility. LEAVE is still emitted by some compilers.
12735+
12736+
ENTER A, B is basically equivalent to:
12737+
12738+
....
12739+
push %rbp
12740+
mov %rsp, %rbp
12741+
sub %rsp, A
12742+
....
12743+
12744+
which implies an allocation of:
12745+
12746+
* one dword to remember EBP
12747+
* A bytes for local function variables
12748+
12749+
I didn't have the patience to study the B parameter, and it does not seem to be used often: http://stackoverflow.com/questions/26323215/do-any-languages-compilers-utilize-the-x86-enter-instruction-with-a-nonzero-ne
12750+
12751+
LEAVE is equivalent to:
12752+
12753+
....
12754+
mov %rbp, %rsp
12755+
pop %rbp
12756+
....
12757+
12758+
which restores RSP and RBP to the values they had before the prologue.
12759+
1270012760
=== x86 miscellaneous instructions
1270112761

1270212762
<<intel-manual-1>> 5.1.13 "Miscellaneous Instructions"

userland/arch/x86_64/enter.S

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* https://github.com/cirosantilli/linux-kernel-module-cheat#x86-enter-and-leave-instructions */
2+
3+
#include <lkmc.h>
4+
5+
LKMC_PROLOGUE
6+
/* Save values of interest before enter. */
7+
mov %rbp, %r12
8+
mov %rsp, %r13
9+
enter $16, $0
10+
mov %rsp, %r14
11+
12+
/* Restore stack so that we can do our assertions. */
13+
add $16, %rsp
14+
leave
15+
16+
/* ENTER pushed the stack down 24 bytes:
17+
*
18+
* * 8 due to `push %rbp` that `enter` does automatically
19+
* * 16 due to `enter $16`
20+
*/
21+
sub %r14, %r13
22+
LKMC_ASSERT_EQ(%r13, $24)
23+
LKMC_EPILOGUE

userland/arch/x86_64/freestanding/linux/hello.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ asm_main_after_prologue:
1919
syscall
2020
msg:
2121
.ascii "hello\n"
22-
len = . - msg
22+
len = . - msg
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* https://github.com/cirosantilli/linux-kernel-module-cheat#linux-system-calls
2+
*
3+
* int $0x80 sycalls are still supported by x86_64 for some kind of backwards compatibility,
4+
* (TODO so when x86_64 started it didn't have SYSCALL?) althoug you should prefre
5+
* SYSCALL / VSDO.
6+
*
7+
* https://stackoverflow.com/questions/29440225/in-linux-x86-64-are-syscalls-and-int-0x80-related
8+
*/
9+
10+
.text
11+
.global _start
12+
_start:
13+
/* write */
14+
mov $4, %rax
15+
mov $1, %rbx
16+
lea msg(%rip), %rcx
17+
mov $len, %rdx
18+
int $0x80
19+
20+
/* exit */
21+
mov $1, %rax
22+
mov $0, %rbx
23+
int $0x80
24+
msg:
25+
.ascii "hello\n"
26+
len = . - msg

userland/arch/x86_64/push.S

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* https://github.com/cirosantilli/linux-kernel-module-cheat#x86-push-and-pop-instructions */
2+
3+
#include <lkmc.h>
4+
5+
LKMC_PROLOGUE
6+
/* register hello world. */
7+
mov %rsp, %r12
8+
mov $0x123456789ABCDEF0, %rax
9+
push %rax
10+
/* Save the stack delta. */
11+
sub %rsp, %r12
12+
/* Save the stack value. */
13+
mov (%rsp), %r13
14+
/* Restore the stack and save its value to R14. */
15+
pop %r14
16+
/* The stack still goes down by 8 even though we pushed a 4-byte immediate. */
17+
LKMC_ASSERT_EQ(%r12, $8)
18+
LKMC_ASSERT_EQ(%r13, $0x123456789ABCDEF0)
19+
LKMC_ASSERT_EQ(%r14, $0x123456789ABCDEF0)
20+
21+
/* Immediate. Can only push up to 4 byte immediates. */
22+
mov %rsp, %r12
23+
push $0x12345678
24+
sub %rsp, %r12
25+
mov (%rsp), %r13
26+
pop %r14
27+
/* The stack still goes down by 8 even though we pushed a 4-byte immediate. */
28+
LKMC_ASSERT_EQ(%r12, $8)
29+
LKMC_ASSERT_EQ(%r13, $0x12345678)
30+
LKMC_ASSERT_EQ(%r14, $0x12345678)
31+
32+
/* Word example. */
33+
mov %rsp, %r12
34+
mov $0x1234, %ax
35+
push %ax
36+
sub %rsp, %r12
37+
mov $0, %r13
38+
mov (%rsp), %r13w
39+
mov $0, %r14
40+
pop %r14w
41+
/* The stack was decremented only by 2 as expected. */
42+
LKMC_ASSERT_EQ(%r12, $2)
43+
LKMC_ASSERT_EQ(%r13, $0x1234)
44+
LKMC_ASSERT_EQ(%r14, $0x1234)
45+
LKMC_EPILOGUE

userland/arch/x86_64/pushf.S

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* https://github.com/cirosantilli/linux-kernel-module-cheat#x86-data-transfer-instructions */
2+
3+
#include <lkmc.h>
4+
5+
LKMC_PROLOGUE
6+
/* First example. */
7+
8+
/* Clear carry flag. */
9+
clc
10+
/* Save RSP before PUSHF. */
11+
mov %rsp, %r12
12+
pushf
13+
/* Save stack value after PUSHF. Should contain the original FLAGS. */
14+
mov (%rsp), %r13
15+
/* Re-align stack to 16-bits for our asserts. */
16+
sub $8, %rsp
17+
18+
/* The stack went down by 16: 8 from PUSHF, 8 from our SUB. */
19+
sub %rsp, %r12
20+
LKMC_ASSERT_EQ(%r12, $16)
21+
22+
/* Check that bit 0 (Carry Flag) of R13 is clear. */
23+
bt $0, %r13
24+
LKMC_ASSERT(jnc)
25+
26+
/* Restore the stack. */
27+
add $16, %rsp
28+
29+
/* Now let's set carry flag instead. */
30+
stc
31+
pushf
32+
mov (%rsp), %r13
33+
sub $8, %rsp
34+
bt $0, %r13
35+
/* Assert that it was pushed to stack set. */
36+
LKMC_ASSERT(jc)
37+
add $16, %rsp
38+
39+
/* POPF pops the stack into flags of course. */
40+
clc
41+
pushf
42+
stc
43+
popf
44+
LKMC_ASSERT(jnc)
45+
46+
/* PUSHFQ has the same opcode as PUSHF in the Intel manual.
47+
* which mentions that PUSHF can be requested with a prefix.
48+
*
49+
* GNU GAS 2.32 emits the same PUSHFQ code for both by default.
50+
* according to objdump.
51+
*/
52+
clc
53+
pushf
54+
stc
55+
popf
56+
LKMC_ASSERT(jnc)
57+
LKMC_EPILOGUE

0 commit comments

Comments
 (0)