Как собрать сборку 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 -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' 

Я прокомментирую строку синтаксиса, так как ясность ее не понимает и повторите попытку, на этот раз она преуспеет.

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

  % 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, то ясность даже не может собраться:

 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-бит, но, возможно, есть обходной путь? Есть ли совместимый с GAS 16-разрядный ассемблер? Преобразователи GAS-to-Intel не работают.

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

Кроме того, gcc никогда не предназначался для производства 8086-битового кода. Порт 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 ) и необработанный двоичный код (то есть только код). Определите структуру указателей функций, по одной для каждой внешней функции, которую вам нужно использовать, что-то вроде

 struct context_t
 {
     int (* printf) (char * format, ...); 
     ...
 };

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

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

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