GCC의 C/C ++ 소스에서 어셈블러 출력을 어떻게 얻습니까?
문제
이것을 어떻게합니까?
무언가가 어떻게 컴파일되는지 분석하려면 어떻게 방출 된 어셈블리 코드를 얻을 수 있습니까?
해결책
사용 -S
GCC 옵션 (또는 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를 사용하여 얻을 수있는 세부 사항 수준에 대한 표시를 제공합니다.
다른 팁
이렇게하면 C 코드 + 줄 번호가 연결되어 ASM 코드가 생성되어 어떤 줄이 어떤 코드를 생성하는지 더 쉽게 알 수 있습니다.
# create assembler code:
c++ -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 번째 페이지).
다음 명령 줄은 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)
보고 싶은 것이 출력의 링크에 의존하는 경우, 출력 개체 파일/실행 파일의 OBJDUMP는 위에서 언급 한 GCC -S 외에 유용 할 수 있습니다. 다음은 Loren Merritt의 매우 유용한 스크립트입니다. 기본 OBJDUMP SYNTAX를보다 읽기 쉬운 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의 출력에도 사용할 수 있다고 생각합니다.
모두가 말했듯이 -S 옵션을 사용하십시오. -save -temps 옵션을 사용하는 경우 전처리 파일도 얻을 수도 있습니다 (.i), 어셈블리 파일 (.S) 및 객체 파일 (*. o). (-e, -s 및 -c를 사용하여 각각을 얻으십시오.)
모두가 지적했듯이 사용하십시오 -S
GCC 옵션. 또한 최적화 옵션을 추가할지 여부에 따라 결과가 다를 수 있다고 덧붙이고 싶습니다.-O0
없음, -O2
Agressive 최적화를 위해).
특히 RISC 아키텍처에서 컴파일러는 종종 최적화를 수행 할 때 인식을 넘어서 코드를 거의 변환합니다. 결과를 보는 것은 인상적이고 매력적입니다!
앞에서 언급했듯이 -s 플래그를보십시오.
또한 '-fdump-tree'깃발, 특히 '-fdump-tree-all'제품군을 보면 GCC의 중간 형태를 볼 수 있습니다. 이것들은 종종 어셈블러보다 더 읽기 쉬울 수 있으며 (적어도 나에게) 최적화가 어떻게 수행되는지 볼 수 있습니다.
-s 옵션을 사용합니다.
gcc -S program.c
LLVM 어셈블리를 찾고 있다면 :
llvm-gcc -emit-llvm -S hello.c
질문이 2008 년 이었기 때문에 답들 사이에 이러한 가능성이 보이지 않지만 2018 년에는 Matt Goldbolt의 온라인 웹 사이트를 사용할 수 있습니다. https://godbolt.org
당신은 또한 현지에서 클론을하고 그의 프로젝트를 실행할 수 있습니다. https://github.com/mattgodbolt/compiler-explorer
에서: http://www.delorie.com/djgpp/v2faq/faq8_20.html
gcc -c -g -wa, -a, -ad [기타 GCC 옵션] foo.c> foo.lst
Phirephly의 대답을 대안하거나 모든 사람이 말했듯이 -S를 사용합니다.
다음은 Windows에서 C 프로그램의 어셈블리 코드를보고 인쇄하는 단계입니다.
콘솔 / 터미널 / 명령 프롬프트 :
CodeBlocks와 같은 C 코드 편집기에 C 프로그램을 작성하고 Extention .C로 저장하십시오.
컴파일하고 실행하십시오.
성공적으로 실행되면 GCC 컴파일러를 설치 한 폴더로 이동하여
'.c'파일의 '.s'파일을 얻으려면 명령을 따릅니다.
C : gcc> gcc-s C 파일의 완전한 경로 Enter
예제 명령 (내 경우와 같이)
C : gcc> gcc -sd : aa_c_certified anllate_letters.c
이것은 원본 '.c'파일의 '.s'파일을 출력합니다.
4. 그런 다음 다음 명령을 입력하십시오
c; gcc> cpp filename.s는 입력합니다
예제 명령 (내 경우와 같이)
C; GCC> CPP Alternate_letters.s
이것은 C 프로그램의 전체 어셈블리 언어 코드를 인쇄/출력합니다.
"-s"를 옵션으로 사용하십시오. 터미널에 어셈블리 출력을 표시합니다.
최근에 나는 프로그램에서 각 기능의 조립을 알고 싶었습니다.
이것이 내가 한 방법입니다.
$ 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
-save-temps
이것은 언급되었습니다 https://stackoverflow.com/a/17083009/895245 그러나 더 자세히 설명하겠습니다. 할 때 :
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
현재 작업 디렉토리 대신 객체 출력이므로 잠재적 인베이스 이름 충돌을 피합니다.
이 옵션의 장점 -S
빌드 자체를 많이 방해하지 않고 빌드 스크립트에 추가하기가 쉽습니다.
이 옵션에 대한 또 다른 멋진 점은 추가하는 것입니다. -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에서 테스트.
다음은 GCC를 사용하는 C를위한 솔루션입니다.
gcc -S program.c && gcc program.c -o output
여기서 첫 번째 부분은 프로그램의 어셈블리 출력을 프로그램과 동일한 파일 이름으로 저장하지만 변경 사항이 있습니다. .에스 확장자, 일반 텍스트 파일로 열 수 있습니다.
두 번째 부분은 실제 사용을 위해 프로그램을 컴파일하고 지정된 파일 이름으로 프로그램에 대한 실행 파일을 생성합니다.
그만큼 program.c 위에 사용 된 프로그램의 이름은 다음과 같습니다 산출 생성하려는 실행 파일의 이름입니다.
btw 그것은 stackoverflow에 대한 나의 첫 번째 게시물입니다 :-}