mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-23 02:05:57 +01:00
x86 asm: move stack instructions in from x86-assembly-cheat
This commit is contained in:
64
README.adoc
64
README.adoc
@@ -12136,6 +12136,7 @@ The following <<userland-setup>> programs illustrate how to make system calls:
|
|||||||
|
|
||||||
* x86_64
|
* x86_64
|
||||||
** link:userland/arch/x86_64/freestanding/linux/hello.S[]
|
** link:userland/arch/x86_64/freestanding/linux/hello.S[]
|
||||||
|
** link:userland/arch/x86_64/freestanding/linux/int_system_call.S[]
|
||||||
** link:userland/arch/x86_64/inline_asm/freestanding/linux/hello.c[]
|
** link:userland/arch/x86_64/inline_asm/freestanding/linux/hello.c[]
|
||||||
** link:userland/arch/x86_64/inline_asm/freestanding/linux/hello_regvar.c[]
|
** link:userland/arch/x86_64/inline_asm/freestanding/linux/hello_regvar.c[]
|
||||||
* arm
|
* arm
|
||||||
@@ -12391,9 +12392,15 @@ For the newer x86_64 registers, the naming convention is somewhat saner:
|
|||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
* RAX: Accumulator. The general place where you add, subtract and otherwise manipulate results in-place. Magic for example for <<MUL,x86 binary arithmetic instructions>>
|
* RAX: Accumulator. The general place where you add, subtract and otherwise manipulate results in-place. Magic for example for <<x86-binary-arithmetic-instructions,MUL>>.
|
||||||
* RCX, RSI, RDI: Counter, Source and Destination. Used in <<x86-string-instructions>>
|
* RCX, RSI, RDI: Counter, Source and Destination. Used in <<x86-string-instructions>>
|
||||||
|
|
||||||
|
==== x86 FLAGS registers
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/FLAGS_register
|
||||||
|
|
||||||
|
TODO: add some more info here. Just need a link placeholder for now.
|
||||||
|
|
||||||
=== x86 addressing modes
|
=== x86 addressing modes
|
||||||
|
|
||||||
Example: link:userland/arch/x86_64/address_modes.S[]
|
Example: link:userland/arch/x86_64/address_modes.S[]
|
||||||
@@ -12438,6 +12445,27 @@ Bibliography:
|
|||||||
** link:userland/arch/x86_64/movzx.S[]: MOVZX
|
** link:userland/arch/x86_64/movzx.S[]: MOVZX
|
||||||
** link:userland/arch/x86_64/movsx.S[]: MOVSX
|
** link:userland/arch/x86_64/movsx.S[]: MOVSX
|
||||||
* link:userland/arch/x86_64/bswap.S[]: BSWAP: convert between little endian and big endian
|
* link:userland/arch/x86_64/bswap.S[]: BSWAP: convert between little endian and big endian
|
||||||
|
* link:userland/arch/x86_64/pushf.S[] PUSHF: <<x86-push-and-pop-instructions,push and pop>> the <<x86-flags-registers>> to / from the stack
|
||||||
|
|
||||||
|
==== x86 PUSH and POP instructions
|
||||||
|
|
||||||
|
link:userland/arch/x86_64/push.S[]
|
||||||
|
|
||||||
|
`push %rax` is basically equivalent to:
|
||||||
|
|
||||||
|
....
|
||||||
|
sub $8, %rsp
|
||||||
|
mov %rax, (%rsp)
|
||||||
|
....
|
||||||
|
|
||||||
|
and `pop %rax`:
|
||||||
|
|
||||||
|
....
|
||||||
|
mov (%rsp), %rax
|
||||||
|
add $8, %rsp
|
||||||
|
....
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
==== x86 CQTO and CLTQ instructions
|
==== x86 CQTO and CLTQ instructions
|
||||||
|
|
||||||
@@ -12450,7 +12478,7 @@ Instructions without E suffix: sign extend RAX into RDX:RAX.
|
|||||||
|
|
||||||
Instructions E suffix: sign extend withing RAX itself.
|
Instructions E suffix: sign extend withing RAX itself.
|
||||||
|
|
||||||
Common combo with idiv 32-bit, which takes the input from `edx:eax`: so you need to set up `edx` before calling it.
|
Common combo with IDIV 32-bit, which takes the input from EDX:EAX: so you need to set up EDX before calling it.
|
||||||
|
|
||||||
Has some Intel vs AT&T name overload hell:
|
Has some Intel vs AT&T name overload hell:
|
||||||
|
|
||||||
@@ -12697,6 +12725,38 @@ REP and REPZ also additionally stop if the comparison operation they repeat fail
|
|||||||
* REP: INS, OUTS, MOVS, LODS, and STOS
|
* REP: INS, OUTS, MOVS, LODS, and STOS
|
||||||
* REPZ: CMPS and SCAS
|
* REPZ: CMPS and SCAS
|
||||||
|
|
||||||
|
==== x86 ENTER and LEAVE instructions
|
||||||
|
|
||||||
|
link:userland/arch/x86_64/enter.S[]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
ENTER appears obsolete and is kept mostly for backwards compatibility. LEAVE is still emitted by some compilers.
|
||||||
|
|
||||||
|
ENTER A, B is basically equivalent to:
|
||||||
|
|
||||||
|
....
|
||||||
|
push %rbp
|
||||||
|
mov %rsp, %rbp
|
||||||
|
sub %rsp, A
|
||||||
|
....
|
||||||
|
|
||||||
|
which implies an allocation of:
|
||||||
|
|
||||||
|
* one dword to remember EBP
|
||||||
|
* A bytes for local function variables
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
LEAVE is equivalent to:
|
||||||
|
|
||||||
|
....
|
||||||
|
mov %rbp, %rsp
|
||||||
|
pop %rbp
|
||||||
|
....
|
||||||
|
|
||||||
|
which restores RSP and RBP to the values they had before the prologue.
|
||||||
|
|
||||||
=== x86 miscellaneous instructions
|
=== x86 miscellaneous instructions
|
||||||
|
|
||||||
<<intel-manual-1>> 5.1.13 "Miscellaneous Instructions"
|
<<intel-manual-1>> 5.1.13 "Miscellaneous Instructions"
|
||||||
|
|||||||
23
userland/arch/x86_64/enter.S
Normal file
23
userland/arch/x86_64/enter.S
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#x86-enter-and-leave-instructions */
|
||||||
|
|
||||||
|
#include <lkmc.h>
|
||||||
|
|
||||||
|
LKMC_PROLOGUE
|
||||||
|
/* Save values of interest before enter. */
|
||||||
|
mov %rbp, %r12
|
||||||
|
mov %rsp, %r13
|
||||||
|
enter $16, $0
|
||||||
|
mov %rsp, %r14
|
||||||
|
|
||||||
|
/* Restore stack so that we can do our assertions. */
|
||||||
|
add $16, %rsp
|
||||||
|
leave
|
||||||
|
|
||||||
|
/* ENTER pushed the stack down 24 bytes:
|
||||||
|
*
|
||||||
|
* * 8 due to `push %rbp` that `enter` does automatically
|
||||||
|
* * 16 due to `enter $16`
|
||||||
|
*/
|
||||||
|
sub %r14, %r13
|
||||||
|
LKMC_ASSERT_EQ(%r13, $24)
|
||||||
|
LKMC_EPILOGUE
|
||||||
@@ -19,4 +19,4 @@ asm_main_after_prologue:
|
|||||||
syscall
|
syscall
|
||||||
msg:
|
msg:
|
||||||
.ascii "hello\n"
|
.ascii "hello\n"
|
||||||
len = . - msg
|
len = . - msg
|
||||||
|
|||||||
26
userland/arch/x86_64/freestanding/linux/int_system_call.S
Normal file
26
userland/arch/x86_64/freestanding/linux/int_system_call.S
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#linux-system-calls
|
||||||
|
*
|
||||||
|
* int $0x80 sycalls are still supported by x86_64 for some kind of backwards compatibility,
|
||||||
|
* (TODO so when x86_64 started it didn't have SYSCALL?) althoug you should prefre
|
||||||
|
* SYSCALL / VSDO.
|
||||||
|
*
|
||||||
|
* https://stackoverflow.com/questions/29440225/in-linux-x86-64-are-syscalls-and-int-0x80-related
|
||||||
|
*/
|
||||||
|
|
||||||
|
.text
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
/* write */
|
||||||
|
mov $4, %rax
|
||||||
|
mov $1, %rbx
|
||||||
|
lea msg(%rip), %rcx
|
||||||
|
mov $len, %rdx
|
||||||
|
int $0x80
|
||||||
|
|
||||||
|
/* exit */
|
||||||
|
mov $1, %rax
|
||||||
|
mov $0, %rbx
|
||||||
|
int $0x80
|
||||||
|
msg:
|
||||||
|
.ascii "hello\n"
|
||||||
|
len = . - msg
|
||||||
45
userland/arch/x86_64/push.S
Normal file
45
userland/arch/x86_64/push.S
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#x86-push-and-pop-instructions */
|
||||||
|
|
||||||
|
#include <lkmc.h>
|
||||||
|
|
||||||
|
LKMC_PROLOGUE
|
||||||
|
/* register hello world. */
|
||||||
|
mov %rsp, %r12
|
||||||
|
mov $0x123456789ABCDEF0, %rax
|
||||||
|
push %rax
|
||||||
|
/* Save the stack delta. */
|
||||||
|
sub %rsp, %r12
|
||||||
|
/* Save the stack value. */
|
||||||
|
mov (%rsp), %r13
|
||||||
|
/* Restore the stack and save its value to R14. */
|
||||||
|
pop %r14
|
||||||
|
/* The stack still goes down by 8 even though we pushed a 4-byte immediate. */
|
||||||
|
LKMC_ASSERT_EQ(%r12, $8)
|
||||||
|
LKMC_ASSERT_EQ(%r13, $0x123456789ABCDEF0)
|
||||||
|
LKMC_ASSERT_EQ(%r14, $0x123456789ABCDEF0)
|
||||||
|
|
||||||
|
/* Immediate. Can only push up to 4 byte immediates. */
|
||||||
|
mov %rsp, %r12
|
||||||
|
push $0x12345678
|
||||||
|
sub %rsp, %r12
|
||||||
|
mov (%rsp), %r13
|
||||||
|
pop %r14
|
||||||
|
/* The stack still goes down by 8 even though we pushed a 4-byte immediate. */
|
||||||
|
LKMC_ASSERT_EQ(%r12, $8)
|
||||||
|
LKMC_ASSERT_EQ(%r13, $0x12345678)
|
||||||
|
LKMC_ASSERT_EQ(%r14, $0x12345678)
|
||||||
|
|
||||||
|
/* Word example. */
|
||||||
|
mov %rsp, %r12
|
||||||
|
mov $0x1234, %ax
|
||||||
|
push %ax
|
||||||
|
sub %rsp, %r12
|
||||||
|
mov $0, %r13
|
||||||
|
mov (%rsp), %r13w
|
||||||
|
mov $0, %r14
|
||||||
|
pop %r14w
|
||||||
|
/* The stack was decremented only by 2 as expected. */
|
||||||
|
LKMC_ASSERT_EQ(%r12, $2)
|
||||||
|
LKMC_ASSERT_EQ(%r13, $0x1234)
|
||||||
|
LKMC_ASSERT_EQ(%r14, $0x1234)
|
||||||
|
LKMC_EPILOGUE
|
||||||
57
userland/arch/x86_64/pushf.S
Normal file
57
userland/arch/x86_64/pushf.S
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/* https://github.com/cirosantilli/linux-kernel-module-cheat#x86-data-transfer-instructions */
|
||||||
|
|
||||||
|
#include <lkmc.h>
|
||||||
|
|
||||||
|
LKMC_PROLOGUE
|
||||||
|
/* First example. */
|
||||||
|
|
||||||
|
/* Clear carry flag. */
|
||||||
|
clc
|
||||||
|
/* Save RSP before PUSHF. */
|
||||||
|
mov %rsp, %r12
|
||||||
|
pushf
|
||||||
|
/* Save stack value after PUSHF. Should contain the original FLAGS. */
|
||||||
|
mov (%rsp), %r13
|
||||||
|
/* Re-align stack to 16-bits for our asserts. */
|
||||||
|
sub $8, %rsp
|
||||||
|
|
||||||
|
/* The stack went down by 16: 8 from PUSHF, 8 from our SUB. */
|
||||||
|
sub %rsp, %r12
|
||||||
|
LKMC_ASSERT_EQ(%r12, $16)
|
||||||
|
|
||||||
|
/* Check that bit 0 (Carry Flag) of R13 is clear. */
|
||||||
|
bt $0, %r13
|
||||||
|
LKMC_ASSERT(jnc)
|
||||||
|
|
||||||
|
/* Restore the stack. */
|
||||||
|
add $16, %rsp
|
||||||
|
|
||||||
|
/* Now let's set carry flag instead. */
|
||||||
|
stc
|
||||||
|
pushf
|
||||||
|
mov (%rsp), %r13
|
||||||
|
sub $8, %rsp
|
||||||
|
bt $0, %r13
|
||||||
|
/* Assert that it was pushed to stack set. */
|
||||||
|
LKMC_ASSERT(jc)
|
||||||
|
add $16, %rsp
|
||||||
|
|
||||||
|
/* POPF pops the stack into flags of course. */
|
||||||
|
clc
|
||||||
|
pushf
|
||||||
|
stc
|
||||||
|
popf
|
||||||
|
LKMC_ASSERT(jnc)
|
||||||
|
|
||||||
|
/* PUSHFQ has the same opcode as PUSHF in the Intel manual.
|
||||||
|
* which mentions that PUSHF can be requested with a prefix.
|
||||||
|
*
|
||||||
|
* GNU GAS 2.32 emits the same PUSHFQ code for both by default.
|
||||||
|
* according to objdump.
|
||||||
|
*/
|
||||||
|
clc
|
||||||
|
pushf
|
||||||
|
stc
|
||||||
|
popf
|
||||||
|
LKMC_ASSERT(jnc)
|
||||||
|
LKMC_EPILOGUE
|
||||||
Reference in New Issue
Block a user