Как собрать сборку GAS и связать ее с библиотекой Open Watcom C?

Я пытаюсь создать 16-битные исполняемые файлы DOS, но с помощью компилятора gcc. Поэтому я использую древний порт gcc-4.3 ia16. Я сделал Docker-образ своей сборки: https://registry.hub.docker.com/u/ysangkok/ia16-gcc-rask

Вот что я пытаюсь:

host $ mkdir results
host $ docker run -v $PWD/results:/results -it ysangkok/ia16-gcc-rask
container $ cd results

Я не включаю заголовок, потому что gcc не может использовать заголовки libc OpenWatcom.

container $ echo 'main() { printf("lol"); }' > test.c

Я не связываюсь, потому что у меня нет доступных 16-битных binutils. Если я создаю объектный файл, он неправильно помечается как 16-битный.

container $ /trunk/build-ia16-master/prefix/bin/ia16-unknown-elf-gcc -S test.c

Теперь у меня есть этот файл сборки:

    .arch i8086,jumps
    .code16
    .att_syntax prefix
#NO_APP
    .section    .rodata
.LC0:
    .string "lol"
    .text
    .p2align    1
    .global main
    .type   main, @function
main:
    pushw   %bp
    movw    %sp,    %bp
    subw    $4, %sp
    call    __main
    movw    $.LC0,  %ax
    pushw   %ax
    call    printf
    addw    $2, %sp
    movw    %bp,    %sp
    popw    %bp
    ret
    .size   main, .-main
    .ident  "GCC: (GNU) 4.3.0 20070829 (experimental)"

Вне контейнера, в хосте, я пытаюсь собрать его с помощью yasm:

 % yasm -m x86 -p gas -f elf -o test.o test.s  
test.s:1: warning: directive `.arch' not recognized
test.s:3: error: junk at end of line, first unrecognized character is `p'

Я закомментирую синтаксическую строку, так как yasm не понимает этого, и попробую еще раз, на этот раз это удастся.

Я проверяю символы перемещения:

 % objdump -r test.o

test.o:     file format elf32-i386

RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE 
00000007 R_386_PC16        __main
0000000a R_386_16          .rodata
0000000e R_386_PC16        printf

К сожалению, они 32-битные. Когда я пытаюсь в любом случае связать в контейнере, это не работает:

root@1341f35c4590:/# cd ow/binl/
root@1341f35c4590:/ow/binl# WATCOM=/ow /ow/binl/wlink 
Open Watcom Linker Version 1.9
Portions Copyright (c) 1985-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.
Press CTRL/D to finish
WLINK>system dos
WLINK>file /results/test.o
[ comment: i press control-d on the next line ]
WLINK>loading object files
Warning! W1080: file /results/test.o is a 32-bit object file
Error! E2015: file /results/test.o(test.s): bad relocation type specified
Error! E2015: file /results/test.o(test.s): bad relocation type specified
Error! E2015: file /results/test.o(test.s): bad relocation type specified

Если я попытаюсь сделать COFF вместо ELF, yasm не сможет даже собрать:

root@1341f35c4590:/# cd ow/binl/
root@1341f35c4590:/ow/binl# WATCOM=/ow /ow/binl/wlink 
Open Watcom Linker Version 1.9
Portions Copyright (c) 1985-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.
Press CTRL/D to finish
WLINK>system dos
WLINK>file /results/test.o
WLINK>loading object files
Warning! W1080: file /results/test.o is a 32-bit object file
Error! E2015: file /results/test.o(test.s): bad relocation type specified
Error! E2015: file /results/test.o(test.s): bad relocation type specified
Error! E2015: file /results/test.o(test.s): bad relocation type specified

Я знаю, что yasm не поддерживает 16-бит, но, возможно, есть обходной путь? Есть ли ГАЗ-совместимый 16-битный ассемблер? Конвертеры GAS-Intel не работают.

1 ответ

Решение

Я не эксперт, но AFAIK нет 16-битных GAS-совместимых ассемблеров.

Кроме того, gcc никогда не предназначался для создания 8086 16-битного кода. Порт Rask создает 16-битный код в том смысле, что размер операнда по умолчанию равен 16-битному. Так что инструкция вроде mov ax, 1234h испускается как b8 34h 12h а не как 66 b8 34h 12h который будет интерпретироваться как mov eax, xxxx1234h в реальном режиме (если вы работаете на 80386+)

То же самое для адресного режима.

Проблема в том, что это всего лишь код, форматы объектных файлов по-прежнему 32-битные, поэтому они предназначены для использования 32-битными инструментами для использования в среде v86. Например, ELF не поддерживает перемещение 16 бит, как и COFF (в соответствии с nasm).

Таким образом, даже GCC и GAS создают 16-битный код, они выводят только относительно новый формат объекта. Все инструменты, которым предоставлен объектный файл, создают исполняемый файл MZ или COM, были созданы до этих форматов и не поддерживают их. Никаких усилий не было потрачено на добавление поддержки новых форматов, так как DOS давно уже не используется.


Очень длинные обходные пути (не предназначены для использования)

Я могу только изобразить два, очень, очень сложных, способа использования gcc в качестве компилятора.

  1. Попробуйте портировать на NASM. NASM поддерживает гораздо больший формат выходного файла, чем YASM (опять старый 16-битный формат был удален).

Соберите исходный файл с -masm=intel флаг, чтобы получить синтаксис Intel. Затем вам нужен инструмент для преобразования директив GAS dot в директивы NASM. Это должно быть закодировано вручную. Большинство из них простые замены, такие как .global XXX до GLOBAL XXX но вам нужно конвертировать эффективные адреса и добавить EXTERN XXX для неопределенных функций.

  1. Сделайте переезд самостоятельно. (Вы должны быть опытными с архитектурой IA16 и DOS)

Вы не должны использовать какой-либо внешний символ и производить код PIC (-fPIC флаг) и сырой двоичный файл (то есть просто код). Определите структуру указателей функций, по одному для каждой внешней функции, которую вы хотите использовать, что-то вроде

структура context_t
{
    int (*printf)(char* format, ...); 
    ...
};
Затем объявите указатель на context_t, сказать context_t* ctx; Если вам нужно использовать такую ​​функцию, как printf использование ctx->printf вместо. Скомпилируйте код.

Теперь создайте источник C, назовите его loader, который определяет переменную типа context_t и инициализировать его указатели. Затем загрузчик должен прочитать двоичный файл, найти место, выделенное для ctx указатель и установить его по адресу его context_t переменной, затем загрузите двоичный файл в память (на границе сегмента) и выполните его с дальним вызовом.

Вам нужно найти положение указателя в файле, вы можете использовать файл карты, сгенерированный GCC (-Xlinker -Map=output.map коммутатор) или используйте сигнатуру, подобную старой 32-битной службе BIOS PCI (сигнатура $PCI), и отсканируйте ее. Помните, что код, сгенерированный GCC, может накладывать другие ограничения, но переключатель PIC должен минимизировать это. Вы можете добавить двоичный файл после загрузчика (будьте осторожны, если вы используете формат MZ и следите за выравниванием) и упростить вещи.

Другие вопросы по тегам