test environment:
far jump and near jump.
gcc 產生的組合語言 call 指令是 far jump, 在 stack 會放入 4 byte 的返回指令,
near jump 則會在 stack 放入 2 byte 返回指令。這會造成在取 C 語言的參數時計算的 byte 數會相差 2 byte。
far jump
calll func_a
call func_a
near jump
callw func_a
我不知道原因, 這是我透過 DispAX 將 %esp 印出來觀察到的結果。之前一直 hang 在位址的計算上, 成功將 %esp 印出來, 總算找到問題。
可是很奇怪, 在 C 程式我加入了
asm(".code16gcc\n");
從 objdump -D 反組譯出來的 code 也是 callw 的指令, 可是卻要使用 4 byte 的返回指令長度來做加減, 不懂 ...
32 bit 版本只有移除
asm(".code16gcc\n");16 bit 版本多了 0x66 這個 byte。
這個測試很簡單, 程式從組合語言開始, 設定好堆疊 (%ss, %esp) 後, 呼叫 C 語言的 funcion kmain, 再由 kmain 呼叫組合語言 function write_mem8。
write_mem8 會以 0xb8000 + offset 將字元顯示到螢幕上。
ex:
write_mem8(0, 'A') // 顯示 A 在第 0 行第 0 列
write_mem8(2, 'B') // 顯示 B 在第 0 行第 1 列
感覺很容易, 但我遇到以下難以理解的問題。
write_mem8 會從 %esp 抓出傳入的參數, 但是一旦我使用 C 語言的參數慣例, 使用 %epb 來抓取傳入的參數:
pushl %ebp
mov %esp, %ebx
程式總是會在
pushl %ebp
這裡 hang 住。
透過 kmain call write_mem8 就會有這問題, 直接在組合語言裡頭 call write_mem8 就沒事。
在花了幾小時之後, 我終於找到問題了, 因為我沒 popl %ebp, 造成 ret 將 return address 取出時, 取到錯誤的 return address。
這是指到 c function 傳入的參數,
132 mov 8(%ebp), %ecx; # 1nd arg, address
133 movb 12(%ebp), %al; # 2nd arg, char
若是 near call (callw func), 則要改成
132 mov 6(%ebp), %ecx; # 1nd arg, address
133 movb 10(%ebp), %al; # 2nd arg, char
組合語言可以使用 callw func, calll func 來決定哪一種版本, 但是 C 語言只會產生 calll 版本, 雖然 c 語言是產生 call func, 但轉成 opcode 時, 是 calll func 的意思(far call)。
x86 保護模式 c runtime 版本:
http://descent-incoming.blogspot.com/2011/09/c-run-time-environment.htmlx86 保護模式 c runtime 版本提到的 static array 在 real mode 上沒什麼問題, 可直接使用, 畢竟保護模式的 gdt 和 real mode 定址方式並不一樣。
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。