Маленькая модель DOS .exe, скомпилированная и связанная OpenWatcom, дает сбой - PullRequest
1 голос
/ 19 июня 2020

Я пытаюсь создать небольшую программу DOS .exe. Я написал точку входа в сборке NASM

; st.nasm
global _small_code_
global _printmsg_
extern _main0_

segment code
_small_code_:
..start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, stacktop

mov ah, 9  ; WRITE_STDOUT
mov dx, hello_msg
int 0x21
call _main0_
; call _printmsg_
; mov ax, 3
mov dx, ax
add dx, hello_msg
mov ah, 9  ; WRITE_STDOUT
int 0x21
mov ah, 0x4c  ; EXIT, exit code in al
int 0x21

_printmsg_:
ret
push dx
xchg ax, dx
mov ah, 9  ; WRITE_STDOUT
mov dx, hello_msg  ; !!
int 0x21
pop dx
ret  ; !! restore AX?

segment data
hello_msg: db 'Hello, World!', 13, 10, '$'

segment stack stack
resb 1024
stacktop:

Обратите внимание, что я не уверен, как должны выглядеть код ..start: и сегменты, я скопировал эту часть откуда-то.

Я написал основную программу в C:

/* prog.c */
void _printmsg(const char *msg);
int add(int a, int b) {
  return a + b * 2;
}
void other() {
  _printmsg("Hello!\r\n$"); /*CRASH*/
  /*_printmsg(0);*/  /*OK*/
}
int _main0() {
  return 5;
}

Я компилирую ее следующим образом:

$ nasm -f obj -o st.obj st.nasm
$ owcc -bdos -mcmodel=s -fno-stack-check -Os -s -march=i86 -o prog.exe prog.c st.obj

Результат prog.exe:

$ xxd prog.exe 
00000000: 4d5a 8b00 0100 0200 0300 4000 ffff 0100  MZ........@.....
00000010: 4b04 0000 0a00 0100 2000 0000 0000 0000  K....... .......
00000020: 0b00 0100 1000 0100 0000 0000 0000 0000  ................
00000030: d1e2 01d0 c3b8 0000 e934 00b8 0500 c300  .........4......
00000040: 4865 6c6c 6f21 0d0a 2400 b801 008e d8b8  Hello!..$.......
00000050: 0100 8ed0 bc4b 04b4 09ba 3b00 cd21 e8da  .....K....;..!..
00000060: ff89 c281 c23b 00b4 09cd 21b4 4ccd 21c3  .....;....!.L.!.
00000070: 5292 b409 ba3b 00cd 215a c348 656c 6c6f  R....;..!Z.Hello
00000080: 2c20 576f 726c 6421 0d0a 24              , World!..$

Дизассемблирование prog.exe:

$ ndisasm -e 0x20 -b 16 prog.exe 
00000000  0B00              or ax,[bx+si]
00000002  0100              add [bx+si],ax
00000004  1000              adc [bx+si],al
00000006  0100              add [bx+si],ax
00000008  0000              add [bx+si],al
0000000A  0000              add [bx+si],al
0000000C  0000              add [bx+si],al
0000000E  0000              add [bx+si],al
00000010  D1E2              shl dx,1
00000012  01D0              add ax,dx
00000014  C3                ret
00000015  B80000            mov ax,0x0
00000018  E93400            jmp 0x4f
0000001B  B80500            mov ax,0x5
0000001E  C3                ret
0000001F  004865            add [bx+si+0x65],cl
00000022  6C                insb
00000023  6C                insb
00000024  6F                outsw
00000025  210D              and [di],cx
00000027  0A24              or ah,[si]
00000029  00B80100          add [bx+si+0x1],bh
0000002D  8ED8              mov ds,ax
0000002F  B80100            mov ax,0x1
00000032  8ED0              mov ss,ax
00000034  BC4B04            mov sp,0x44b
00000037  B409              mov ah,0x9
00000039  BA3B00            mov dx,0x3b
0000003C  CD21              int 0x21
0000003E  E8DAFF            call 0x1b
00000041  89C2              mov dx,ax
00000043  81C23B00          add dx,0x3b
00000047  B409              mov ah,0x9
00000049  CD21              int 0x21
0000004B  B44C              mov ah,0x4c
0000004D  CD21              int 0x21
0000004F  C3                ret
00000050  52                push dx
00000051  92                xchg ax,dx
00000052  B409              mov ah,0x9
00000054  BA3B00            mov dx,0x3b
00000057  CD21              int 0x21
00000059  5A                pop dx
0000005A  C3                ret
0000005B  48                dec ax
0000005C  656C              gs insb
0000005E  6C                insb
0000005F  6F                outsw
00000060  2C20              sub al,0x20
00000062  57                push di
00000063  6F                outsw
00000064  726C              jc 0xd2
00000066  64210D            and [fs:di],cx
00000069  0A24              or ah,[si]

prog.exe помещает DOSBox в бесконечный l oop. Как ни странно, если я удалю строковый литерал из исходного файла C (в функции other, которая даже не вызывается), он успешно возвращается. Что не так в файле сборки?

Обратите внимание, что я впервые использую OpenWatcom и впервые создаю файл DOS .exe.

Я не Я хочу написать функцию main, потому что это приведет к тому, что OpenWatcom lib c будет связан с выходным исполняемым файлом, что сделает его излишне большим.

1 Ответ

3 голосов
/ 20 июня 2020

Основная проблема заключается в том, как вы определяете сегмент кода. Компилятор Watcom C / C ++ при использовании модели памяти SMALL требует, чтобы сегмент кода был вызван _TEXT с классом CODE. Это несоответствие между кодом сборки и кодом C приводит к тому, что сегмент кода находится в разных физических сегментах, а call _main0_ перескакивает в неправильное место в памяти, вызывая выбросы исключений и зависание программы или

Вы также можете заставить компоновщик Watcom сгенерировать необходимый СТЕК в DOS EXE, создав сегмент под названием _STACK с атрибутом STACK и классом STACK. Если вы создадите сегмент стека таким образом, вам не нужно будет инициализировать SS: SP в начале вашей программы.

Другие разделы, которые Watcom использует в SMALL модели памяти:

  • _DATA сегмент с классом DATA для чтения / записи данных
  • CONST сегмент с классом DATA для строковые литералы (которые не предполагается изменять)
  • CONST2 сегмент с классом DATA для других данных только для чтения
  • _BSS сегмент с классом BSS для неинициализированных данных.

Watcom ожидает, что все сегменты CONST, CONST2, _DATA и _BSS будут в одной группе с именем DGROUP. На все данные в одной группе можно ссылаться по имени группы. Когда вы настраиваете DGROUP так, как ожидает Watcom, тогда все, что вам нужно сделать, это инициализировать DS сегментом DGROUP, а не отдельными сегментами в группе.

метка, которая начинается с .., действует как точка входа DOS, с которой должно начаться выполнение. Таким образом, ..start используется для создания точки входа в заголовке DOS EXE, сообщающей загрузчику программы, где начать выполнение, когда программа загружается в память.

Пересмотренная версия вашего ассемблерного кода могла бы выглядеть так :

; st.nasm

; DGROUP in watcom C/C++ for small model is:
GROUP DGROUP CONST CONST2 _DATA _BSS

global _small_code_
global _printmsg_
extern _main0_

; Code Segment (16-bit code)
segment _TEXT use16 class=CODE
_small_code_:
; .. denotes the label to be used as the DOS entry point
..start:
    mov ax, DGROUP
    mov ds, ax

    mov ah, 9  ; WRITE_STDOUT
    mov dx, hello_msg
    int 0x21
    call _main0_
    ; call _printmsg_
    ; mov ax, 3
    mov dx, ax
    add dx, hello_msg
    mov ah, 9  ; WRITE_STDOUT
    int 0x21
    mov ah, 0x4c  ; EXIT, exit code in al
    int 0x21

_printmsg_:
    ret
    push dx
    xchg ax, dx
    mov ah, 9  ; WRITE_STDOUT
    mov dx, hello_msg  ; !!
    int 0x21
    pop dx
    ret  ; !! restore AX?

; Read only string literals here
segment CONST class=DATA
hello_msg: db 'Hello, World!', 13, 10, '$'

; Other read only data here
segment CONST2 class=DATA

; Read/Write data here
segment _DATA class=DATA

; Uninitialized data segment
segment _BSS class=BSS

; Stack segment 1k in size
segment _STACK STACK class=STACK
resb 1024

Этот код предполагает, что SS! = DS, однако он должен быть скомпилирован с опцией -Wc,-zu OW CC, которая передает -zu в W CC (Watcom Компилятор). -zu изменяет генерацию кода так, чтобы:

-zu             SS != DGROUP (i.e., do not assume stack is in data segment)

Если вы используете sh для установки SS == DS == DGROUP, есть несколько способов сделать это. Один из вариантов, который я могу предложить, - это поместить _STACK в DGROUP со всеми другими данными программы. Вам понадобится метка после resb 1024, например stack_top:, чтобы вы могли загрузить это смещение в SP при запуске после того, как вы установите SS на то же значение, что и DS * 1074. *. Это изменение приведет к созданию кода сборки, который будет выглядеть так:

; st.nasm

; DGROUP in watcom C/C++ for small model is:
GROUP DGROUP CONST CONST2 _DATA _BSS _STACK
; _STACK has been added to DGROUP so we can set SS==DS==DGROUP

global _small_code_
global _printmsg_
extern _main0_

; Code Segment (16-bit code)
segment _TEXT use16 class=CODE
_small_code_:
; .. denotes the label to be used as the DOS entry point
..start:
    mov ax, DGROUP
    mov ds, ax
    mov ss, ax             ; Set stack SS:SP to DGROUP:stack_top
    mov sp, stack_top

    mov ah, 9  ; WRITE_STDOUT
    mov dx, hello_msg
    int 0x21
    call _main0_
    ; call _printmsg_
    ; mov ax, 3
    mov dx, ax
    add dx, hello_msg
    mov ah, 9  ; WRITE_STDOUT
    int 0x21
    mov ah, 0x4c  ; EXIT, exit code in al
    int 0x21

_printmsg_:
    ret
    push dx
    xchg ax, dx
    mov ah, 9  ; WRITE_STDOUT
    mov dx, hello_msg  ; !!
    int 0x21
    pop dx
    ret  ; !! restore AX?

; Read/Write data here
segment _DATA class=DATA

; Read only string literals here
segment CONST class=DATA
hello_msg: db 'Hello, World!', 13, 10, '$'

; Other read only data here
segment CONST2 class=DATA

; Uninitialized data segment
segment _BSS class=BSS

; Stack segment 1k in size
segment _STACK STACK class=STACK
resb 1024
stack_top:
...