gpt4 book ai didi

gcc - DOS .COM 文件末尾的额外字节,使用 GCC 编译

转载 作者:行者123 更新时间:2023-12-03 18:30:55 27 4
gpt4 key购买 nike

我有以下 C 源文件,其中一些 asm block 通过调用 DOS 系统调用来实现打印和退出例程。

__asm__(
".code16gcc;"
"call dosmain;"
"mov $0x4C, %AH;"
"int $0x21;"
);

void print(char *str)
{
__asm__(
"mov $0x09, %%ah;"
"int $0x21;"
: // no output
: "d"(str)
: "ah"
);
}

void dosmain()
{
// DOS system call expects strings to be terminated by $.
print("Hello world$");
}

链接器脚本文件和构建脚本文件是这样的,

OUTPUT_FORMAT(binary)
SECTIONS
{
. = 0x0100;
.text :
{
*(.text);
}
.data :
{
*(.data);
*(.bss);
*(.rodata);
}
_heap = ALIGN(4);
}
gcc -fno-pie -Os -nostdlib -ffreestanding -m16 -march=i386 \
-Wl,--nmagic,--script=simple_dos.ld simple_dos.c -o simple_dos.com

我习惯于在汇编中构建 .COM 文件,我知道 dos 文件的结构。但是,对于使用 GCC 生成的 .COM 文件,我在末尾得到了一些额外的字节,但我无法弄清楚原因。 (阴影区域内的字节和下面的方框是预期的,其他一切都下落不明)。

enter image description here

[ enter image description here ]

我的直觉是这些是 GCC 使用的一些静态存储。我认为这可能是由于程序中的字符串所致。因此,我对行 print("Hello world$"); 进行了注释,但额外的字节仍然存在。如果有人知道发生了什么并告诉如何防止 GCC 在输出中插入这些字节,那将会有很大的帮助。

此处提供源代码:Github

PS:目标文件也包含这些额外的字节。

最佳答案

由于您使用的是 native 编译器而不是 i686(或 i386)交叉编译器,因此您可以获得大量额外信息。它相当依赖于编译器配置。我建议执行以下操作以删除不需要的代码生成和部分:

  • 使用 GCC 选项 -fno-asynchronous-unwind-tables 消除任何 .eh_frame 部分。在这种情况下,这就是在 DOS COM 程序末尾附加不需要的数据的原因
  • 使用 GCC 选项 -static 进行构建而不重定位以避免任何形式的动态链接。
  • 让 GCC 使用 -Wl--build-id=none 选项传递给链接器,以避免不必要地生成任何 .note.gnu.build- id 部分。
  • 修改链接描述文件以丢弃任何 .comment 部分。

您的构建命令可能如下所示:

gcc -fno-pie -static -Os -nostdlib -fno-asynchronous-unwind-tables -ffreestanding \
-m16 -march=i386 -Wl,--build-id=none,--nmagic,--script=simple_dos.ld simple_dos.c \
-o simple_dos.com

我会将您的链接描述文件修改为:

OUTPUT_FORMAT(binary)
SECTIONS
{
. = 0x0100;
.text :
{
*(.text*);
}
.data :
{
*(.data);
*(.rodata*);
*(.bss);
*(COMMON)
}
_heap = ALIGN(4);

/DISCARD/ : { *(.comment); }
}

除了添加/DISCARD/指令以消除任何 .comment 部分外,我还在 .bss 旁边添加了 *(COMMON)。两者都是 BSS 部分。我还将它们移到数据部分之后,因为如果它们出现在其他部分之后,它们将不会占用 .COM 文件中的空间。我还将 *(.rodata); 更改为 *(.rodata*); 并将 *(.text); 更改为 * (.text*); 因为 GCC 可以生成以 .rodata.text 开头但具有不同后缀的节名称。


内联汇编

与您询问的问题无关,但很重要。在此内联程序集中:

__asm__(
"mov $0x09, %%ah;"
"int $0x21;"
: // no output
: "d"(str)
: "ah"
);

Int 21h/AH=9h还破坏了 AL。您应该使用 ax 作为 clobber。

由于您是通过寄存器传递数组的地址,因此您还需要添加一个内存 破坏器,以便编译器在您的内联程序集发出之前将整个数组存入内存。约束 "d"(str) 只告诉编译器您将使用指针作为输入,而不是指针指向什么。

如果您在 -O3 处进行了优化编译,您可能会发现程序的以下版本甚至没有您的字符串 "Hello world$"这是因为这个错误:

__asm__(
".code16gcc;"
"call dosmain;"
"mov $0x4C, %AH;"
"int $0x21;"
);

void print(char *str)
{
__asm__(
"mov $0x09, %%ah;"
"int $0x21;"
: // no output
: "d"(str)
: "ax");
}

void dosmain()
{
char hello[] = "Hello world$";
print(hello);
}

dosmain 的生成代码在堆栈上为字符串分配了空间,但在打印字符串之前从未将字符串放入堆栈:

00000100 <print-0xc>:
100: 66 e8 12 00 00 00 calll 118 <dosmain>
106: b4 4c mov $0x4c,%ah
108: cd 21 int $0x21
10a: 66 90 xchg %eax,%eax

0000010c <print>:
10c: 67 66 8b 54 24 04 mov 0x4(%esp),%edx
112: b4 09 mov $0x9,%ah
114: cd 21 int $0x21
116: 66 c3 retl

00000118 <dosmain>:
118: 66 83 ec 10 sub $0x10,%esp
11c: 67 66 8d 54 24 03 lea 0x3(%esp),%edx
122: b4 09 mov $0x9,%ah
124: cd 21 int $0x21
126: 66 83 c4 10 add $0x10,%esp
12a: 66 c3 retl

如果您更改内联程序集以包含这样的 “内存” 破坏器:

void print(char *str)
{
__asm__(
"mov $0x09, %%ah;"
"int $0x21;"
: // no output
: "d"(str)
: "ax", "memory");
}

生成的代码可能看起来类似于:

00000100 <print-0xc>:
100: 66 e8 12 00 00 00 calll 118 <dosmain>
106: b4 4c mov $0x4c,%ah
108: cd 21 int $0x21
10a: 66 90 xchg %eax,%eax

0000010c <print>:
10c: 67 66 8b 54 24 04 mov 0x4(%esp),%edx
112: b4 09 mov $0x9,%ah
114: cd 21 int $0x21
116: 66 c3 retl

00000118 <dosmain>:
118: 66 57 push %edi
11a: 66 56 push %esi
11c: 66 83 ec 10 sub $0x10,%esp
120: 67 66 8d 7c 24 03 lea 0x3(%esp),%edi
126: 66 be 48 01 00 00 mov $0x148,%esi
12c: 66 b9 0d 00 00 00 mov $0xd,%ecx
132: f3 a4 rep movsb %ds:(%si),%es:(%di)
134: 67 66 8d 54 24 03 lea 0x3(%esp),%edx
13a: b4 09 mov $0x9,%ah
13c: cd 21 int $0x21
13e: 66 83 c4 10 add $0x10,%esp
142: 66 5e pop %esi
144: 66 5f pop %edi
146: 66 c3 retl

Disassembly of section .rodata.str1.1:

00000148 <_heap-0x10>:
148: 48 dec %ax
149: 65 6c gs insb (%dx),%es:(%di)
14b: 6c insb (%dx),%es:(%di)
14c: 6f outsw %ds:(%si),(%dx)
14d: 20 77 6f and %dh,0x6f(%bx)
150: 72 6c jb 1be <_heap+0x66>
152: 64 24 00 fs and $0x0,%al

内联汇编的替代版本,它使用变量通过 a 约束传递子函数 9,并使用 + 将其标记为输入/输出(因为AX 的返回值被破坏)可以这样完成:

void print(char *str)
{
unsigned short int write_fun = (0x09<<8) | 0x00;
__asm__ __volatile__ (
"int $0x21;"
: "+a"(write_fun)
: "d"(str)
: "memory"
);
}

建议:不要使用 GCC 生成 16 位代码。内联汇编是 difficult to get right并且您可能会在低级例程中使用相当数量的它。你可以看看 Smaller C , Bruce's C compiler , 或 Openwatcom C作为替代品。它们都可以生成DOS COM程序。

关于gcc - DOS .COM 文件末尾的额外字节,使用 GCC 编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56368210/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com