旧的 MinGW 版本存在“ld”根本无法创建非 PE 文件的问题。
也许当前版本也有同样的问题。
解决方法是使用“ld”创建一个 PE 文件,然后使用“objcopy”将 PE 文件转换为二进制、十六进制或 S19。
- - 编辑 - -
再次思考这个问题我发现两个问题:
正如我已经说过的,某些版本的“ld”在创建“二进制”输出(而不是“PE”、“ELF”或任何使用的格式)时存在问题。
代替:
ld.exe --oformat binary -o file.bin c.o asm.o
您应该使用以下顺序来创建二进制文件:
ld.exe -o file.tmp c.o asm.o
objcopy -O binary file.tmp file.bin
这将创建一个名为“binary.tmp”的“.exe”文件;那么“objcopy”将从“.exe”文件创建原始数据。
第二个问题是链接本身:
“ld”采用类似“.exe”的文件格式 - 即使输出文件是二进制文件。这意味着 ...
- ...您甚至无法确定“main.o”的目标代码是否确实放置在结果目标代码的首地址处。 “ld”还可以将“a()”的代码放在“main()”之前,甚至可以将“内部”代码放在“a()”和“main()”之前。
- ...寻址的工作方式有点不同,这意味着如果您做错了什么,将会创建大量填充字节(可能在文件的开头!)。
我看到的唯一可能性是创建一个“链接器脚本”(有时称为“链接器命令文件”)并在汇编器代码中创建一个特殊部分(因为我通常使用“nasm”之外的另一个汇编器,我不知道语法是否这里是正确的):
[BITS 32]
GLOBAL _a
SECTION .entry
jmp _main
SECTION .text
_a:
jmp $
在链接描述文件中,您可以指定哪些节以何种顺序出现。指定“.entry”是文件的第一部分,这样您就可以确定它是文件的第一条指令。
在链接器脚本中,您也可能会说多个部分(例如“.entry”、“.text”和“.data”)应组合成一个部分。这很有用,因为 PE 文件中的节通常是 0x1000 字节对齐的!如果您不将多个部分合并为一个部分,您将在各部分之间获得大量存根字节!
不幸的是,我不是链接器脚本方面的专家,因此我无法为您提供太多帮助。
使用“-Ttext”也有问题:
在PE文件中,节的实际地址计算为“图像基址”+“相对地址”。 “-Ttext”参数只会影响“相对地址”。因为在 Windows 中,第一节的“相对地址”通常固定为 0x1000,所以“-Ttext 0x2000”除了在第一节的开头填充 0x1000 存根字节之外什么也不做。但是,您根本不会影响“.text”的起始地址 - 您只需在“.text”部分的开头填充存根字节,以便第一个useful字节位于 0x2000。 (也许某些“ld”版本的行为有所不同。)
如果您希望文件的第一部分位于地址 0x100000,则应在链接器脚本中使用等效的“-Ttext 0x1000”(如果使用链接器脚本,则不使用 -Ttext)并定义“图像库”至 0xFF000:
ld.exe -T linkerScript.ld --image-base 0xFF000 -o binary.tmp a.o main.o
“.text”部分的内存地址将为 0xFF000 + 0x1000 = 0x100000。
(“objcopy”生成的二进制文件的第一个字节将是第一个部分的第一个字节 - 表示内存地址 0x100000。)