OS自作 カーネル→ユーザスタック切り替え後に期待したスタックにならない
背景
自作OSを作成しています。
投稿時点 : https://github.com/ooe1220/KansoOS/tree/0693566b02a75e84ce68ac64759b0ed4208471d8
最新 : https://github.com/ooe1220/KansoOS
※編集中のソースはまだあげていません。
問題
従来は以下の様になっていました。(ソースはGIT内)
user_exec.c(カーネル) : call start
start.S(ユーザプログラム入口) : jmp main
test3.c : ret(return 0)でuser_exec.cへ返る
そこで以下のように書き換えました。
user_exec.S(カーネル) : espをユーザ用へ書き換え。jmp start ※EXITシステムコールからラベル指定でuser_execに戻る為、CからSへ書き換えました
start.S(ユーザプログラム入口) : jmp main
test3.c : ret(return 0)でstart.Sへ返る
start.S : int0x80でexitシステムコールを呼び出す(今回はここまで到達していないため割愛)
従来は正常に動いていたのですが、今回ユーザスタック切り替え、カーネル(user_exec.S)→ユーザ(test3.c)をjmpに変更したところ、
プログラムがtest3.c内で止まってしまうようになりました。
test3.cスタックを可視化したところ、全然期待スタックになっていませんでした。
もう何日もはまっており、どこでスタックが壊れたのか分かりません。
詳しい方がおられたら教えていただけませんでしょうか。
ソース
.intel_syntax noprefix
.global user_exec
.global user_exec_ret
/* kernelスタック退避用 */
.section .bss
.global kernel_stack_saved
kernel_stack_saved:
.space 4 /* resd 1 相当 */
.section .text
/* void* entry, int argc, char** argv */
user_exec:
/* ESPに渡された引数を取得 (cdecl, 32bit) */
/* [esp] = return address */
/* [esp+4] = entry */
/* [esp+8] = argc */
/* [esp+12]= argv */
mov eax, DWORD PTR [esp+4] /* entry */
mov esi, DWORD PTR [esp+8] /* argc */
mov edi, DWORD PTR [esp+12] /* argv */
/* カーネルスタック退避 */
mov ebx, esp
mov DWORD PTR [kernel_stack_saved], ebx
/* ユーザースタックに切替 */
#mov esp, 0x10000
mov esp, 0x30000
/* ユーザスタックに argc/argv を push */
push edi /* argv */
push esi /* argc */
/* ユーザプログラムへ飛ぶ */
jmp eax
/* exit から戻るラベル */
user_exec_ret:
mov esp, DWORD PTR [kernel_stack_saved] /* カーネルスタック復元 */
ret
.intel_syntax noprefix
.global _start
.extern main
_start:
call main # Cのmainを呼ぶ
# exit syscall
mov eax, 0 # syscall番号 0 = exit
int 0x80
#include "lib/mystdio.h"
int main(int argc, char **argv){
//asm volatile("hlt");
volatile unsigned short *vram = (unsigned short*)0xB8000;
unsigned char hex[] = "0123456789ABCDEF";
uint32_t *bp;
asm volatile("mov %%ebp, %0" : "=r"(bp)); // EBPを取得
int x = 0, y = 0;
// --- EBPの値を1行目に表示 ---
// --- EBPの値を1行目に表示 ---
char *label = "EBP=";
for(int i = 0; label[i]; i++)
vram[y*80 + x + i] = (0x0F << 8) | label[i];
x += 4; // ラベル分だけ右にずらす
uint32_t ebp_val = (uint32_t)bp;
for(int j = 0; j < 8; j++){
int d = (ebp_val >> (28 - j*4)) & 0xF;
vram[y*80 + x + j] = (0x0F << 8) | hex[d];
}
y++; // 次の行
x = 0; // 行先頭に戻す
// --- スタックダンプ(高アドレス順) ---
// 引数(3)からローカル変数(-2)まで逆順に表示
for(int i = 3; i >= -2; i--){
uint32_t addr = (uint32_t)&bp[i]; // アドレス
uint32_t val = bp[i]; // 値
// アドレスを8桁16進で表示
for(int j = 0; j < 8; j++){
int d = (addr >> (28 - j*4)) & 0xF;
vram[y*80 + x + j] = (0x0F << 8) | hex[d];
}
vram[y*80 + x + 8] = (0x0F << 8) | ':'; // 区切り
// 値を8桁16進で表示
for(int j = 0; j < 8; j++){
int d = (val >> (28 - j*4)) & 0xF;
vram[y*80 + x + 9 + j] = (0x0F << 8) | hex[d];
}
y++; // 次の行へ
}
printf_d("test3 argc = %d\n", argc);
int i=0;
for(;i<argc;i++){
write(argv[i]);
}
return 0;
}
検証
test3.cの中でhlt実行し、qemuでスタックの中身を表示。
何も入っておらず、EBP及びESPがかけ離れているのも気になります。
(qemu) x/4xw $esp
0002ff2c: 0x00000000 0x00000000 0x00000000 0x00000000
(qemu) info registers
EAX=00010000 EBX=0009fa8c ECX=00000001 EDX=00000021
ESI=00000001 EDI=0009facc EBP=0009fb28 ESP=0002ff2c
Cでスタックを表示してみる
EBP=9fb28
ここで
返り値はEBP+4を見てみると0x8369
その付近を見てみる。
(qemu) x/10i 0x8364
0x00008364: call 0x8550
0x00008369: add $0x10,%esp <<<返りアドレス
0x0000836c: jmp 0x82ec
0x00008371: lea 0x0(%esi),%esi
0x00008378: call 0x9450
0x0000837d: cmp $0xa,%al
0x0000837f: je 0x82d7
0x00008385: cmp $0x8,%al
0x00008387: jne 0x8307
0x0000838d: call 0x9450
でも実際はstartからtest3.cへ跳んでいるので返りアドレス付近は以下の様になっているはずです。。。
call main
mov eax, 0
int 0x80
期待したスタック
EBP+12 **argv
EBP+8 argc
EBP+4 返りアドレス(start.S)
test3.cの機械語
test@test-fujitsu:~/kaihatsu/KansoOS/build$ objdump -d test3.elf
00010000 <_start>:
10000: e8 0b 00 00 00 call 10010 <main>
10005: b8 00 00 00 00 mov $0x0,%eax
1000a: cd 80 int $0x80
1000c: 66 90 xchg %ax,%ax
1000e: 66 90 xchg %ax,%ax
00010010 <main>:
10010: 55 push %ebp
10011: 57 push %edi
10012: 56 push %esi
10013: 53 push %ebx
10014: 81 ec b8 00 00 00 sub $0xb8,%esp
...(省略)...
1026b: 5f pop %edi
1026c: 5d pop %ebp
1026d: c3 ret
mainの中で以下が生成されていないのが気になります
push ebp
mov ebp.esp
