获取C代码中ASM指令的字节表示形式

时间:2019-01-19 13:50:19

标签: c assembly powerpc

在C代码中,是否有一种方法可以将ASM指令的文本表示形式(如cmpwi r3, 0x20)转换为二进制表示形式(0x2c030020)?

我正在编写将在运行时嵌入到另一个应用程序中的代码。该代码应该更改正在运行的程序的行为/代码。这意味着有一堆这样的代码行:

*((volatile int *)(0x80001234)) = 0x2c030020;

该代码将ASM指令cmpwi r3, 0x20写入0x80001234,从而覆盖该地址上的当前指令。现在,在我的C代码中拥有常量“ 0x2c030020”,而又不知道该怎么做对维护代码不利。因此,我通常会在上面的代码中添加注释,说明ASM指令:// 2c 03 00 20 = cmpwi r3, 0x20

但是,这些有时会不同步。我可能会快速更改整数值而忘了更新评论,或者我可能只是在评论中打错了打字,引起混乱。

有什么办法可以代替我吗? (伪代码)*((volatile int *)(0x80001234)) = asm("cmpwi r3, 0x20");,然后将导致0x2c030020被写入80001234?还是我需要一个在我的C源文件上运行自定义预处理器的骇人解决方案,用其字节码替换ASM指令?

我知道使用asm()函数的内联汇编代码具有C语法,但是它将执行给定的ASM指令,而不是给我它们的二进制表示形式。

1 个答案:

答案 0 :(得分:1)

听起来这很疯狂,但是我想您有充分的理由。没有一点疯狂,生活就没有乐趣。

您可以使用的一种方法是在构建过程中使用汇编器来生成编译时常量。

第一步是制作一个包含每条汇编指令的文件,每行一条。

例如:

cmpwi   3,0x20
addi    3,3,0
blr

将该文件命名为input.def。然后,使用以下shell脚本:

#!/usr/bin/env bash

(cat << HEADER
    .global main
    .text
main:
HEADER
cat input.def) > asm.s

powerpc-linux-gnu-as asm.s -o asm.o

powerpc-linux-gnu-objdump -d asm.o | \
    sed '1,/<main>/ d' | \
    paste -d'\t' - input.def | \
    awk -F'\t' '{
        bytes=$2
        asm=$4
        disasm=$3
        gsub(/ /, "", bytes);
        gsub(/[, ]+/, "_", asm);
        printf("#define ASM_%-20s 0x%s    // disassembly: %s\n", asm, bytes, disasm)
    }'

# Clean temporaries
rm asm.s asm.o

(我在这里使用GNU汇编器和objdump。如果不使用这些工具,则可能需要更改此部分。这里将objdump用作美化的hexdump实用程序。)

此shell脚本:

  1. 创建一个程序集文件
  2. 组装
  3. 将其与input.def并排放置。 (这样可以看到您键入的程序集。)
  4. 重新格式化十六进制,因此它是合法的C常数。重新格式化asm,使其成为合法的C符号。然后,编写一个定义以将指令名称映射到常量。
  5. 将所有这些内容都放入asm.h

这是很多工作,但是您可以在编译时完成所有工作。

这将产生一个名为asm.h的头文件:

#define ASM_cmpwi_3_0x20         0x2c030020    // disassembly: cmpwi   r3,32
#define ASM_addi_3_3_0           0x38630000    // disassembly: addi    r3,r3,0
#define ASM_blr                  0x4e800020    // disassembly: blr

您可以这样使用asm.h文件:

#include "asm.h"
*((volatile int *)(0x80001234)) = ASM_cmpwi_3_0x20;

如果需要新的asm常量,请编辑input.def并重新运行Shell脚本。