gpt4 book ai didi

assembly - MASM 在保护模式下生成错误的调用目标

转载 作者:行者123 更新时间:2023-12-03 06:33:50 26 4
gpt4 key购买 nike

在保护模式下调用内存地址低于当前函数的函数时,我遇到异常。异常会根据代码配置而有所不同,有时是一般保护故障,有时是无效的操作码等。

这里是一个程序的源代码,该程序在硬件上产生一般保护错误,在 DOSBox 中产生双重错误。相关代码位于段seg32中。当func1尝试回调func2

时,出现错误
single segment stack                                                      
assume cs:single,ds:single,ss:single

gdesc struc ;global descriptor structure definition
limit_lo dw 0ffffh ;low word of 20-bit limit (bits 15:0)
base_lo dw ? ;low word of base address (bits 15:0)
base_mid db ? ;middle byte of base address (bits 23:16)
priv db ? ;privilege and type bits
limit_hi db ? ;granularity, operand size, hi nybble of limit (bits 19:16)
base_hi db ? ;high byte of base address (bits 31:24)
gdesc ends

idesc struc ;interrupt descriptor structure definition
offset_lo dw ? ;low word of offset
selector dw ? ;selector in gdt
unused db 0 ;always zero
type_attr db ? ;type attribute bits
offset_hi dw ? ;high word of offset
idesc ends
;global descriptor table, phys addresses calculated by init code
nulld gdesc <0,0,0,0,0,0> ;null descriptor
pcode gdesc <,,,09eh,0cfh,> ;protected mode code descriptor
pdata gdesc <,,,092h,0cfh,> ;protected mode data descriptor
rcode gdesc <,,,09ah,08fh,> ;real mode code descriptor
rdata gdesc <,,,092h,08fh,> ;real mode data descriptor
vbuff gdesc <,0,0ah,092h,0cfh,> ;vga pixel buffer data descriptor
tbuff gdesc <,8000h,0bh,092h,0cfh,> ;text buffer data descriptor

gdt_limit dw offset gdt_limit-offset nulld-1 ;gdt_limit <- gdt size in bytes-1
gdt_addr dd offset nulld ;gdt_addr <- offset of gdt, physical address calculated at runtime

idt_div idesc <div_err-offset_0,8,0,0eeh,0> ;interrupt descriptor table, div error
idesc <dont_care-offset_0,8,0,0eeh,0> ;debugger call
idesc <nmi-offset_0,8,0,0eeh,0> ;nmi interrupt
idesc <dont_care-offset_0,8,0,0eeh,0> ;breakpoint
idesc <dont_care-offset_0,8,0,0eeh,0> ;into overflow
idesc <dont_care-offset_0,8,0,0eeh,0> ;bound overflow
idesc <invalid_op-offset_0,8,0,0eeh,0> ;invalid opcode
idesc <fpu_err-offset_0,8,0,0eeh,0> ;coprocessor unavailable
idesc <double_fault-offset_0,8,0,0eeh,0> ;double fault
idesc <fpu_err-offset_0,8,0,0eeh,0> ;coprocessor overrun
idesc <dont_care-offset_0,8,0,0eeh,0> ;invalid tss
idesc <not_present-offset_0,8,0,0eeh,0> ;segment not present
idesc <dont_care-offset_0,8,0,0eeh,0> ;stack exception
idesc <gp_fault-offset_0,8,0,0eeh,0> ;general protection fault
idesc <dont_care-offset_0,8,0,0eeh,0> ;reserved
idesc <fpu_err-offset_0,8,0,0eeh,0> ;coprocessor error
idesc 16 dup (<dont_care-offset_0,8,0,0eeh,0>) ;16 reserved
idt_pit idesc <pit_isr-offset_0,8,0,0eeh,0> ;timer isr
idt_kbd idesc <kbd_isr-offset_0,8,0,0eeh,0> ;keyboard isr

idt_limit dw offset idt_limit-offset idt_div-1 ;idt_limit <- idt size in bytes-1
idt_addr dd offset idt_div ;idt_addr <- offset of idt, complete physical address
;calculated at runtime

ridt_limit dw 3ffh ;real mode idt limit
ridt_addr dd 0 ;real mode idt address

m_pic_mask db ? ;original master pic mask
s_pic_mask db ? ;original slave pic mask

start:

mov ax, cs
mov ds, ax ;ds = cs, single segment

cli ;disable maskable interrupts
in al, 70h ;al <- cmos ram index register port
or al, 80h ;set bit 7 to disable nmi
out 70h, al ;non-maskable interrupts disabled

;check for 386+
;enable a20
;reinit PICs

mov al, 11h ;ICW1, IC4 bit set, cascade bit clr, edge trig, init bit set
out 20h, al ;send ICW1 to primary pic cmd register
jmp $+2
jmp $+2 ;delay needed on older systems
out 0a0h, al ;send ICW1 to slave pic cmd register
jmp $+2
jmp $+2

mov al, 20h ;ICW2 base address for primary pic = 20h
out 21h, al ;send ICW2 to primary pic data register
jmp $+2
jmp $+2
mov al, 28h ;ICW2 base address for slave pic = 28h
out 0a1h, al ;send ICW2 to slave pic data register
jmp $+2
jmp $+2

mov al, 4 ;ICW3, on primary pic, bits map to irq lines, use irq 2 for cascade
out 21h, al ;send ICW3 to primary pic data register
jmp $+2
jmp $+2
mov al, 2 ;ICW3, on slave pic, byte value = irq line, use irq 2 for cascade
out 0a1h, al ;send ICW3 to slave pic data register
jmp $+2
jmp $+2

mov al, 1 ;ICW4 set bit 1 to enable 80x86 mode
out 21h, al ;send ICW4 to primary pic data register
jmp $+2
jmp $+2
out 0a1h, al ;send ICW4 to slave pic data register
jmp $+2
jmp $+2

xor al, al ;clear the data registers
out 21h, al
jmp $+2
jmp $+2
out 0a1h, al
jmp $+2
jmp $+2

in al, 21h ;only need keyboard and timer irq enabled for now
mov m_pic_mask, al ;store original master pic mask register, restore before exit
or al, 0fch ;mask out all but irq 0 and 1
out 21h, al ;master pic mask updated
jmp $+2
jmp $+2

in al, 0a1h
mov s_pic_mask, al ;store original slave pic mask register, restore before exit
or al, 0ffh ;mask out every slave irq
out 0a1h, al
jmp $+2
jmp $+2

.386p ;calc and insert phys address into gdt entries
xor eax, eax ;clear high word of eax
mov ax, cs ;eax <- code segment address
shl eax, 4 ;multiply segment address by 16 to convert it to physical address
add gdt_addr, eax ;gdt_addr is defined with offset of gdt, gdt_addr + cs*16 = physical addres of gdt
add idt_addr, eax ;idt_addr is defined with offset of idt, idt_addr + cs*16 = physical addres of idt
lidt idt_limit ;load idtr
lgdt gdt_limit ;load gdtr

mov rcode.base_lo, ax
mov rdata.base_lo, ax ;store low word of cs phys address to real mode descriptors
shr eax, 16 ;shift eax to access high word
mov rcode.base_mid, al
mov rdata.base_mid, al ;store middle byte of cs phys address to real mode descriptors
mov rcode.base_hi, al
mov rdata.base_hi, al ;store high byte of cs phys address to real mode descriptors

xor eax, eax ;clear high word of eax
mov ax, seg seg32 ;eax <- seg32 segment address (fixed up by dos at runtime)
shl eax, 4 ;multiply segment address by 16 to convert it to physical address
mov pcode.base_lo, ax
mov pdata.base_lo, ax ;store low word of seg32 phys address to protected mode descriptors
shr eax, 16 ;shift eax to access high word
mov pcode.base_mid, al
mov pdata.base_mid, al ;store middle byte of seg32 phys address to protected mode descriptors
mov pcode.base_hi, al
mov pdata.base_hi, al ;store high byte of seg32 phys address to protected mode descriptors

mov eax, cr0 ;load control register 0
or al, 1 ;set pe bit
mov cr0, eax ;enable protected mode

;manually encoded jmp 8h:start32
db 66h ;specify 32-bit operand
db 0eah ;jmp opcode
dd offset start32 ;32 bit offset
dw 8 ;global descriptor selector (select protected mode code segment)

real_mode: ;transition back to real mode
.386p
mov eax, cr0 ;load control register into eax
and al, 0feh ;clear pe bit
mov cr0, eax ;real mode enabled
db 0eah ;jmp single:real_cs to load cs:ip
dw offset real_cs ;offset real_cs
dw seg single ;segment single (fixed up by dos at runtime)

real_cs: ;back in real mode
.8086
mov ax, cs
mov ds, ax ;ds = cs
mov ss, ax ;ss = cs

mov al, 11h ;ICW1, IC4 bit set, cascade bit clr, edge trig, init bit set
out 20h, al ;send ICW1 to primary pic cmd register
jmp $+2
jmp $+2 ;delay needed on older systems
out 0a0h, al ;send ICW1 to slave pic cmd register
jmp $+2
jmp $+2

mov al, 8 ;ICW2 base address for primary pic = 8
out 21h, al ;send ICW2 to primary pic data register
jmp $+2
jmp $+2
mov al, 70h ;ICW2 base address for slave pic = 70h
out 0a1h, al ;send ICW2 to slave pic data register
jmp $+2
jmp $+2

mov al, 4 ;ICW3, on primary pic, bits map to irq lines, use irq 2 for cascade
out 21h, al ;send ICW3 to primary pic data register
jmp $+2
jmp $+2
mov al, 2 ;ICW3, on slave pic, byte value = irq line, use irq 2 for cascade
out 0a1h, al ;send ICW3 to slave pic data register
jmp $+2
jmp $+2

mov al, 1 ;ICW4 set bit 1 to enable 80x86 mode
out 21h, al ;send ICW4 to primary pic data register
jmp $+2
jmp $+2
out 0a1h, al ;send ICW4 to slave pic data register
jmp $+2
jmp $+2

xor al, al ;clear the data registers
out 21h, al
jmp $+2
jmp $+2
out 0a1h, al
jmp $+2
jmp $+2

mov al, m_pic_mask ;al <- master pic mask
out 21h, al ;master pic mask restored
jmp $+2
jmp $+2

mov al, s_pic_mask ;al <- slave pic mask
out 0a1h, al ;slave pic mask restored
jmp $+2
jmp $+2

.386p
lidt ridt_limit ;setup idtr for real mode
.8086
mov ax, 40h
mov es, ax ;access kbd data area via segment 40h
mov word ptr es:[1ah], 1eh ;set the kbd buff head to start of buff
mov word ptr es:[1ch], 1eh ;kbd buff tail = head to clear kbd buffer
in al, 70h ;al <- cmos ram index register port
and al, 7fh ;clear bit 7 to enable nmi
out 70h, al ;nmi enabled
sti ;interrupts enabled
mov ax, 4c00h ;Terminate process function selected
int 21h ;return to dos

align 2 ;align stack for 16-bit accesses
s16 db 256 dup (0ffh) ;256 byte stack, need at least 256 bytes to change video
single ends ;modes (int 10h) with some vga bios



.386p
seg32 segment use32
assume cs:seg32,ds:seg32,ss:seg32
offset_0: ;used to generate 16-bit offsets in idt descriptor definitions

db "start" ;used to find start of segment in debug

div_err: ;division error isr
xor edi, edi
mov byte ptr es:[edi], '0'
hlt
iretd

dont_care: ;rare/obscure faults and exceptions
xor edi, edi
mov byte ptr es:[edi], '1'
hlt
iretd

nmi: ;non maskable interrupt isr
xor edi, edi
mov byte ptr es:[edi], '2'
hlt
iretd

invalid_op: ;invalid opcode isr
xor edi, edi
mov byte ptr es:[edi], '3'
hlt
iretd

double_fault: ;double fault isr
xor edi, edi
mov byte ptr es:[edi], '4'
hlt
iretd

fpu_err: ;fpu error isr
xor edi, edi
mov byte ptr es:[edi], '5'
hlt
iretd

not_present: ;descriptor not present isr
xor edi, edi
mov byte ptr es:[edi], '6'
hlt
iretd

gp_fault: ;general protection fault isr
xor edi, edi
mov byte ptr es:[edi], '7'
hlt
iretd

pit_isr: ;int 20h timer isr
push eax
mov al, 20h
out 20h, al
pop eax
iretd

kbd_isr: ;int 21h keyboard isr
push eax
in al, 60h
mov al, 20h
out 20h, al
pop eax
iretd

sp16 dw ? ;16-bit stack pointer

start32:

mov ax, 10h
mov ds, ax ;ds <- protected mode data descriptor (same physical address as code descriptor)
mov fs, ax
mov gs, ax ;setup extra segments
mov ss, ax ;setup stack segment
mov sp16, sp ;store old stack pointer, restore before returning to real mode
mov esp, offset s32_end ;setup 32-bit stack pointer
mov ax, 30h
mov es, ax ;es <- vga compatible text buffer
sti ;ready for interrupts, leave nmi disabled

call func1


exit_pm: ;return to real mode
cli ;interrupts disabled
mov sp, sp16 ;restore 16-bit stack pointer
mov ax, 20h
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax ;load real mode data descriptor selectors
db 0eah ;jmp 18h:ret_real to load real mode code descriptor
dd offset real_mode ;offset to 16-bit code in single segment
dw 18h ;real mode code selector


db "call_here" ;use this to find call target in debug
func2 proc

push eax
push ebx
push ecx
push edx
push esi

pop esi
pop edx
pop ecx
pop ebx
pop eax
ret

func2 endp


func1 proc

push eax
push ebx
push ecx
push edx
push esi

;do arbitrary work
mov eax, 934875h
xor eax, ebx
inc ecx
mul edx
add edx, 94357h
jmp target1
xor ecx, ecx
add edx, 987h
dec esi

target1:
call func2 ;IT NEVER MAKES IT TO FUNC2
jmp over_marker
db "calladdress" ;use this to find call instruction in debug
over_marker:

pop esi
pop edx
pop ecx
pop ebx
pop eax
ret

func1 endp


align 4 ;align stack for 32-bit accesses
s32 db 256 dup (0ffh) ;256 byte stack
s32_end: ;used to initialize esp
seg32 ends
end start

我认为问题在于 MASM 生成了错误的调用目标,而我正在执行垃圾。

我通过使用调试加载程序来测试这一点(只是为了检查操作码。)调试将调用指令加载到 06CA:05A9 和调用目标(push eax ) 到 06CA:057B。调用指令编码为E8 CD FF 00 00,即call loc_0000ffd2

如果是 16 位段,0x5a9 加上 0xffd2 将转入 0x57b。或者偏移量可能是有符号的并且是负数?我是否使用了错误的调用类型?

最佳答案

问题是 MASM 5.10 链接器有缺陷,无法正确处理这种 32 位重定位。正如您怀疑的那样,它将 32 位相对位移视为 16 位值,正如您所正确观察到的那样,它会产生错误的值(特别是在以负位移调用代码时)。为了测试您的代码,我一直使用 MASM 5.10a,链接器版本为 3.64。

您可以继续使用 MASM.EXE 5.10a,但需要替换链接器。 MASM 6.11 附带的 16 位 Microsoft Overlay Linker (LINK.EXE) 可以正常工作。您需要有一个扩展的内存管理器,LINK.EXE 和/或 MASM.EXE 才能正常运行。 MASM 6.11 是可以从 DOS 运行的 MASM 产品的最后一个版本。 MASM 6.11 安装盘可以从 here 下载.

<小时/>

Borland 的 TASM 和 TLINK 作为替代方案

如果您下载并安装Borland's Turbo Assembler v2.0x您可以使用 TASM 汇编代码并使用 TLINK 链接。如果您在 TASM 生成的目标文件上运行 TLINK,它实际上会警告您此问题!错误将类似于:

32-bit record encountered in module Use "/3" option

如果您使用 /3 选项,它将启用 32 位处理并应生成正确的可执行文件。

要使用 TASM 进行组装(它仍可与 MASM 一起使用),必须对这些行进行小幅调整:

lidt idt_limit                             ;load idtr 
lgdt gdt_limit ;load gdtr

...

lidt ridt_limit ;setup idtr for real mode

TASM 对类型很挑剔,它们必须写成:

lidt fword ptr idt_limit                   ;load idtr 
lgdt fword ptr gdt_limit ;load gdtr

...

lidt fword ptr ridt_limit ;setup idtr for real mode
<小时/>

JWasm 作为替代方案

JWasm 是一个兼容 MASM 的开源解决方案,基于 Watcom 的汇编器 (WASM),并具有更现代的更新。 JWAsm 还可以在 Windows、Linux、MacOS 等其他平台上构建和运行。JWasm 能够像 MASM 一样将文件组装为 DOS 对象文件 (OMF),但它还具有集成的 16 位链接器,允许您构建DOS MZ 直接可执行。您可以从 here 下载预构建的 DOS 版本的 JWASM .

JWasm 像 TASM 一样对类型很挑剔,因此请参阅有关 fword ptr 的 TASM 部分

要将单个源汇编文件汇编并链接到 DOS 可执行文件,您只需执行以下操作:

jwasmr -mz filename.asm

这应该生成一个名为filename.exe的文件

关于assembly - MASM 在保护模式下生成错误的调用目标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61955769/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com