【问题标题】:Porting from Windows to Linux. Assembler command translation从 Windows 移植到 Linux。汇编命令翻译
【发布时间】:2023-06-23 21:43:01
【问题描述】:

我最近开始学习从 Windows 移植到 Linux。我一直在将程序从 Intel 语法翻译为 AT&T 语法,还将它从 x32 转换为 x64。而且由于我对汇编程序,尤其是 AT&T 还很陌生,所以我在移植时遇到了一些麻烦。顺便提一下:我故意不使用.intel_syntax 指令。

所以我一直在翻译这些命令:

RTLWriteIntegerBuffer: TIMES 3 DB 0x90,0x8D,0x40,0x00

接着是:

LEA EDI,[OFFSET RTLWriteIntegerBuffer+ECX-1]

另一个:

LEA EBX,[EDX+'0']

还有一个:

ReadCharInited: DB 0
CMP BYTE PTR ReadCharInited,0

另一个问题是:AT&T 语法和 Intel 语法之间是否存在 1:1 的映射关系? 或者是否存在 AT&T 不支持的特定 Intel 命令?

也许有人知道这样的功能:

HEAP_NO_SERIALIZE=1
HEAP_GENERATE_EXCEPTIONS=4
HEAP_ZERO_MEMORY=8
...
INVOKE HeapAlloc,EAX,HEAP_GENERATE_EXCEPTIONS+HEAP_ZERO_MEMORY+HEAP_CREATE_ALIGN_16,4194332

这可能是 Borland Turbo Assembler 特有的调用 kernel32.dllHeapAlloc 的方式,但我不确定。 可以翻译成fallocate syscall 吗?

提前致谢

【问题讨论】:

  • 您可以使用.rept 代替times 或输入它。对于其他人,请阅读 at&t 内存引用语法。 fallocateHeapAlloc 完全不同。
  • 你真的需要 AT&T 语法吗?移植到gas的.intel_syntax noprefix(与MASM非常相似)或NASM语法不是更容易吗?然后你只需要改变系统调用/ABI,而不是每条指令。 (参见*.com/tags/x86/info 获取汇编器手册的链接)
  • 手工很容易出错,我建议先用 MASM 组装,然后用 objdump -drwC 反汇编,然后将指令和 cmets 移植到反汇编中。我认为 objdump 可以选择更接近于为汇编器输入做好准备,或者使用 Agner Fog 的 objconv 反汇编器,它确实可以生成可以再次汇编的输出。
  • 有关 AT&T 语法的介绍,您可能想RTFM

标签: assembly system-calls gnu-assembler att


【解决方案1】:

在谈论“AT&T 语法”与“Intel 语法”时,通常仅指指令助记符和操作数排序和格式之间的区别。

因此,例如,这是 AT&T 语法中的指令:

movl $1, (%esi)

这是使用 Intel 语法的相同指令:

mov  DWORD PTR [esi], 1

对于以 Intel 语法表示的每条指令,该指令在 AT&T 语法中都有一个等效表示。

由于不再有 AT&T 汇编器和 Intel 汇编器,因此指令(除了指令之外的所有内容)是另一回事。 GNU 汇编器 (GAS) 支持 AT&T 和 Intel 语法,但只支持它自己的指令,这些指令是 AT&T 汇编器使用的指令的扩展。 Microsoft 的 MASM 仅支持 Intel 语法,但也仅支持其自己的指令,这些指令是原始 Intel 汇编程序的扩展。从一个汇编程序的指令到另一个汇编程序的指令并不总是直接等效的。在某些情况下,它们使用不同的目标文件格式这一事实可能会阻止找到在使用不同目标文件格式的不同汇编器中实现指令功能的任何方法。 (或者甚至是使用不同格式的同一个汇编器,例如 GNU 汇编器。)

例如,这里有一些 GAS 指令:

.rept 3
.byte 0x90, 0x8D, 0x40, 0x00
.endr

下面是等效的 MASM 指令:

REPT 3
DB 90h, 8Dh, 40h, 00h
ENDM

但没有 MASM 等效于以下 GAS 指令,因为它特定于 ELF 对象格式,MASM 不支持:

.protected foo

另一方面,没有直接等效于以下 MASM 指令,因为 GAS 不支持任何复杂的高级语言指令:

INVOKE HeapAlloc,EAX,HEAP_GENERATE_EXCEPTIONS+HEAP_ZERO_MEMORY+HEAP_CREATE_ALIGN_16,4194332

要移植以前特定于 ELF 的指令,您必须重新设计应用程序以处理 Windows 如何处理共享库。要移植后面的 MASM 特定指令,您要么必须创建自己的宏来确定如何正确传递所有参数,要么只需根据Linux x86-64 ABI。 (您还必须找到一个合适的 Linux 函数来调用和传递一组不同的参数,但这是与翻译指令本身不同的问题。)

一些汇编器试图与其他汇编器兼容;例如,Borland 的 TASM 试图与 MASM 兼容,尽管它是 MASM 的旧版本。所以在 TASM 中工作的东西(在其默认的 MASM 模式下)通常会在 MASM 中工作,反之亦然。然而,许多汇编程序基本上使用他们自己的 x86 汇编语言版本。

例如,您在帖子中显示的代码似乎使用了两种不同的汇编语言版本,并且不能由任何单一的汇编程序进行汇编。您的第一行代码使用TIMES 指令,但该指令仅受 NASM 支持,它不使用 AT&T 语法或 Intel 语法。它有自己的指令语法,尽管它与 Intel 语法没有什么不同。它也有自己不兼容的指令集,不是基于任何特定的东西,就像你展示的 TIMES 指令。

您的其余代码似乎采用 MASM 语法。除了第三行,它不能与 NASM 正确组装(第一行也不能与 MASM 正确组装)。我也不确定是否会与 TASM 一起组装,因为在 MASM 6 中添加了 INVOKE 指令。

请注意,鉴于您的代码的性质,用汇编语言编写它可能一无所获,最好将其翻译成 C、C++ 或其他您更熟悉的语言。

【讨论】:

  • 在支持讨论指令后,我再次 +1 建议使用 Linux 系统调用移植到 C 而不是 AT&T x86-64 asm。 (我也注意到了 TIMES 与 INVOKE 的区别,但不确定 TIMES 是否只是 NASM)。
【解决方案2】:

我对 Windows 不是很熟悉,不过还是让我试着帮助你吧。

RTLWriteIntegerBuffer: TIMES 3 DB 0x90,0x8D,0x40,0x00

DB 指令在 UNIX 汇编器中转换为 .byteTIMES 在气体中受支持(如 .rept)但我建议避免使用它,因为它不能移植到其他 UNIX 汇编器。所以这个sn-p就变成了

RTLWriteIntegerBuffer:
    .byte 0x90,0x8d,0x40,0x00
    .byte 0x90,0x8d,0x40,0x00
    .byte 0x90,0x8d,0x40,0x00
LEA EDI,[OFFSET RTLWriteIntegerBuffer+ECX-1]

[disp+base+index*scale] 形式的 Intel 风格的内存操作数在 AT&T 语法中变为 disp(base,index,scale)。如果indexscale 都为空,则可以改写disp(base),否则只需省略缺少的寄存器(但保留逗号)。你的指令变成了

lea RTLWriteIntegerBuffer-1(%ecx),%edi

注意交换的操作数。在 AT&T 语法中,所有两个参数的操作码都交换了操作数,除了一些浮点指令。

LEA EBX,[EDX+'0']

同样,这个变成了

lea '0'(%edx),%ebx
ReadCharInited: DB 0
CMP BYTE PTR ReadCharInited,0

这个变成了

ReadCharInited: .byte 0
    cmpb $0,ReadCharInited

注意b 后缀表明这是一个字节指令。其他后缀包括w 用于wordl 用于doubleword(长)和q 用于quadword(仅限amd64)。立即数加前缀$,内存操作数不加前缀。

另一个问题是:AT&T 语法和 Intel 语法之间是否存在 1:1 的映射关系?或者是否存在 AT&T 不支持的特定 Intel 命令?

对于说明,一般都有。解决这个问题的一种方法是用 Intel 语法编写指令,然后以 AT&T 语法(objdump -d)转储它,反之亦然(objdump -d -Mintel)。

对于伪指令(例如TIMESDB)可能没有,因为 UNIX 汇编器在概念上不同于例如masm。

INVOKE HeapAlloc,EAX,HEAP_GENERATE_EXCEPTIONS+HEAP_ZERO_MEMORY+HEAP_CREATE_ALIGN_16,4194332

您可能只想使用 C 标准库中的旧 calloc 而不是这个函数。如果您打算链接到 libc,这样的事情应该可以工作:

push $4194332
push $1
call calloc
add $8,%esp

请注意,虽然没有 HeapDestroy 或类似名称,但如果您想要此功能,则需要编写自己的分配器。

【讨论】:

  • 在 Windows 中,callocmalloc 构建在 HeapAlloc 之上,而 free 构建在 HeapFree 之上,使用由 C 运行时库管理的私有堆. (HeapFreeHeapAlloc 的反面;HeapDestroyHeapCreate 的反面。)所以是的,只需在 *nix 中调用 callocfree
【解决方案3】:

AT&T 语法和 Intel 语法之间是否存在 1:1 映射?或者是否存在 AT&T 不支持的特定 Intel 命令?

两种语法都可以表达每条 x86 指令的各种形式。任何有效的 x86 机器代码都可以反汇编为 AT&T、MASM 或 NASM 语法。

但存在一些差异,因此映射助记符并不完全是 1:1。例如,在 AT&T 语法中,您必须使用 movabs $0x123456789abcd, %rax 来获取使用 64 位立即数的编码。

在 NASM 语法中,汇编器会根据常量自动选择 mov r64, imm64 编码与 mov r/m32, imm32mov r/m64, sign-extended-imm32 编码。所以mov rax, 1 可能会组合成一个5 字节的mov r32, imm32 或一个7 字节的mov r/m64, imm32(这就是为什么你应该总是写mov eax, 1 以确保你得到更小的编码)。但是mov rax, 0x123456789abcd 总是会组装成 10 字节的mov r64, imm64 形式,而不必使用不同的助记符。

Intel's insn set ref for MOV。 ( 标签 wiki 中的其他链接)。

x86-64.org 的 what's new in x86-64 的这个 archive.org 副本还涵盖了 movabs 与 mov 助记符问题以及其他内容。

【讨论】:

  • 我确实想将 OP 添加到这个特定的子问题中,AT&T 与英特尔的映射基本上是 1:1(因为 x86 机器代码是 x86 机器代码,仍然相同)。但是由于他也在进行 32b -> 64b 转换,他可能会遇到在 64b 模式下不再有效的特定 32b 指令,因此在他的情况下,由于 32->64 位转换,1:1 映射可能会被破坏。