是的,按照您描述的方式确实有可能。不,它不能移植到除您之外的任何 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。
最后但并非最不重要的一点是,即使是最简单的输入,非人类反汇编程序也很可能无法正确执行此操作。请记住,在最低级别的东西(也就是机器代码和汇编语言)中,没有类型的概念。例如,您永远无法知道寄存器是有符号整数还是无符号整数,或者基本上是其他任何东西。编译器还可以随意使用堆栈指针,从而使工作变得更糟。
一旦编译结束,编译器不期待任何将来的反汇编,会清除所有这些宝贵的信息,因为您通常在运行时不需要它。在大多数情况下,如果可能的话,拆卸真的不值得,而且很可能您正在寻找不同的解决方案。从高级中级语言到低级语言再到中低级语言的翻译,将这一点发挥到了极致,接近了可以翻译成其他语言的极限。