gcc의 C / C ++ 소스에서 어셈블러 출력을 어떻게 얻습니까?


답변:


421

사용 -SGCC (또는 g ++) 옵션을 선택합니다.

gcc -S helloworld.c

그러면 helloworld.c에서 전 처리기 (cpp)가 실행되고 초기 컴파일을 수행 한 다음 어셈블러가 실행되기 전에 중지됩니다.

기본적으로 이것은 파일을 출력합니다 helloworld.s. -o옵션 을 사용하여 출력 파일을 계속 설정할 수 있습니다 .

gcc -S -o my_asm_output.s helloworld.c

물론 원본 소스가있는 경우에만 작동합니다. 결과 객체 파일 만있는 경우 옵션 objdump을 설정하여 --disassemble(또는 -d약식 양식으로)을 사용하는 방법이 있습니다.

objdump -S --disassemble helloworld > helloworld.dump

이 옵션은 객체 파일에 대해 디버깅 옵션이 활성화되어 있고 ( -g컴파일시) 파일이 제거되지 않은 경우에 가장 효과적 입니다.

실행 file helloworld하면 objdump를 사용하여 얻을 수있는 세부 수준에 대한 정보가 제공됩니다.


3
이것이 정확하지만 Cr McDonough의 답변 결과가 더 유용하다는 것을 알았습니다.
Rhys Ulerich

3
추가 사용 : objdump -M intel -S --helloworld> helloworld.dump를 사용하여 Linux에서 nasm과 호환되는 인텔 구문으로 객체 덤프를 가져옵니다.
touchStone

2
당신이 최적화 / 체크에 하나의 함수가있는 경우에, 당신은 온라인 대화 형 C ++ 컴파일러 예에 시도 줄 수 godbolt
fiorentinoing

1
@touchStone : GAS .intel_syntax는 NASM과 호환 되지 않습니다 . 그것은 MASM과 비슷하지만 (예를 들어 주소 mov eax, symbol가있는 NASM과는 달리 부하 mov r32, imm32입니다) MASM과 완전히 호환되지는 않습니다. 특히 NASM 구문으로 작성하려는 경우 읽기 좋은 형식으로 사용하는 것이 좋습니다. objdump -drwC -Mintel | less또는 gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | less유용합니다. GCC / clang 어셈블리 출력에서 ​​"노이즈"를 제거하는 방법 도 참조하십시오 . -masm=intelclang 과도 작동합니다.
Peter Cordes

3
더 나은 사용gcc -O -fverbose-asm -S
Basile Starynkevitch

173

그러면 C 코드 + 줄 번호가 섞인 어셈블리 코드가 생성되어 어떤 줄이 어떤 코드를 생성하는지 쉽게 알 수 있습니다.

# create assembler code:
g++ -S -fverbose-asm -g -O2 test.cc -o test.s
# create asm interlaced with source lines:
as -alhnd test.s > test.lst

에서 발견 프로그래머를위한 알고리즘 , 3 페이지 (PDF 파일의 전체 15 페이지입니다).


3
(실제로 페이지 (번호 매기기) 3 (PDF의 15 번째 페이지)에 있음)
Grumdrig

1
안타깝게도 asOS X에서는 이러한 플래그를 알 수 없습니다. 그렇다면 -Wa옵션을에 전달 하는 데 한 줄로 사용할 수 있습니다 as.
Grumdrig

23
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lst이것의 짧은 버전이 될 것입니다.
legends2k

4
또한 중 하나를 사용할 수 있습니다 gcc -c -g -Wa,-ahl=test.s test.c또는gcc -c -g -Wa,-a,-ad test.c > test.txt
phuclv

1
범례 및 Lu'u와 같은 단일 명령 버전을 포함하여 이것을 자세히 설명 하는 블로그 게시물 이 게시되었습니다. 그런데 왜 -O0? 그것은 값을 추적하기 어렵게하는로드 / 스토어들로 가득 차 있으며, 최적화 된 코드가 얼마나 효율적인지에 대해서는 아무 것도 알려주지 않습니다.
Peter Cordes

51

다음 명령 줄은 Christian Garbin의 블로그에 있습니다.

g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt

암시 적 캐스트가 포함 된 루틴에 대해 Win-XP의 DOS 창에서 G ++을 실행했습니다.

c:\gpp_code>g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
horton_ex2_05.cpp: In function `int main()':
horton_ex2_05.cpp:92: warning: assignment to `int' from `double'

출력은 원래 C ++ 코드와 반복되는 어셈블 된 생성 코드입니다 (C ++ 코드는 생성 된 asm 스트림에서 주석으로 표시됨)

  16:horton_ex2_05.cpp **** using std::setw;
  17:horton_ex2_05.cpp ****
  18:horton_ex2_05.cpp **** void disp_Time_Line (void);
  19:horton_ex2_05.cpp ****
  20:horton_ex2_05.cpp **** int main(void)
  21:horton_ex2_05.cpp **** {
 164                    %ebp
 165                            subl $128,%esp
?GAS LISTING C:\DOCUME~1\CRAIGM~1\LOCALS~1\Temp\ccx52rCc.s
166 0128 55                    call ___main
167 0129 89E5          .stabn 68,0,21,LM2-_main
168 012b 81EC8000      LM2:
168      0000
169 0131 E8000000      LBB2:
169      00
170                    .stabn 68,0,25,LM3-_main
171                    LM3:
172                            movl $0,-16(%ebp)

@ 팔라딘-반드시 그런 것은 아닙니다. OP는 C / C ++ 소스 코드와 동등한 어셈블러 출력을 얻는 것에 관한 것입니다. 목록을 가져옵니다. 이는 컴파일러와 최적화 프로그램이 수행하는 작업을 이해하는 데 더 유용하다는 데 동의합니다. 그러나 행 번호를 기대하지 않고 어셈블러 자체가 barf를 일으키고 어셈블리 명령에서 남은 바이트를 컴파일했습니다.
Jesse Chisholm

적어도 사용 -O2, 또는 어떤 최적화 프로젝트를 빌드 할 때 GCC가 코드를 최적화하는 방법을보고 싶다면 당신이 실제로 사용하는 옵션을 제공합니다. (또는 LTO를 사용한다면, 원하는대로 링커 출력을 분해해야합니다.)
Peter Cordes

27

-S 스위치 사용

g++ -S main.cpp

또는 gcc와 함께

gcc -S main.c

또한 이것을보십시오


7
FAQ를 확인하십시오. "자신의 프로그래밍 질문을하고 대답하는 것도 좋습니다." 요점은 이제 StackOverflow가 Q & A를 다른 사람들을위한 자원으로 포함한다는 것입니다.
Steve Jessop

때로는 다른 사람이 와서 더 나은 대답으로 당신을 놀라게 할 것입니다. 그러나 때때로 내 것이 조금 장황 할 수도 있습니다 ...
PhirePhly

자신 만의 질문에 대한 답변 단추도 있습니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

13

당신이보고 싶은 것이 출력의 링크에 달려 있다면, 출력 객체 파일 / 실행 파일에 대한 objdump도 위에서 언급 한 gcc -S 외에도 유용 할 수 있습니다. Loren Merritt의 기본 objdump 구문을 읽기 쉬운 nasm 구문으로 변환하는 매우 유용한 스크립트는 다음과 같습니다.

#!/usr/bin/perl -w
$ptr='(BYTE|WORD|DWORD|QWORD|XMMWORD) PTR ';
$reg='(?:[er]?(?:[abcd]x|[sd]i|[sb]p)|[abcd][hl]|r1?[0-589][dwb]?|mm[0-7]|xmm1?[0-9])';
open FH, '-|', '/usr/bin/objdump', '-w', '-M', 'intel', @ARGV or die;
$prev = "";
while(<FH>){
    if(/$ptr/o) {
        s/$ptr(\[[^\[\]]+\],$reg)/$2/o or
        s/($reg,)$ptr(\[[^\[\]]+\])/$1$3/o or
        s/$ptr/lc $1/oe;
    }
    if($prev =~ /\t(repz )?ret / and
       $_ =~ /\tnop |\txchg *ax,ax$/) {
       # drop this line
    } else {
       print $prev;
       $prev = $_;
    }
}
print $prev;
close FH;

나는 이것이 gcc -S의 출력에도 사용될 수 있다고 생각합니다.


2
여전히이 스크립트는 구문을 완전히 변환하지 않는 더티 해킹입니다. 예를 들어 mov eax,ds:0x804b794NASMish는 아닙니다. 또한, 때로는 그냥 유용한 정보를 제거합니다 : movzx eax,[edx+0x1]메모리가 피연산자인지 추측 독자 잎 byte이나 word.
Ruslan

우선 NASM 구문으로 분해하려면 Agner Fog 's를 사용하십시오objconv . 출력 파일 =을 사용하여 stdout으로 분해 /dev/stdout할 수 있으므로 파이프를 less통해 볼 수 있습니다. 이 ndisasm있지만 플랫 바이너리 만 분해하고 객체 파일 (ELF / PE)에 대해 알지 못합니다.
Peter Cordes

9

모든 사람들이 지적했듯이 -SGCC 옵션을 사용하십시오 . 또한 최적화 옵션 추가 여부에 따라 결과가 다를 수 있음을 추가하고 싶습니다 -O0.-O2 적극적인 최적화의 경우).

특히 RISC 아키텍처에서 컴파일러는 최적화를 수행 할 때 인식을 훨씬 뛰어 넘는 코드를 종종 변환합니다. 결과를 보는 것은 인상적이고 매력적입니다!


9

모두가 말했듯이 -S 옵션을 사용하십시오. -save-temps 옵션을 사용하면 사전 처리 된 파일 ( .i), 어셈블리 파일 ( .s) 및 객체 파일 (* .o)을 얻을 수도 있습니다 . (-E, -S 및 -c를 사용하여 각각 가져 오기)


8

앞에서 언급했듯이 -S 플래그를보십시오.

또한 '-fdump-tree'플래그 계열, 특히 '-fdump-tree-all'을 보면 gcc의 중간 형식을 볼 수 있습니다. 이것들은 종종 어셈블러보다 더 읽기 쉽기 때문에 (최소한 나에게) 최적화 패스가 어떻게 수행되는지 볼 수 있습니다.




8

-save-temps

이것은 https://stackoverflow.com/a/17083009/895245 에서 언급 되었지만 더 자세히 설명하겠습니다.

이 옵션의 가장 큰 장점은 -S빌드 자체를 많이 방해하지 않고 빌드 스크립트에 추가하기가 매우 쉽다는 것입니다.

할 때 :

gcc -save-temps -c -o main.o main.c

main.c

#define INC 1

int myfunc(int i) {
    return i + INC;
}

이제 일반 출력 외에도 main.o현재 작업 디렉토리에는 다음 파일도 포함됩니다.

  • main.i 보너스이며 사전 제안 된 파일을 포함합니다.

    # 1 "main.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 31 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 32 "<command-line>" 2
    # 1 "main.c"
    
    
    int myfunc(int i) {
        return i + 1;
    }
  • main.s 원하는 생성 된 어셈블리를 포함합니다.

        .file   "main.c"
        .text
        .globl  myfunc
        .type   myfunc, @function
    myfunc:
    .LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    $1, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE0:
        .size   myfunc, .-myfunc
        .ident  "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
        .section    .note.GNU-stack,"",@progbits

많은 파일에 대해 수행하려면 대신 사용을 고려하십시오.

 -save-temps=obj

중간 파일을 -o현재 작업 디렉토리 대신 오브젝트 출력 과 동일한 디렉토리에 저장 하므로 잠재적 인 기본 이름 충돌을 피할 수 있습니다.

이 옵션에 대한 또 다른 멋진 점은 다음을 추가하는 것입니다 -v.

gcc -save-temps -c -o main.o -v main.c

실제로 추악한 임시 파일 대신 사용되는 명시 적 파일을 표시 /tmp하므로 사전 처리 / 컴파일 / 어셈블리 단계를 포함하여 진행중인 작업을 정확하게 알 수 있습니다.

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s

우분투 19.04 amd64, GCC 8.3.0에서 테스트되었습니다.




3

이 commnads의 출력

Windows에서 C 프로그램의 어셈블리 코드를 보거나 인쇄하는 단계는 다음과 같습니다.

콘솔 / 터미널 / 명령 프롬프트 :

  1. 코드 블록과 같은 C 코드 편집기에서 C 프로그램을 작성하고 확장자를 .c로 저장하십시오.

  2. 컴파일하고 실행하십시오.

  3. 성공적으로 실행되면 gcc 컴파일러를 설치 한 폴더로 이동하여

    '.c'파일의 '.s'파일을 가져 오는 명령

    C : \ gcc> gcc -S C 파일의 완전한 경로 ENTER

    예제 명령 (내 경우와 같이)

    C : \ gcc> gcc -SD : \ Aa_C_Certified \ alternate_letters.c

    원본 '.c'파일의 '.s'파일이 출력됩니다.

4. 이 후 다음 명령을 입력하십시오

C; \ gcc> cpp 파일 이름 .s ENTER

예제 명령 (내 경우와 같이)

C; \ gcc> cpp alternate_letters.s

C 프로그램의 전체 어셈블리 언어 코드를 인쇄 / 출력합니다.


2

"-S"를 옵션으로 사용하십시오. 터미널의 어셈블리 출력을 표시합니다.


터미널에 표시하려면을 사용하십시오 gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less. -S자체적으로 생성 foo.s합니다.
Peter Cordes

2

최근에 나는 프로그램에서 각 기능의 어셈블리를 알고 싶었
습니다.

$ gcc main.c                      // main.c source file
$ gdb a.exe                       // gdb a.out in linux
  (gdb) disass main               // note here main is a function
                                  // similary it can be done for other functions

2

gcc를 사용하는 C 솔루션은 다음과 같습니다.

gcc -S program.c && gcc program.c -o output
  1. 여기서 첫 번째 부분은 Program과 동일한 파일 이름으로 프로그램의 어셈블리 출력을 저장하지만 확장자가 .s 인 다른 일반 텍스트 파일로 열 수 있습니다.

  2. 두 번째 부분은 실제 사용을 위해 프로그램을 컴파일하고 지정된 파일 이름으로 프로그램의 실행 파일을 생성합니다.

그만큼 program.c에 위의 사용은 프로그램의 이름 출력 생성하려는 실행 파일의 이름입니다.

BTW StackOverFlow의 첫 번째 게시물입니다 :-}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.