Gcc C代码中堆栈上的三重错误

Gcc C代码中堆栈上的三重错误,gcc,assembly,x86,stack,osdev,Gcc,Assembly,X86,Stack,Osdev,我正在编写一个用于学习的玩具内核,但我遇到了一些麻烦。我制作了一个简单的引导加载程序,从软盘加载一个段(用32位代码编写),然后引导加载程序启用A20门并打开保护模式。如果我用汇编语言编写32位代码,我可以很好地跳转到32位代码,但是如果我用C语言编写,我会得到三重错误。当我反汇编C代码时,我可以看到前两条指令涉及到设置一个新的堆栈框架。这是正在运行的ASM代码和失败的C代码之间的关键区别。 ASM代码使用NASM v2.10.05,C代码使用DJGPP4.72集合中的GCC 这是引导加载程序代

我正在编写一个用于学习的玩具内核,但我遇到了一些麻烦。我制作了一个简单的引导加载程序,从软盘加载一个段(用32位代码编写),然后引导加载程序启用A20门并打开保护模式。如果我用汇编语言编写32位代码,我可以很好地跳转到32位代码,但是如果我用C语言编写,我会得到三重错误。当我反汇编C代码时,我可以看到前两条指令涉及到设置一个新的堆栈框架。这是正在运行的ASM代码和失败的C代码之间的关键区别。 ASM代码使用NASM v2.10.05,C代码使用DJGPP4.72集合中的GCC

这是引导加载程序代码:

org 7c00h
BITS 16

entry:
mov [drive], dl         ;Save the current drive


cli
mov ax,cs               ; Setup segment registers
mov ds,ax               ; Make DS correct
mov ss,ax               ; Make SS correct        

mov bp,0fffeh
mov sp,0fffeh           ;Setup a temporary stack
sti

;Set video mode to text
;===================
mov ah, 0
mov al, 3
int 10h
;===================

;Set current page to 0
;==================
mov ah, 5
mov al, 0
int 10h
;==================

;Load the sector
;=============
call load_image
;=============

;Clear interrupts
;=============
cli
;=============

;Disable NMIs
;============
in ax, 70h
and ax, 80h ;Set the high bit to 1
out 70h, ax
;============

;Enable A20:
;===========
mov ax, 02401h
int 15h
;===========

;Load the GDT
;===========
lgdt [gdt_pointer]
;===========

;Clear interrupts
;=============
cli
;=============

;Enter protected mode
;==================
mov eax, cr0
or eax, 1       ;Set the low bit to 1
mov cr0, eax
;==================

jmp 08h:clear_pipe  ;Far jump to clear the instruction queue


;======================================================
load_image:
reset_drive:
    mov ah, 00h
    ; DL contains *this* drive, given to us by the BIOS
    int 13h
    jc reset_drive

read_sectors:
    mov ah, 02h
    mov al, 01h
    mov ch, 00h
    mov cl, 02h
    mov dh, 00h
    ; DL contains *this* drive, given to us by the BIOS

    mov bx, 7E0h
    mov es, bx
    mov bx, 0

    int 13h
    jc read_sectors

ret
;======================================================

BITS 32 ;Protected mode now!
clear_pipe:

mov ax, 10h             ; Save data segment identifier
mov ds, ax              ; Move a valid data segment into the data segment register
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax              ; Move a valid data segment into the stack segment register
mov esp, 90000h        ; Move the stack pointer to 90000h
mov ebp, esp

jmp 08h:7E00h           ;Jump to the kernel proper

;===============================================
;========== GLOBAL DESCRIPTOR TABLE ==========
;===============================================

gdt:                    ; Address for the GDT

gdt_null:               ; Null Segment
    dd 0
    dd 0

gdt_code:               ; Code segment, read/execute, nonconforming
    dw 0FFFFh       ;   LIMIT, low 16 bits
    dw 0            ;   BASE, low 16 bits           
    db 0            ;   BASE, middle 8 bits
    db 10011010b    ;   ACCESS byte
    db 11001111b    ;   GRANULARITY byte
    db 0            ;   BASE, low 8 bits

gdt_data:               ; Data segment, read/write, expand down
    dw 0FFFFh
    dw 0
    db 0
    db 10010010b
    db 11001111b
    db 0

gdt_end:                ; Used to calculate the size of the GDT



gdt_pointer:                       ; The GDT descriptor
    dw gdt_end - gdt - 1    ; Limit (size)
    dd gdt                  ; Address of the GDT
;===============================================
;===============================================

drive: db 00    ;A byte to store the current drive in
times 510-($-$$) db 00
db 055h
db 0AAh
这是核心代码:

void main()
{
    asm("mov byte ptr [0x8000], 'T'");
    asm("mov byte ptr [0x8001], 'e'");
    asm("mov byte ptr [0x8002], 's'");
    asm("mov byte ptr [0x8003], 't'");
}
内核只是将这四个字节插入内存,我可以在VMPlayer虚拟机中运行代码时检查内存。如果出现字节,则我知道代码正在工作。如果我在ASM中编写的代码如下所示,则程序可以工作:

org 7E00h
BITS 32

main:
mov byte [8000h], 'T'
mov byte [8001h], 'e'
mov byte [8002h], 's'
mov byte [8003h], 't'

hang:
    jmp hang
因此,唯一的区别是我在反汇编C代码中发现的两个堆栈操作,它们是:

push ebp
mov ebp, esp

在这件事上的任何帮助都将不胜感激。我想我遗漏了一些相对次要但至关重要的东西,在这里,因为我知道这类事情是可以做到的。

我会尝试将受保护模式堆栈的地址移到另一个位置,比如0x80000,它(根据这个:)是保证免费使用的RAM(与您当前使用的地址相反-0x90000-显然可能不存在-这取决于您使用的测试环境)。

尝试使用以下技术:


从对象文件生成.text节的平面二进制文件。

您确定要跳转到内核吗?看起来您要跳转到0x08:0x7E00,C不一定要将主函数放在编译器抛出的ELF或PE文件的顶部(事实上,它从来没有这样做过)。您需要向我们展示如何编译您的C代码。很抱歉,我忘了提到这一点。我正在使用以下命令:
nasm-f bin bootloader.asm-o bootloader.bin
gcc-nostinc-s-masm=intel-Wall-C kernel.C-o kernel.o
剥离所有内核。o
然后我这样做是为了检查反汇编的代码:
objdump--反汇编--反汇编程序选项intel kernel.o
我正在使用我在stackoverflow上找到的strip函数,在研究如何使用GCC时,我发现它将二进制文件从约600字节减半(对于一个扇区来说太大)到~340左右。如果您尝试访问asm代码中的堆栈,是否会遇到同样的问题?您确定在堆栈地址90000h处有RAM可供使用吗?但反汇编代码与其他代码之间的唯一区别是函数prolog?您是否尝试过将其添加到asm文件中,并查看这三个错误是否存在?抱歉-刚刚看到您的迟到st comment-显然,如果您可以使用非C代码使堆栈正常工作,则情况并非如此。返回到我的原始代码,仅将堆栈指针更改为0x7FFF(即不进行其他更改)仍然存在三重错误。编辑:看起来我们在那里交叉了帖子,没关系。。。