如何解决当我在C中调用ASM函数时,参数不会出现在堆栈中
好,所以我有两个文件,一个是A C文件,另一个是A ASM文件。他们的代码是。
C文件
void print()
{
print_char('A');
}
ASM文件
print_char:
push ebp ; prolouge
mov ebp,esp
mov ah,0eh ; set code for printing
mov al,[esp+8] ; move char into al
int 10h ; call int 10h
mov esp,ebp ; epilouge
pop ebp
ret
它会打印出一个称为三栏的东西。美国能源部的任何人都知道为什么
解决方法
我有许多bootloader tips的Stackoverflow答案,包括设置段,堆栈以及不依赖寄存器来获取特定值等。我也有一个simple bootloader的Stackoverflow答案,可以从1.44MB的软盘映像加载内核(或第二阶段)。
如果Int 10h/ah=0eh
正在打印字符且未出错,则您试图生成的代码似乎打算在实模式下运行。您提到您使用的是Ubuntu,如果您使用的是cross compiler,则不要提及(我强烈建议您使用)。另一种可能性是您正在使用实验性ia16-elf-gcc编译器,该编译器可以生成16位实模式代码,该代码可以在从8086到80386+的各种处理器上运行。
我会假设在没有任何其他信息的情况下,您正在Ubuntu上使用本机gcc
编译器,而不是交叉编译器或ia16-gcc编译器。您应该注意,本机GCC可以使用-m16
选项生成在实模式下运行的代码,但是生成的代码只能在80386+或更高版本的处理器上运行。除非您竭尽全力,否则GCC生成的代码应放置在内存的前64KiB中,因此我建议 CS = DS = ES = SS = 0 来减少麻烦。主流GCC不了解实模式段:偏移寻址。
您实际显示的代码不足以知道遇到的所有问题。 print_char
对我来说很突出:
print_char:
push ebp ; prolouge
mov ebp,esp
mov ah,0eh ; set code for printing
mov al,[esp+8] ; move char into al
int 10h ; call int 10h
mov esp,ebp ; epilouge
pop ebp
ret
尤其是ret
会引起问题。由于GCC生成的代码具有32位操作数和32位地址覆盖,因此它期望调用函数将32位返回地址压入堆栈。在实模式下,汇编代码中的简单ret
将返回到堆栈上的16位地址,并且仅弹出2个字节而不是4个字节。这将在返回调用方时导致堆栈不平衡,并且导致不可预测的结果。要解决此问题,您需要在ret
上使用32位操作数替代,因此必须为o32 ret
。
如果要从汇编中调用 C 函数,则必须确保使用CALL
的版本将完整的32位地址压入堆栈而不是16位一位。否则,将导致对堆栈上传递的所有参数进行错误索引,并且GCC将执行ret
,这将期望从堆栈中弹出32位返回地址。如果您有一个名为kmain
的函数(例如内核入口点),那么您可能会考虑使用以下方式对CALL
进行编码:
call kmain
您真正需要的是:
call dword kmain
您可能需要查看的其他问题:
- 在调用C代码之前,确保段寄存器已正确设置
- 设置适当的堆栈指针
SS:SP
- 将DF标志设置为0(使用
CLD
) - 如果使用浮点x87浮点,请使用
finit
对其进行初始化 - 在从程序集中调用任何 C 之前,请确保堆栈在16个字节的边界上对齐。
- 确保将整个内核加载到内存中
- 在 C 代码使用任何未初始化或初始化为零的全局变量之前,您需要将BSS部分清零
- 确保生成的代码具有适当的VMA /原点。
- 确保未使用
-fno-pic
使用位置独立代码(PIC)构建 C 代码
- 确保使用
-m16
选项构建 C 代码,以便所生成的指令在80386+上以16位实模式运行时可以正常工作。 - 使用
-fno-asynchronous-unwind-tables
删除异步展开表,并使用-fno-exceptions
关闭异常
作为示例,我将使用引导加载程序在0x7e00加载内核。调用print_char
和简单的print_string
。
boot.asm :
STAGE2_ABS_ADDR equ 0x07e00
STAGE2_RUN_SEG equ 0x0000
STAGE2_RUN_OFS equ STAGE2_ABS_ADDR
; Run stage2 with segment of 0x0000 and offset of 0x7e00
STAGE2_LOAD_SEG equ STAGE2_ABS_ADDR>>4
; Segment to start reading Stage2 into
; right after bootloader
STAGE2_LBA_START equ 1 ; Logical Block Address(LBA) Stage2 starts on
; LBA 1 = sector after boot sector
STAGE2_LBA_END equ STAGE2_LBA_START + NUM_STAGE2_SECTORS
; Logical Block Address(LBA) Stage2 ends at
DISK_RETRIES equ 3 ; Number of times to retry on disk error
bits 16
ORG 0x7c00
; Include a BPB (1.44MB floppy with FAT12) to be more compatible with USB floppy media
%ifdef WITH_BPB
%include "bpb.inc"
%endif
boot_start:
xor ax,ax ; DS=SS=0 for stage2 loading
mov ds,ax
mov ss,ax ; Stack at 0x0000:0x7c00
mov sp,0x7c00
cld ; Set string instructions to use forward movement
; Read Stage2 1 sector at a time until stage2 is completely loaded
load_stage2:
mov [bootDevice],dl ; Save boot drive
mov di,STAGE2_LOAD_SEG ; DI = Current segment to read into
mov si,STAGE2_LBA_START ; SI = LBA that stage2 starts at
jmp .chk_for_last_lba ; Check to see if we are last sector in stage2
.read_sector_loop:
mov bp,DISK_RETRIES ; Set disk retry count
call lba_to_chs ; Convert current LBA to CHS
mov es,di ; Set ES to current segment number to read into
xor bx,bx ; Offset zero in segment
.retry:
mov ax,0x0201 ; Call function 0x02 of int 13h (read sectors)
; AL = 1 = Sectors to read
int 0x13 ; BIOS Disk interrupt call
jc .disk_error ; If CF set then disk error
.success:
add di,512>>4 ; Advance to next 512 byte segment (0x20*16=512)
inc si ; Next LBA
.chk_for_last_lba:
cmp si,STAGE2_LBA_END ; Have we reached the last stage2 sector?
jl .read_sector_loop ; If we haven't then read next sector
.stage2_loaded:
mov ax,STAGE2_RUN_SEG ; Set up the segments appropriate for Stage2 to run
mov ds,ax
mov es,ax
; FAR JMP to the Stage2 entry point at physical address 0x07e00
xor ax,ax ; ES=FS=GS=0 (DS zeroed earlier)
mov es,ax
mov fs,ax
mov gs,ax
; SS:SP is already at 0x0000:0x7c00,keep it that way
; DL still contains the boot drive number
; Far jump to second stage at 0x0000:0x7e00
jmp STAGE2_RUN_SEG:STAGE2_RUN_OFS
.disk_error:
xor ah,ah ; Int13h/AH=0 is drive reset
int 0x13
dec bp ; Decrease retry count
jge .retry ; If retry count not exceeded then try again
error_end:
; Unrecoverable error; print drive error; enter infinite loop
mov si,diskErrorMsg ; Display disk error message
call print_string
cli
.error_loop:
hlt
jmp .error_loop
; Function: print_string
; Display a string to the console on display page 0
;
; Inputs: SI = Offset of address to print
; Clobbers: AX,BX,SI
print_string:
mov ah,0x0e ; BIOS tty Print
xor bx,bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 0x10 ; print character
.getch:
lodsb ; Get character from string
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret
; Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder,Head,Sector).
;
; Resources: http://www.ctyme.com/intr/rb-0607.htm
; https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
; https://stackoverflow.com/q/45434899/3857942
; Sector = (LBA mod SPT) + 1
; Head = (LBA / SPT) mod HEADS
; Cylinder = (LBA / SPT) / HEADS
;
; Inputs: SI = LBA
; Outputs: DL = Boot Drive Number
; DH = Head
; CH = Cylinder (lower 8 bits of 10-bit cylinder)
; CL = Sector/Cylinder
; Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
; Sector in lower 6 bits of CL
;
; Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
push ax ; Preserve AX
mov ax,si ; Copy LBA to AX
xor dx,dx ; Upper 16-bit of 32-bit value set to 0 for DIV
div word [sectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT
mov cl,dl ; CL = S = LBA mod SPT
inc cl ; CL = S = (LBA mod SPT) + 1
xor dx,dx ; Upper 16-bit of 32-bit value set to 0 for DIV
div word [numHeads] ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
mov dh,dl ; DH = H = (LBA / SPT) mod HEADS
mov dl,[bootDevice] ; boot device,not necessary to set but convenient
mov ch,al ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
shl ah,6 ; Store upper 2 bits of 10-bit Cylinder into
or cl,ah ; upper 2 bits of Sector (CL)
pop ax ; Restore scratch registers
ret
; If not using a BPB (via bpb.inc) provide default Heads and SPT values
%ifndef WITH_BPB
numHeads: dw 2 ; 1.44MB Floppy has 2 heads & 18 sector per track
sectorsPerTrack: dw 18
%endif
bootDevice: db 0x00
diskErrorMsg: db "Unrecoverable disk error!",0
; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db 0
dw 0xaa55
; Beginning of stage2. This is at 0x7E00 and will allow your stage2 to be 32.5KiB
; before running into problems. DL will be set to the drive number originally
; passed to us by the BIOS.
NUM_STAGE2_SECTORS equ (stage2_end-stage2_start+511) / 512
; Number of 512 byte sectors stage2 uses.
stage2_start:
; Insert stage2 binary here. It is done this way since we
; can determine the size(and number of sectors) to load since
; Size = stage2_end-stage2_start
incbin "stage2.bin"
; End of stage2. Make sure this label is LAST in this file!
stage2_end:
; Fill out this file to produce a 1.44MB floppy image
TIMES 1024*1440-($-$$) db 0x00
kernel.c :
extern void print_char(const char inchar);
void print_string(const char *string)
{
while (*string)
print_char(*string++);
}
void kmain(unsigned int drive_num)
{
(void) drive_num; /* Quiet compiler warning / unused variable */
print_char('A'); /* Print A */
print_char(13); /* Print Carriage Return */
print_char(10); /* Print Line Feed */
print_string("Hello,world!\r\nThis is a test!\r\n");
return;
}
tty.asm :
bits 16
global print_char
print_char:
; Removed the prologue and epilogue code as it isn't needed
push bx ; BX is non volatile register we need to save it
mov ah,0eh ; set code for printing
mov al,[esp+6] ; move char into al
xor bx,bx ; Ensure page 0 (BH = 0),BL is color if in graphics mode
int 10h ; call int 10h
pop bx
o32 ret ; We need to do a long return because the return address
; the C code put on the stack was a 4 byte return address.
; Failure to get this right can corrupt the stack
entry.asm :
bits 16
extern kmain
extern __bss_start
extern __bss_sizel
global _start
; The linker script will place .text.entry before other sections.
section .text.entry
_start:
; DL - drive number we booted as
xor ax,ax ; DS=ES=SS=0 (CS was already set to 0)
mov es,ax
mov ds,ax
mov esp,0x7c00-16 ; SS:SP is 0x0000:0x7c00 below the bootloader
; Create stack space to pass drive number as parameter and
; ensure ESP is still 16 byte aligned before calling kmain
finit ; Initialize x87 FPU
cld ; Set Direction Flag (DF) is cleared (forward movement)
sti ; Enable interrupts
; Zero out the BSS memory area a DWORD at a time
; since the memory isn't guaranteed to already be zero
xor eax,eax
mov ecx,__bss_sizel
mov edi,__bss_start
rep stosd
movzx edx,dl ; Zero extend drive number to 32-bit value
mov [esp],edx ; Pass drive number as first parameter to kmain
call dword kmain ; We need to call C functions with `dword` so a 32-bit
; return address is on the stack
.hltloop: ; Infinite loop to end the kernel
hlt
jmp .hltloop
link.ld :
OUTPUT_FORMAT(elf32-i386)
SECTIONS {
. = 0x7e00;
.text : SUBALIGN(4)
{
*(.text.entry) /* Ensure .text.entry appears first */
*(.text*)
*(.rodata*)
*(.data)
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all BSS sections from all files */
}
. = ALIGN(4);
__bss_end = .;
__bss_sizeb = __bss_end - __bss_start; /* BSS size in bytes */
__bss_sizel = (__bss_end - __bss_start) / 4; /* BSS size in longs/DWORDs */
/DISCARD/ : { /* Remove Unneeded sections */
*(.eh_frame);
*(.comment);
}
__end = .;
}
为了进行编译/组装/链接,您可以使用以下命令:
# Build kernel assembly files
nasm -f elf32 entry.asm -o entry.o
nasm -f elf32 tty.asm -o tty.o
# Compile the C files
gcc -c -Wall -Wextra -m16 -O3 -ffreestanding -fno-exceptions \
-fno-asynchronous-unwind-tables -fno-pic kernel.c -o kernel.o
# Link the files to an 32-bit ELF executable using a linker script
ld -Tlink.ld -melf_i386 -nostartfiles -nostdlib \
tty.o entry.o kernel.o -o kernel.elf
# Convert the ELF executable to a binary file that can be loaded by the bootloader
objcopy -O binary kernel.elf stage2.bin
# Generate the bootloader/disk image
nasm -f bin boot.asm -o disk.img
您可以使用QEMU运行它,
qemu-system-i386 -fda disk.img
您可以使用BOCHS来运行它:
bochs -qf /dev/null 'boot:floppy' \
'floppya: type=1_44,1_44="disk.img",status=inserted,write_protected=0'
我建议使用BOCHS调试实模式代码。运行时,它应该输出类似于以下内容的
:版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。