Odpowiedzi:
Użyj -S
opcji gcc (lub g ++).
gcc -S helloworld.c
Spowoduje to uruchomienie preprocesora (cpp) na helloworld.c, wykonanie wstępnej kompilacji, a następnie zatrzymanie przed uruchomieniem asemblera.
Domyślnie spowoduje to wyświetlenie pliku helloworld.s
. Plik wyjściowy można nadal ustawić za pomocą -o
opcji.
gcc -S -o my_asm_output.s helloworld.c
Oczywiście działa to tylko wtedy, gdy masz oryginalne źródło. Alternatywą, jeśli masz tylko wynikowy plik obiektowy, jest użycie objdump
, ustawiając --disassemble
opcję (lub -d
w formie skróconej).
objdump -S --disassemble helloworld > helloworld.dump
Ta opcja działa najlepiej, jeśli dla pliku obiektowego ( -g
w czasie kompilacji) włączona jest opcja debugowania, a plik nie został usunięty.
Uruchomienie file helloworld
da ci pewne wskazówki co do poziomu szczegółowości, który uzyskasz za pomocą objdump.
.intel_syntax
jest kompatybilny z NASM . Jest bardziej podobny do MASM (np. mov eax, symbol
Jest obciążeniem, w przeciwieństwie do NASM, gdzie jest mov r32, imm32
adresem), ale też nie jest w pełni kompatybilny z MASM. Bardzo polecam to jako przyjemny format do czytania, szczególnie jeśli lubisz pisać w składni NASM. objdump -drwC -Mintel | less
lub gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | less
są przydatne. (Zobacz także Jak usunąć „szum” z danych wyjściowych zespołu GCC / clang? ). -masm=intel
działa również z clang.
gcc -O -fverbose-asm -S
Spowoduje to wygenerowanie kodu zestawu z przeplatanym kodem C + numery linii, aby łatwiej zobaczyć, które linie generują jaki kod:
# 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
Znalezione w Algorytmy dla programistów , strona 3 (co stanowi ogólną 15 stronę pliku PDF).
as
w systemie OS X te flagi nie są znane. Gdyby tak było, prawdopodobnie można by to zrobić w jednym wierszu, używając -Wa
opcji do przekazania opcji as
.
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lst
byłaby to krótka wersja tego.
gcc -c -g -Wa,-ahl=test.s test.c
albogcc -c -g -Wa,-a,-ad test.c > test.txt
-O0
? Jest pełen ładunków / sklepów, które utrudniają śledzenie wartości i nie mówią nic o wydajności zoptymalizowanego kodu.
Następująca linia poleceń pochodzi z bloga Christiana Garbina
g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
Uruchomiłem G ++ z okna DOS na Win-XP, z procedurą zawierającą ukrytą obsadę
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'
Dane wyjściowe są zestawione wygenerowany kod iterowany z oryginalnym kodem C ++ (kod C ++ jest wyświetlany jako komentarze w wygenerowanym strumieniu 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)
-O2
Jeśli chcesz zobaczyć, jak gcc optymalizuje kod, użyj przynajmniej lub jakiejkolwiek opcji optymalizacji, której faktycznie używasz podczas budowania projektu. (Lub jeśli używasz LTO, tak jak powinieneś, to musisz zdemontować wyjście linkera, aby zobaczyć, co naprawdę dostajesz.)
Jeśli to, co chcesz zobaczyć, zależy od połączenia wyniku, wówczas objdump na pliku wynikowym pliku / pliku wykonywalnym może być również przydatny jako dodatek do wyżej wspomnianego gcc -S. Oto bardzo przydatny skrypt autorstwa Lorena Merritta, który konwertuje domyślną składnię objdump na bardziej czytelną składnię 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;
Podejrzewam, że można tego również użyć na wyjściu gcc -S.
mov eax,ds:0x804b794
Nie jest bardzo NASMish. Czasami po prostu usuwa przydatne informacje: movzx eax,[edx+0x1]
pozostawia czytelnikowi odgadnięcie, czy operand pamięci był, byte
czy też word
.
objconv
. Możesz go zdemontować na standardowe wyjście z plikiem wyjściowym = /dev/stdout
, dzięki czemu możesz przesyłać strumieniowo do less
oglądania. Jest też ndisasm
, ale dezasembluje tylko płaskie pliki binarne i nie wie o plikach obiektowych (ELF / PE).
Jak wszyscy zauważyli, skorzystaj z -S
opcji GCC. Chciałbym również dodać, że wyniki mogą się różnić (szalenie!) W zależności od tego, czy dodasz opcje optymalizacji (w -O0
przypadku braku, w -O2
przypadku agresywnej optymalizacji).
W szczególności na architekturach RISC kompilator często przekształca kod niemal nie do poznania w procesie optymalizacji. Spojrzenie na wyniki jest imponujące i fascynujące!
Jak wspomniano wcześniej, spójrz na flagę -S.
Warto również przyjrzeć się rodzinie flag „-fdump-tree”, w szczególności „-fdump-tree-all”, która pozwala zobaczyć niektóre pośrednie formy gcc. Często mogą być bardziej czytelne niż asembler (przynajmniej dla mnie) i pozwalają zobaczyć, jak przebiegają optymalizacje.
Nie widzę takiej możliwości wśród odpowiedzi, prawdopodobnie dlatego, że pytanie pochodzi z 2008 r., Ale w 2018 r. Możesz skorzystać ze strony internetowej Matta Goldbolta https://godbolt.org
Możesz także lokalnie uzyskać klon i uruchomić jego projekt https://github.com/mattgodbolt/compiler-explorer
-save-temps
Zostało to wspomniane na https://stackoverflow.com/a/17083009/895245 ale pozwólcie, że jeszcze to zilustruję.
Dużą zaletą tej opcji -S
jest to, że bardzo łatwo jest dodać ją do dowolnego skryptu kompilacji, bez ingerowania w samą kompilację.
Kiedy to zrobisz:
gcc -save-temps -c -o main.o main.c
main.c
#define INC 1
int myfunc(int i) {
return i + INC;
}
a teraz, poza normalnym wyjściem main.o
, bieżący katalog roboczy zawiera również następujące pliki:
main.i
jest premią i zawiera wstępnie przejęty plik:
# 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
zawiera żądany wygenerowany zespół:
.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
Jeśli chcesz to zrobić dla dużej liczby plików, rozważ użycie zamiast tego:
-save-temps=obj
który zapisuje pliki pośrednie w tym samym katalogu, co -o
obiekt wyjściowy zamiast w bieżącym katalogu roboczym, unikając w ten sposób potencjalnych konfliktów nazw basename.
Kolejną fajną rzeczą w tej opcji jest dodanie -v
:
gcc -save-temps -c -o main.o -v main.c
w rzeczywistości pokazuje jawne pliki używane zamiast brzydkich tymczasowych plików /tmp
, więc łatwo jest dokładnie wiedzieć, co się dzieje, co obejmuje etapy wstępnego przetwarzania / kompilacji / montażu:
/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
Testowane w Ubuntu 19.04 amd64, GCC 8.3.0.
Użyj opcji -S:
gcc -S program.c
Od: http://www.delorie.com/djgpp/v2faq/faq8_20.html
gcc -c -g -Wa, -a, -ad [inne opcje GCC] foo.c> foo.lst
zamiast odpowiedzi PhirePhly Lub po prostu użyj -S, jak wszyscy mówili.
Oto kroki, aby zobaczyć / wydrukować kod asemblera dowolnego programu C w systemie Windows
konsola / terminal / wiersz poleceń:
Napisz program C w edytorze kodu C, takim jak bloki kodu i zapisz go z rozszerzeniem .c
Skompiluj i uruchom.
Po pomyślnym uruchomieniu przejdź do folderu, w którym zainstalowałeś kompilator gcc i podaj
następujące polecenie, aby uzyskać plik „.s” pliku „.c”
C: \ gcc> gcc -S pełna ścieżka do pliku C. ENTER
Przykładowe polecenie (jak w moim przypadku)
C: \ gcc> gcc -SD: \ Aa_C_Certified \ alternate_letters.c
Spowoduje to wygenerowanie pliku „.s” oryginalnego pliku „.c”
4 Następnie wpisz następujące polecenie
C; \ gcc> cpp filename.s ENTER
Przykładowe polecenie (jak w moim przypadku)
C; \ gcc> cpp alternate_letters.s
Spowoduje to wydrukowanie / wydrukowanie całego kodu języka asemblera twojego programu C.
Jako opcji użyj „-S”. Wyświetla dane wyjściowe zestawu w terminalu.
gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less
. -S
sam tworzy foo.s
.
Niedawno chciałem poznać zestawienie każdej funkcji w programie.
Tak to zrobiłem.
$ 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
Oto rozwiązanie dla C przy użyciu gcc:
gcc -S program.c && gcc program.c -o output
Tutaj pierwsza część przechowuje dane wyjściowe asemblera programu pod tą samą nazwą pliku, co Program, ale ze zmienionym rozszerzeniem .s , można go otworzyć jak każdy zwykły plik tekstowy.
Druga część tutaj kompiluje twój program do faktycznego użycia i generuje plik wykonywalny dla twojego programu o określonej nazwie pliku.
Program.c użyte powyżej to nazwa programu i wyjście to nazwa pliku wykonywalnego chcesz wygenerować.
BTW To mój pierwszy post na StackOverFlow: -}