Важность количества страниц и размера последней страницы в заголовке MZ (DOS, 16 бит) .EXE

Я пытаюсь научиться создавать файлы Dos .EXE с помощью сборки (NASM), создавая заголовок вручную и собирая файл как двоичный файл. У меня проблема с параметрами страницы (как общее количество страниц, так и количество байтов на последней странице). Какие бы маленькие я не устанавливал начальные значения, программа будет работать.

В крайнем случае, следующая программа работает даже при установке 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

Экспериментируя с разными размерами программ, я пришел к выводу, что Дос загружает в память все 512 байт каждой страницы. Если да, то какова цель количества байтов на последней странице?

Может ли это мешать .bss, данным стека и/или распределению динамической памяти?


person padawan    schedule 09.01.2013    source источник


Ответы (2)


Общее количество страниц определенно не игнорируется, оно даже используется программами, которые не хотят, чтобы весь их файл загружался изначально. Нужные фрагменты они прочитают позже сами. Поле bytes in the last page может игнорироваться или не игнорироваться в зависимости от версии ОС. Его также можно округлить до границы абзаца или сектора диска. Вы не должны зависеть от конкретного поведения и заполнить его правильно.

Ваш тестовый код работает, потому что он небольшой, и ваша конкретная ОС выбрала загрузку достаточного количества кода в память. Если вы сделаете свою программу больше одной страницы, но все же укажете 1 в поле page count, вероятно, ваш код не будет полностью загружен и не будет работать. Я пытался:

times (32-$+$$) db 0
times (512) nop
mov dx,message
mov ah,09h              ; write string ds:dx to stdout
int 21h
int 20h

Это не работает, если page count равно 1, но работает, если page count равно 2 (используется dosbox для тестирования).

person Jester    schedule 10.01.2013

Поле количества страниц используется DOS, а не Windows NT.

Единственные два поля IMAGE_DOS_HEADER, используемые NT, — это e_magic (которое должно быть IMAGE_DOS_SIGNATURE) и e_lfanew, представляющее собой смещение менее 4 МБ от начала IMAGE_DOS_HEADER до структуры IMAGE_NT_HEADERS, которая содержит всю информацию для загрузчика NT.

person SecurityMatt    schedule 28.01.2013