I'm writing on a very basic kernel. I tried to write a function, with parameters passed through the stack. The kernel is compiled with nasm (like described in this question) and run with QEMU. I'm using gdb for debugging.
After a long while having problems I wrote this to test some basic stack operations:
BITS 16
global start
start:
mov ax, 0x7C00
add ax, 288
mov ss, ax
mov sp, 4096
mov ax, 0x7C00
mov ds, ax
test:
push 42
push 43
push "T"
pop ax
pop ax
push 44
pop ax
pop ax
jmp $
Going through this step by step and looking what sp
contains and looking what at the pointed address is reveals that sp
is de-/incremented right, but the address it's pointing to always contains 0x0000.
I thought this could be related to the mov sp, 4096
line. So I commented it out. This didn't work either. The only difference was that the values sp
points to are now some others but not the ones I pushed there.
Is there something I have to do to initialize the stack or something similar?
Explanation
- You want to look at
16*$ss + $esp
in GDB. (Like Jester suggested in his comment)
- This is explained in x86 Segmentation. Note the same applies to data memory access and the
DS
register.
- You set
SS
to 0x7C00 + 288
and SP
to 4096. Thus the physical stack pointer address is ((0x7c00+0x0120)<<4) + 0x1000
giving 0x7e200
.
- Writing all memory addresses and offsets in your code in hexadecimal may help with the arithmetic.
Scripting GDB to Examine the Stack
boot.asm
BITS 16
global start
start:
mov ax, 0x7C00
add ax, 0x0120
mov ss, ax
mov sp, 0x1000
mov ax, 0x7C00
mov ds, ax
test:
push 42
push 43
push 'T'
pop ax
pop ax
push 44
pop ax
pop ax
hlt
examine-stack.gdb
set confirm 0
set pagination 0
set architecture i8086
target remote localhost:1234
file boot
set disassemble-next-line 1
define hook-stop
printf "Stack Pointer: 0x%04x, AX: 0x%04x\n", ($ss*16 + $esp), $ax
# after stack setup, the linear stack pointer address is 0x7e200
set variable $sp_linear = 0x7e200
x/8xb ($sp_linear - 8)
end
break test
continue
set variable $i = 0
while $i < 8
stepi
set variable $i = $i + 1
end
monitor quit
disconnect
quit
x86-boot.ld
ENTRY(start);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
_text = .;
*(.text);
_text_end = .;
}
.data :
{
_data = .;
*(.bss);
*(.bss*);
*(.data);
*(.rodata*);
*(COMMON)
_data_end = .;
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
/DISCARD/ :
{
*(.note*);
*(.iplt*);
*(.igot*);
*(.rel*);
*(.comment);
/* add any unwanted sections spewed out by your version of gcc and flags here */
}
}
Build with:
nasm -g -f elf -F dwarf boot.asm -o boot.o
cc -nostdlib -m32 -T x86-boot.ld -Os -Wall -g3 -I. -Wl,--build-id=none boot.o -o boot
objcopy -O binary boot boot.good.bin
Sample Session
$ qemu-system-x86_64 -s -S boot.good.bin &
$ gdb -q -x examine-stack.gdb
The target architecture is assumed to be i8086
0x0000fff0 in ?? ()
Breakpoint 1 at 0x7c10: file boot.asm, line 13.
Stack Pointer: 0x7e200, AX: 0x7c00
0x7e1f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Breakpoint 1, test () at boot.asm:13
13 push 42
=> 0x00007c10 : 6a 2a push $0x2a
Stack Pointer: 0x7e1fe, AX: 0x7c00
0x7e1f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x2a 0x00
14 push 43
=> 0x00007c12 : 6a 2b push $0x2b
Stack Pointer: 0x7e1fc, AX: 0x7c00
0x7e1f8: 0x00 0x00 0x00 0x00 0x2b 0x00 0x2a 0x00
15 push 'T'
=> 0x00007c14 : 6a 54 push $0x54
Stack Pointer: 0x7e1fa, AX: 0x7c00
0x7e1f8: 0x00 0x00 0x54 0x00 0x2b 0x00 0x2a 0x00
16 pop ax
=> 0x00007c16 : 58 pop %ax
Stack Pointer: 0x7e1fc, AX: 0x0054
0x7e1f8: 0x00 0x00 0x54 0x00 0x2b 0x00 0x2a 0x00
17 pop ax
=> 0x00007c17 : 58 pop %ax
Stack Pointer: 0x7e1fe, AX: 0x002b
0x7e1f8: 0x00 0x00 0x54 0x00 0x2b 0x00 0x2a 0x00
18 push 44
=> 0x00007c18 : 6a 2c push $0x2c
Stack Pointer: 0x7e1fc, AX: 0x002b
0x7e1f8: 0x00 0x00 0x54 0x00 0x2c 0x00 0x2a 0x00
19 pop ax
=> 0x00007c1a : 58 pop %ax
Stack Pointer: 0x7e1fe, AX: 0x002c
0x7e1f8: 0x00 0x00 0x54 0x00 0x2c 0x00 0x2a 0x00
20 pop ax
=> 0x00007c1b : 58 pop %ax
Stack Pointer: 0x7e200, AX: 0x002a
0x7e1f8: 0x00 0x00 0x54 0x00 0x2c 0x00 0x2a 0x00
21 hlt
=> 0x00007c1c : f4 hlt