- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在保护模式下调用内存地址低于当前函数的函数时,我遇到异常。异常会根据代码配置而有所不同,有时是一般保护故障,有时是无效的操作码等。
这里是一个程序的源代码,该程序在硬件上产生一般保护错误,在 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'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 是一个兼容 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/
我被告知“汇编”是您在文件中编写的内容,让您的“汇编程序”将其转换为二进制代码。 但我看到这两个术语在各种作品中混合搭配。我什至听说你编写了“汇编器”,然后“汇编器”使其可执行。 正确的用词是什么?
我在正确终止用 Assembly 编写的 16 位 DOS 程序时遇到问题。这是部分代码: .386P .model flat stack_s segment stack 'stack'
我需要多少档才能正确执行以下指令。我对我所做的事情有些困惑,所以我在这里看到专家的答案。 lw $1,0($2); beq $1,$2,Label; 请注意,检查是否会发生分支将在解码阶段完成。但是在
我正在尝试在汇编中进行简单的乘法运算,但是由于某些原因,当标记了MUL函数时,我看不到寄存器会发生变化。 mov bx, 5 mov cx, 10 mul cx 最佳答案 这些称为指令,它们指定
我正在尝试在 Assembly 中实现递归斐波那契程序。但是,我的程序崩溃了,出现了未处理的异常,我似乎无法找出问题所在。我不怀疑这涉及我对堆栈的不当使用,但我似乎无法指出哪里...... .386
我编写了以下代码: .386 .model small .stack 100h .data text db "Paper",0 .code start : lea dx ,
我有一个用汇编语言编写的裸机 ARM 的启动代码,我正在尝试了解它是如何工作的。该二进制文件被写入一些外部闪存中,并在启动时将其自身的一部分复制到 RAM 中。尽管我读过这篇文章wikipedia e
我在数据部分定义了一个二维数组和两个一维数组(一个用于列总和,一个用于行总和),并且我编写了一个函数,将二维数组求和到一维数组中。我使用 eax 和 ebx 作为二维数组的索引,但是当 eax 或 e
我正在开始组装,我正在使用 nasm 来组装代码,我正在尝试处理驻留在内存中的字符串并更改它,我想检查一个字节是否在某个范围内(ascii),这样我就可以决定如何处理它,我似乎不知道如何检查一个值是否
虽然您通常不希望将一个整体程序集用于小型项目以外的任何事情,但可能会将事物分离得太多。 组装分离过多的迹象/气味是什么? 最佳答案 第一个(明显的)是:在一个有很多项目的解决方案中,其中只有少数(比如
我正在尝试编写斐波那契的汇编代码版本,它给出第 n 个斐波那契数并返回它。 出于某种原因,它在存储斐波那契数的返回值和添加它们时遇到问题。 我希望它打印第 n 个斐波那契数。 我对我的代码做了一些修改
我有一个最小的、可重现的示例有两个问题,该示例具有三个针对 .NET Core 3.1 的项目。但我也想以 .NET Standard 2.0 为目标。 该示例适用于需要在运行时加载程序集并使用提供的
: 运算符在汇编中做什么?代码如下:DS:DX我还没有找到该运算符(operator)的任何文档。(我正在使用 NASM) 最佳答案 那实际上只是一个寄存器分隔符,而不是运算符。这意味着使用 DX 寄
我在哪里可以找到为 gmp-5.0.0 编写的程序的汇编代码我正在使用 UBUNTU 和 G++ 编译器..编译代码的命令是“g++ test.cc -o outp -lgmp” 实际上我想知道在 1
我是组装新手,我有一个关于如何表示负数的问题 我有三个 DWORDS 变量,比如说: result DWORD 0 i DWORD 3 j DWORD 5 我想计算这个公式:result = i -
我想编写我的第一个汇编程序。我在论文上做了一些程序,但这是我第一次使用编译器。我正在使用 ideone .我的程序很简单, 翻译 A = 5 - A到 assembly NEG A ADD A, 5
程序集,masm 嘿,我写了宏来打印存储在 dane1 段中的 1 字节值。 我将值除以 16,然后将提醒推送到堆栈,直到值==0。然后我弹出提醒将它们转换为 ASCII 码,并打印它们。 有人可以看
我正在研究 nasm 的一个大学项目。唯一的问题是我无法生成 162 和 278 之间的偶数随机数。我尝试了很多算法,但似乎无法限制范围内的数字。 是否有一个小技巧或调整来获得所需的范围内的数字?目的
终于在无数次错误的漫长 session 之后,希望这是最后一个。 没有编译或运行时错误,只是一个逻辑错误。 编辑:(固定伪代码) 我的伪代码: first = 1; second = 1; thir
我知道在程序集r0中调用函数时,包含第一个参数,直到r3是第四个。我知道,当它超过四个时,将使用堆栈指针,但是我不太确定具体细节。 r0-r3仍然保持前四个,其余的进入堆栈吗?我正在看下面的程序集,试
我是一名优秀的程序员,十分优秀!