当我在C中调用ASM函数时,参数不会出现在堆栈中

如何解决当我在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调试实模式代码。运行时,它应该输出类似于以下内容的

enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-