在MZ(DOS,16位).EXE头中,页面计数和最后一页的大小的重要性

6

我正在学习如何使用汇编语言(NASM)手动构建头文件并将文件组装成二进制文件以创建Dos .EXE文件。我在页面选项方面遇到了问题(包括总页数和最后一页的字节计数)。无论我将初始值设置得多小,程序都能正常工作。

作为一个极端案例,即使设置1个字节的1页,以下程序也能正常运行:

;
; the smallest possible "Hello, World!" .EXE (DOS MZ) file
; assemble with:
; nasm -f bin -w+all -O0 smallest_hello_exe.asm -o ASM.EXE
;

bits 16
cpu 8086

;
; by setting cs:ip=-10h:100h instead of 0h:0h inside the .EXE header
; (identical assignments), we achieve the following two advantages:
; 1) ds==cs, so no "push cs pop ds" is needed in order for ds:dx
; to point to the message string
; 2) we can exit by int 20h instead of int 21h, thus omitting the
; ah=4ch assignment
; (int 20h requires that cs points to the PSP segment)
;

;
; we do not the address calculations to take the .EXE header into account
; so we must subtract its length (20h) by an "org -20h"
; but, since ip will be 100h, we must also issue an "org 100h"
; and, since 0x100-0x20=0xE0...

org 0xE0        ; 100h for ip value - 20h for header



section .text align=1
;
; the MZ .EXE header structure
; 28 bytes long
; 1 pararaph equals 16 bytes
; 1 page equals 512 bytes
; suggested reading: int 21h,ah=4bh procedure
;
host_exe_header:
.signature: dw 'MZ'     ; the 'MZ' characters
.last_page_size: dw 1   ; number of used bytes in the final file page, 0 for all
.page_count: dw 1       ; number of file pages including any last partial page
.reloc: dw 0            ; number of relocation entries after the header
.paragraphs: dw 2       ; size of header + relocation table, in paragraphs
.minalloc: dw 0         ; minimum required additional memory, in paragraphs
.maxalloc: dw 0xFFFF    ; maximum memory to be allocated, in paragraphs
.in_ss: dw 0            ; initial relative value of the stack segment
.in_sp: dw 0xF000       ; initial sp value
.checksum: dw 0         ; checksum: 1's complement of sum of all words
.in_ip: dw 100h         ; initial ip value
.in_cs: dw -10h         ; initial relative value of the text segment
.offset: dw 0           ; offset of the relocation table from start of header
.overlay: dw 0          ; overlay value (0h = main program)

; pad header (its size in bytes must be a multiple of 16)
times (32-$+$$) db 0

mov dx,message
mov ah,09h              ; write string ds:dx to stdout
int 21h
int 20h

section .data align=1
message: db 'Hello, World!$'

section .bss align=1

通过尝试不同大小的程序,我得出结论,Dos会将每一页的512个字节全部加载到内存中。如果是这样,那么最后一页的字节数有何意义?

它是否会干扰.bss、堆栈数据和/或动态内存分配?

2个回答

4
总页码绝对不会被忽略,甚至被一些程序使用,这些程序不想在初始加载时加载全部文件,而是稍后自己读取必要的片段。最后一页字节数字段可能会被忽略,也可能不会,这取决于操作系统版本。它还可能被舍入到段落或磁盘扇区边界。您不应该依赖特定的行为并适当填充它。
您的测试代码可以工作是因为它很小,并且您特定的操作系统选择将足够的代码加载到内存中。如果您的程序大于单个页面但仍在page count字段中指定了1,则您的代码可能无法完全加载并且无法正常工作。我试过:
times (32-$+$$) db 0
times (512) nop
mov dx,message
mov ah,09h              ; write string ds:dx to stdout
int 21h
int 20h

如果 页面计数 为 1,此方法将失败,但是当 页面计数 为 2 时可以正常工作(使用 dosbox 进行测试)。


1
页面计数字段仅由DOS使用,而不是Windows NT。
NT使用的IMAGE_DOS_HEADER的唯二字段是e_magic(必须为IMAGE_DOS_SIGNATURE)和e_lfanew,它是一个偏移量,距离IMAGE_DOS_HEADER的开始不到4MB,指向包含NT加载程序所有信息的IMAGE_NT_HEADERS结构。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接