MinGW的ld无法在非PE输出文件上执行PE操作

时间:2015-06-19 13:37:04

标签: gcc assembly mingw nasm osdev

我知道还有一些类似的问题,不管是StackOverflow还是没有。我为此研究了很多,但仍未找到一个解决方案。 我正在做一个作为副项目的操作系统。我一直在汇编,但现在我想加入C代码。 为了测试,我制作了这个汇编代码文件(名为test.asm):

[BITS 32]

GLOBAL _a

SECTION .text

_a:
    jmp $

然后我制作了这个C文件(名为main.c):

extern void a(void);
int main(void)
{
    a();
}

要链接,我使用了这个文件(名为make.bat):

"C:\minGW\bin\gcc.exe"  -ffreestanding -c -o c.o main.c
nasm -f coff -o asm.o test.asm
"C:\minGW\bin\ld.exe" -Ttext 0x100000 --oformat binary -o out.bin c.o asm.o

pause

我已经研究了很多年,我仍然在努力寻找答案。我希望这不会被标记为重复。我承认存在类似的问题,但都有不同的答案,没有一个对我有用。

问题:我做错了什么?

1 个答案:

答案 0 :(得分:3)

旧的MinGW版本存在“ld”根本无法创建非PE文件的问题。

也许当前版本有同样的问题。

解决方法是使用“ld”创建PE文件,然后使用“objcopy”将PE文件转换为二进制,HEX或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”也可以在“main()”之前加上“a()”的代码,甚至可以在“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”部分的开头填充存根字节,以便第一个有用的字节位于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。)