问题描述
我试图产生16位DOS可执行文件,但使用gcc编译器。所以,我使用的是古老的GCC-4.3 IA16端口。我做了我构建一个码头工人形象:
下面就是我想:
主机$的mkdir结果
主持人$搬运工运行-v $ PWD /结果:/结果 - 它ysangkok / IA16-GCC-拉斯克
集装箱$ CD结果
我不包括头,导致GCC不能使用OpenWatcom的libc中的头。
容器$回声'的main(){printf的(笑); }'> test.c的
我不链接,因为我没有可用的16位的binutils。如果我建立一个目标文件,这是不正确标记为16位。
容器$ /干线/建造-IA16主/ preFIX /斌/ IA16-未知精灵-GCC -S test.c的
现在我有这样的汇编文件:
.arch i8086,跳跃
。code16
.att_syntax preFIX
#NO_APP
.section伪.RODATA
.LC0:
.string笑
。文本
.p2align 1
。全球主要
.TYPE为主,@function
主要:
PUSHW%基点
MOVW%SP,BP%
SUBW $ 4%SP
调用__main
MOVW $ .LC0,斧头%
PUSHW%斧
调用printf
ADDW $ 2,SP%
MOVW%BP,SP%
POPW%基点
RET
.size为主,。,主
.identGCC(GNU)4.3.0 20070829(实验)
在容器外,在主机,我尝试用YASM组装它:
%YASM -m -p 86 -f气-o精灵test.o test.s
test.s:1:警告:指令`.arch'无法识别
test.s:3:错误:垃圾在行结束,第一个无法识别的字符是'P'
我注释掉的语法线以来YASM不理解它,然后再试一次,这次它成功了。
我测试搬迁符号:
objdump的%-r test.otest.o:文件格式ELF32-I386RELOCATION记录[.text段]:
OFFSET类型值
00000007 R_386_PC16 __main
0000000A R_386_16 .RODATA
0000000e R_386_PC16的printf
可悲的是,他们是32位。当我尝试和容器反正链接,这是行不通的:
根@ 1341f35c4590:/#CD流/ BINL /
根@ 1341f35c4590:/流/ BINL#WATCOM = /流/流/ BINL /万联科
打开在Watcom链接器1.9版
部分版权所有(C)1985-2002 Sybase,Inc.保留所有权利。
来源$ C $ c中的开放的Sybase公众在Watcom许可证下是可用的。
见http://www.openwatcom.org/了解详情。
preSS CTRL / D完成
万联科> DOS系统
万联科>文件/results/test.o
[评论:我的下一行preSS控制-D]
万联科>加载目标文件
警告! W1080:文件/results/test.o是一个32位对象文件
错误! E2015:文件/results/test.o(test.s):指定坏定位类型
错误! E2015:文件/results/test.o(test.s):指定坏定位类型
错误! E2015:文件/results/test.o(test.s):指定坏定位类型
如果我试图做一个COFF而不是ELF的,YASM甚至无法装配:
根@ 1341f35c4590:/#CD流/ BINL /
根@ 1341f35c4590:/流/ BINL#WATCOM = /流/流/ BINL /万联科
打开在Watcom链接器1.9版
部分版权所有(C)1985-2002 Sybase,Inc.保留所有权利。
来源$ C $ c中的开放的Sybase公众在Watcom许可证下是可用的。
见http://www.openwatcom.org/了解详情。
preSS CTRL / D完成
万联科> DOS系统
万联科>文件/results/test.o
万联科>加载目标文件
警告! W1080:文件/results/test.o是一个32位对象文件
错误! E2015:文件/results/test.o(test.s):指定坏定位类型
错误! E2015:文件/results/test.o(test.s):指定坏定位类型
错误! E2015:文件/results/test.o(test.s):指定坏定位类型
我知道YASM不支持16位,但也许有一个解决方法吗?是否有气体兼容的16位汇编?气到英特尔转换器不工作。
我的不可以专家,但据我所知没有16位兼容GAS汇编。
此外GCC从来就产生8086的16位code。该拉斯克港产16位code中的
该操作数大小为16位默认。因此,像 MOV AX,1234H
发出为 B8 34H 12H
,而不是 66的指令B8 34H 12H
这将是
除$ P $实模式PTED为 MOV EAX,xxxx1234h
(如果您对80386 +运行)
为地址模式同样的事情。
的问题是,这仅仅是code时,对象的文件格式仍是32位,所以他们是为了由32位工具最终用于在使用中使用
V86的环境。
ELF例如不支持16位的搬迁,也不会COFF(根据NASM)。
因此,即使GCC和GAS产生16位code,他们只输出相对较新的目标格式。
给定一个目标文件创建一个MZ或COM可执行每个工具在这些格式之前创建的,并且不支持他们。
任何努力都用在增加新格式的支持,因为DOS停止很久以前就被使用。
非常长的变通办法(并不意味着被使用)
我只能像二,非常非常难,方式使用GCC编译器。
- 尝试移植到NASM。 NASM支持超过YASM(再次旧的16位格式已被丢弃)更为输出文件格式。
组装与 -masm =英特尔
标记的源文件,以获得英特尔的语法。然后,你需要GAS点指令转换成NASM指令的工具。
的这需要手动为codeD 的。他们大多是简单的替换,比如。全球
XXX为 GLOBAL XXX
,但你需要转换的有效地址,
添加 EXTERN XXX
未定义功能。
您不能使用任何外部符号,并产生PIC code( -fPIC
标志)和原始二进制(即只是code)。
定义函数指针的结构,一个是你需要使用的每个外部函数,像
结构context_t
{
INT(*的printf)(字符*格式,...);
...
};
然后声明一个指针 context_t
,说 context_t * CTX
;
如果你需要使用像 A printf函数
使用则将ctx->的printf
来代替。
编译code。
现在创建一个C源代码,称之为装载机,定义类型 context_t
的一个变量,并初始化它的指针。
loader然后必须读取二进制文件中,找到分配给 CTX
指针空间,并将其设置为 context_t $ C $的地址C>变量,则
加载在内存中(在段边界)的二进制文件,并与远调用执行它。
您需要找到文件中的指针的位置,你可以使用GCC( -Xlinker -Map = output.map
开关)或生成的地图文件使用签名
像旧的BIOS PCI 32bit的服务($ PCI签名),并扫描它。
要注意的是海湾合作委员会中产生的code规定其他限制,但PIC开关应尽量减少这一点。
您可以追加事件的二进制文件的加载器(如果你使用MZ格式和心灵对准谨防)后,简化的东西。
I am trying to produce 16-bit DOS executables, but using the gcc compiler. So I am using the ancient gcc-4.3 ia16 port. I made a Docker image of my build: https://registry.hub.docker.com/u/ysangkok/ia16-gcc-rask
Here's what I am trying:
host $ mkdir results
host $ docker run -v $PWD/results:/results -it ysangkok/ia16-gcc-rask
container $ cd results
I don't include the header, cause gcc can't use OpenWatcom's libc headers.
container $ echo 'main() { printf("lol"); }' > test.c
I don't link cause I don't have 16-bit binutils available. If I build an object file, it isn't correctly marked as 16-bit.
container $ /trunk/build-ia16-master/prefix/bin/ia16-unknown-elf-gcc -S test.c
Now I have this assembly file:
.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)"
Outside the container, in the host, I try to assemble it with 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'
I comment out the syntax line since yasm doesn't understand it, and try again, this time it succeeds.
I test the relocation symbols:
% 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
Sadly they are 32-bit. When I try and link anyway in the container, it doesn't work:
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
If I try and make a COFF instead of an ELF, yasm can't even assemble:
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
I know yasm doesn't support 16-bit, but maybe there is a workaround? Is there a GAS-compatible 16-bit assembler? The GAS-to-Intel converters are not working.
I'm not an expert but AFAIK there are no 16 bit GAS compatible assemblers.
Furthermore gcc was never meant to produce 8086 16 bit code. The Rask port produce 16 bit code in the sensethat the operand size is 16 bit by default. So an instruction like mov ax, 1234h
is emitted as b8 34h 12h
rather than as 66 b8 34h 12h
which will beinterpreted as mov eax, xxxx1234h
in real mode (if you run on 80386+)
Same thing for the address mode.
The problem is that this is just the code, the object file formats are still for 32 bit, so they are meant to be used by 32 bit tools eventually for use in av86 environment.ELF for example don't support 16 bit relocation, nor COFF does (according to nasm).
So even GCC and GAS produce 16 bit code they output only relatively new object format.Every tools that given an object file create a MZ or COM executable was created before these formats and don't support them.No efforts have been spent on adding support to new formats as DOS ceased to be used long time ago.
Very long Workarounds (not meant to be used)
I can only image two, very very hard, way to use gcc as a compiler.
- Try porting to NASM. NASM support far more output file format than YASM (again old 16 bit format have been dropped).
Assemble the source file with -masm=intel
flag to get Intel syntax. Then you need a tool to convert GAS dot directives to NASM directives.This have to be coded manually. Most of them are simple substitutions like .global
XXX to GLOBAL XXX
but you need to convert effective addresses andadd EXTERN XXX
for undefined functions.
- Do the relocations yourself. (You need to be skilled with IA16 architecture and DOS)
You must not use any external symbol and produce PIC code (-fPIC
flag) and a raw binary (i.e. just code).Define a struct of function pointers, one for each external function you need to use, something like
struct context_t { int (*printf)(char* format, ...); ... };
Then declare a pointer to context_t
, say context_t* ctx
;If you need to use a function like printf
use ctx->printf
instead.Compile the code.
Now create a C source, call it loader, that define a variable of type context_t
and initialize its pointers.The loader then must read the binary file, locate the space allocated for the ctx
pointer and set it to the address of its context_t
variable, thenload the binary file in memory (at segment boundary) and execute it with a far call.
You need to find the position of the pointer in the file, you can use a map file generated by GCC (-Xlinker -Map=output.map
switch) or use a signaturelike the old BIOS PCI 32bit service (the $PCI signature) and scan for it.Beware that the code generated by GCC may impose other constraints, but the PIC switch should minimize this.You can event append the binary file after the loader (beware if you use MZ format and mind the alignment) and simplify things.
这篇关于我如何组装GAS组装和与开放在Watcom C库链接呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!