2016-06-04 5 views
0

c++ 프로그램을 가져 와서 어셈블리 (.S) 파일로 컴파일한다고 가정합니다.C++에서 C로 변환

그런 다음 어셈블리 파일을 가져 와서 "dissassemble"을 C으로 만들면 다른 플랫폼에서이 코드를 다시 컴파일 할 수 있습니까?

내가 묻는 이유는 내가 개발하려고하는 플랫폼에 c++ 컴파일러가 없기 때문에 c 컴파일러가 있기 때문입니다.

+0

dis-assembler가 유효한 C 코드를 제공하는 이유는 무엇입니까? 먼저 유효한 C 코드를 제공하는지 확인해야합니다. – SHR

+0

@SHR 내가 걱정하고있는 것은'uint16_t's를'uint8_t's로 바꾸는 것입니다. – DarthRubik

+0

@DarthRubik @DarthRubik이 이것을 체크해보십시오. http://llvm.org/releases/3.1/docs /FAQ.html#translatecxx – SHR

답변

2

예, 설명하는 방식으로 실제로 가능합니다. 아니요, 귀하가 아닌 다른 CPU 아키텍처, OS 및 컴파일러 트리플렛으로 이식 할 수 없습니다.

이유를 알아 보겠습니다.

#include <iostream> 

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

이의는 x86-64의 리눅스 박스에, g++를 사용하여 어셈블러로이 문제를 살펴 보자 ... 몇 가지 기본 C++ 코드를 가지고 ... (I 목적에의 최적화 및 폐기 디버그 기호를 설정)

$ 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 

그 혼란은 우리가 예외 처리, 템플릿 및 네임 스페이스에 대해 지불하는 가격입니다.

/* 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로이 분해하자? 바로 지금입니다. 그러나이 코드는 작동하지 않습니다! 당신은 _ZSo ( std::ostream)을 C struct의 조건에 아직 정의해야합니다. 모든 예외 처리 항목은 물론입니다.

일단 사용을 시작하면 이동이 거의 불가능 해지고 try/throw/catch입니다. 즉, __cxa_allocate_exception 또는 __cxa_throw이 절대 휴대 할 수있는 방법은 없습니다. 이 문제를 해결할 수있는 유일한 방법은 zero-cost exception handling 대신에 setjmp/longjmp 접근법을 예외로 사용하는 모든 프로그램을 다시 작성하는 것입니다 (표준 라이브러리까지 포함하여 모든 라이브러리를 포함합니다!).

마지막으로, 가장 단순한 입력에서도 비 (非) 인체 디스어셈블러가 제대로 작동하지 않을 것입니다. 가장 낮은 수준의 시스템 코드 (예 : 기계어 및 어셈블리 언어)에는 유형에 대한 개념이 없습니다. 레지스터가 부호있는 정수인지, 부호없는 정수인지, 아니면 기본적으로 다른 것이 있는지 절대 알 수 없습니다. 컴파일러는 스택 포인터를 사용하여 작업을 악화시킬 수도 있습니다.

컴파일이 끝나면 일반적으로 런타임에 컴파일러를 필요로하지 않으므로 컴파일러는 나중에 필요한 모든 정보를 지우지 않습니다. 대부분의 경우, 해체하는 것이 실제로 가능할 수는 없으며 다른 솔루션을 찾고있는 경우가 많습니다. 상위 중간 수준 언어에서 낮은 수준 언어로 낮은 중간 수준 언어로 변환하는 극단적 인 걸리고 다른 무엇이 번역 할 수있는 것의 한계에 접근합니다.

+0

라이브러리를 사용하지 않고 모든 것을 직접 쓰는 경우 (printfs 또는 couts 없음)? – DarthRubik

+0

@DarthRubik : 정의에 따르면 표준 라이브러리의 구현은 이식성이 없습니다. 'printf' 자체를 어떻게 완벽하게 이식성있게 구현할 것인가? 방법이 없습니다. 이것이 바로 표준 라이브러리가 존재하는 이유입니다. 그들은 모든 다른 플랫폼에 공통적이고 이식 가능한 인터페이스를 제공합니다. 어쨌든, 당신은 여전히 ​​* 예외적 인 처리를 다루어야합니다. – 3442

+0

이것은 마이크로 컨트롤러 용입니다 (따라서 printfs가 전혀 없을 것입니다). – DarthRubik