【问题标题】:Convert C++ to C [duplicate]将 C++ 转换为 C [重复]
【发布时间】:2016-06-05 07:41:59
【问题描述】:

假设我在哪里获取c++ 程序并将其编译为程序集 (.S) 文件。

然后我将那个汇编文件“反汇编”到C,该代码是否可以在不同的平台上重新编译?

我问这个的原因是我尝试开发的平台没有c++ 编译器,它确实有一个c 编译器。

【问题讨论】:

  • 如果你的反汇编程序给出了有效的 c 代码,为什么不呢?但首先您需要验证它是否提供了有效的 c 代码。
  • @SHR 我担心的是它会将uint16_ts 变成uint8_ts 之类的东西
  • @DarthRubik 看看这个,这可能是一个更好的解决方案:llvm.org/releases/3.1/docs/FAQ.html#translatecxx
  • 没有明确定义的从程序集到 C 的映射。您必须具体说明什么程序集被什么工具“反汇编”。
  • 一般来说:不要使用未在您的目标平台上实现的编程语言。

标签: c++ c decompiling


【解决方案1】:

是的,按照您描述的方式确实有可能。不,它不能移植到除您之外的任何 CPU 架构、操作系统和编译器三元组。

让我们看看为什么。拿一些基本的 C++ 代码...

#include <iostream>

int main()
{
    std::cout << "Hello, world!\n";
    return 0;
}

让我们在 x86-64 Linux 机器上使用 g++ 将其转换为汇编程序(我打开了优化,并故意丢弃了调试符号)...

$ g++ -o test.s -O3 -S test.cpp

结果是……

    .file   "test.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "Hello, world!\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB1027:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $.LC0, %esi
    movl    $_ZSt4cout, %edi
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE1027:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .section    .text.unlikely
.LCOLDB2:
    .section    .text.startup
.LHOTB2:
    .p2align 4,,15
    .type   _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1032:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1032:
    .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
    .section    .text.unlikely
.LCOLDE2:
    .section    .text.startup
.LHOTE2:
    .section    .init_array,"aw"
    .align 8
    .quad   _GLOBAL__sub_I_main
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .hidden __dso_handle
    .ident  "GCC: (GNU) 5.3.1 20151207 (Red Hat 5.3.1-2)"
    .section    .note.GNU-stack,"",@progbits

这种混乱是我们为异常处理、模板和命名空间付出的代价。让我们手动将其分解为 C,丢弃异常处理表以获得更清晰的视图...

/* std::ostream */
typedef struct
{
    /* ... */
} _ZSo;

/* extern operator<<(std::basic_ostream&, const char*); */
extern _ZStlsIst11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc();

/* namespace std { extern ostream cout; } */
extern _ZSo _ZSt4cout;

/* Our string, of course! */
static const char* LC0 = "Hello, world!\n";

int main()
{
    _ZStlsIst11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(&_ZSt4cout, LC0);
    return 0;
}

看起来不可读,但可移植,对吧?现在,它是。但是,这段代码不起作用!您需要用 C struct 的术语定义 _ZSo (std::ostream),更不用说包括所有异常处理的东西了。

一旦您开始使用try/throw/catch,就几乎不可能实现便携。我的意思是,__cxa_allocate_exception__cxa_throw 绝对不可能是可移植的!解决此问题的唯一方法是重写所有程序(包括您使用的所有库,甚至是标准库!)以使用较慢的 setjmp/longjmp 方法处理异常而不是 zero-cost exception handling

最后但并非最不重要的一点是,即使是最简单的输入,非人类反汇编程序也很可能无法正确执行此操作。请记住,在最低级别的东西(也就是机器代码和汇编语言)中,没有类型的概念。例如,您永远无法知道寄存器是有符号整数还是无符号整数,或者基本上是其他任何东西。编译器还可以随意使用堆栈指针,从而使工作变得更糟。

一旦编译结束,编译器不期待任何将来的反汇编,会清除所有这些宝贵的信息,因为您通常在运行时不需要它。在大多数情况下,如果可能的话,拆卸真的不值得,而且很可能您正在寻找不同的解决方案。从高级中级语言到低级语言再到中低级语言的翻译,将这一点发挥到了极致,接近了可以翻译成其他语言的极限。

【讨论】:

  • 如果我不使用任何库并自己编写所有内容(没有 printfs 或 couts)怎么办?
  • @DarthRubik:根据定义,标准库的实现是不可移植的。您将如何以完全可移植的方式实现printf 本身?不可能。这就是标准库首先存在的原因。它们为所有不同的平台提供了一个通用的、可移植的界面。无论如何,你仍然需要处理异常处理这些东西。
  • 这是一个微控制器(所以我根本没有任何 printfs)
  • 在微控制器上,我永远不会抛出异常
  • @DarthRubik:哦,太好了。所以,如果你在一个嵌入式环境中,你不应该那么关心便携性。在这种情况下,只需手动重写代码。如果没有 C++ 编译器,你可能甚至没有 C 标准库,不是吗?
猜你喜欢
  • 1970-01-01
  • 2016-09-06
  • 2012-07-01
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-01
  • 2019-06-24
相关资源
最近更新 更多