Używasz GCC do stworzenia czytelnego zestawu?

256

Zastanawiałem się, jak użyć GCC na moim źródłowym pliku C do zrzucenia mnemonicznej wersji kodu maszynowego, aby zobaczyć, w jaki sposób mój kod jest kompilowany. Możesz to zrobić w Javie, ale nie byłem w stanie znaleźć sposobu w GCC.

Próbuję ponownie napisać metodę C w asemblerze i zobaczenie, jak działa GCC, byłoby bardzo pomocne.

James
źródło
25
zwróć uwagę, że „kod bajtowy” zazwyczaj oznacza kod używany przez maszynę wirtualną, na przykład JVM lub CLR .NET. Dane wyjściowe GCC są lepiej nazywane „kodem maszynowym”, „językiem maszynowym” lub „językiem asemblera”
Javier,
2
Dodałem odpowiedź za pomocą godbolt, ponieważ jest to bardzo potężne narzędzie do szybkiego eksperymentowania z tym, jak różne opcje wpływają na generowanie kodu.
Shafik Yaghmour,
Możliwy duplikat Jak uzyskać wyjście asemblera ze źródła C / C ++ w gcc?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Aby uzyskać więcej wskazówek na temat zapewniania czytelności wyjścia asm, zobacz także: Jak usunąć „szum” z wyjścia zespołu GCC / clang?
Peter Cordes,

Odpowiedzi:

335

Jeśli kompilujesz za pomocą symboli debugowania, możesz użyć objdumpdo uzyskania bardziej czytelnego demontażu.

>objdump --help
[...]
-S, --source             Intermix source code with disassembly
-l, --line-numbers       Include line numbers and filenames in output

objdump -drwC -Mintel jest miłe:

  • -rpokazuje nazwy symboli przy relokacjach (tak byś zobaczył putsw callinstrukcji poniżej)
  • -R pokazuje relokacje dynamiczne / nazwy symboli (przydatne w bibliotekach współdzielonych)
  • -C rozróżnia nazwy symboli C ++
  • -w jest trybem „szerokim”: nie zawija bajtów kodu maszynowego do linii
  • -Mintel: użyj .intel_syntax noprefixskładni podobnej do GAS / binutils MASM zamiast AT&T
  • -S: przeplataj linie źródłowe z demontażem.

Możesz umieścić coś takiego alias disas="objdump -drwCS -Mintel"w swoim~/.bashrc


Przykład:

> gcc -g -c test.c
> objdump -d -M intel -S test.o

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
#include <stdio.h>

int main(void)
{
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   83 e4 f0                and    esp,0xfffffff0
   6:   83 ec 10                sub    esp,0x10
    puts("test");
   9:   c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
  10:   e8 fc ff ff ff          call   11 <main+0x11>

    return 0;
  15:   b8 00 00 00 00          mov    eax,0x0
}
  1a:   c9                      leave  
  1b:   c3                      ret
Bastien Léonard
źródło
3
Czy istnieje przełącznik umożliwiający pobranie tylko instrukcji Intela?
James
3
Wszystkie są instrukcjami firmy Intel, ponieważ działają na procesorach Intel: D.
toto
12
@toto Myślę, że ma na myśli składnię Intela zamiast AT&T
Amok,
7
Możliwe jest zrezygnowanie z pliku obiektu pośredniego za pomocą sekwencji przełączania -Wa,-adhln -g to gcc. Zakłada się, że asemblerem jest gaz i nie zawsze tak jest.
Marc Butler,
8
@James Tak, dostarcz -Mintel.
fuz
106

Jeśli podasz GCC flagę -fverbose-asm, zrobi to

Umieść dodatkowe informacje o komentarzach w wygenerowanym kodzie zestawu, aby były bardziej czytelne.

[...] Dodano komentarze:

  • informacje o wersji kompilatora i opcjach wiersza poleceń,
  • wiersze kodu źródłowego powiązane z instrukcjami montażu, w formie NAZWA PLIKU: LINENUMBER: ZAWARTOŚĆ LINII,
  • wskazówki, w których wyrażenia wysokiego poziomu odpowiadają różnym operandom instrukcji asemblacji.
Kasper
źródło
Ale potem straciłbym cały przełącznik użyty do objdump- objdump -drwCS -Mintel, więc jak mogę użyć czegoś takiego jak verbosez objdump? Tak, że mogę mieć komentarze w kodzie asm, podobnie jak -fverbose-asmw gcc?
Herdsman
1
@Herdsman: nie możesz. Dodatkowe elementy -fverbose-asmdodawane są w postaci komentarzy w składni asm danych wyjściowych, a nie dyrektyw, które dodadzą coś dodatkowego w .opliku. Wszystko jest odrzucane w czasie montażu. Spójrz na dane wyjściowe asm kompilatora zamiast deasemblacji, np. Na godbolt.org, gdzie możesz łatwo dopasować go do linii źródłowej poprzez najechanie myszką i podświetlanie kolorów odpowiednich linii źródła / asm. Jak usunąć „szum” z wyjścia zespołu GCC / clang?
Peter Cordes
75

Użyj przełącznika -S (uwaga: duże S) do GCC, a on wyemituje kod asemblera do pliku z rozszerzeniem .s. Na przykład następujące polecenie:

gcc -O2 -S foo.c

pozostawi wygenerowany kod zestawu w pliku foo.s.

Zgrane prosto z http://www.delorie.com/djgpp/v2faq/faq8_20.html (ale usunięcie błędne -c)

Andrew Keeton
źródło
35
Nie powinieneś mieszać -c i -S, użyj tylko jednego z nich. W tym przypadku jedno zastępuje drugie, prawdopodobnie w zależności od kolejności ich użycia.
Adam Rosenfield,
4
@AdamRosenfield Wszelkie odniesienia do „nie należy mieszać -c i -S”? Jeśli to prawda, możemy przypomnieć autorowi i go edytować.
Tony
5
@Tony: gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#Overall-Options "Możesz użyć ... jednej z opcji -c, -S lub -E, aby powiedzieć, gdzie gcc ma się zatrzymać. „
Nate Eldredge,
1
Jeśli chcesz wszystkie wyjścia pośrednie, użyj gcc -march=native -O3 -save-temps. Nadal możesz użyć, -caby zatrzymać tworzenie pliku obiektowego bez próby łączenia się, czy cokolwiek innego.
Peter Cordes,
2
-save-tempsjest interesujący, ponieważ zrzuca za jednym razem dokładnie wygenerowany kod, podczas gdy druga opcja wywoływania kompilatora -Soznacza dwa razy kompilację i możliwe, że z różnymi opcjami. Ale -save-temps zrzuca wszystko w bieżącym katalogu, co jest dość niechlujne. Wygląda na to, że jest bardziej przeznaczona jako opcja debugowania dla GCC niż narzędzie do sprawdzania kodu.
Stéphane Gourichon
50

Użycie -Sprzełącznika na GCC w systemach opartych na architekturze x86 powoduje domyślny zrzut składni AT&T, którą można określić za pomocą -masm=attprzełącznika:

gcc -S -masm=att code.c

Natomiast jeśli chcesz utworzyć zrzut w składni Intela, możesz użyć -masm=intelprzełącznika w następujący sposób:

gcc -S -masm=intel code.c

(Oba produkują zrzuty code.cdo różnych składni do plikucode.s )

Aby uzyskać podobne efekty za pomocą objdump, powinieneś użyć przykładu --disassembler-options= intel/ attswitch (z zrzutami kodu, aby zilustrować różnice w składni):

 $ objdump -d --disassembler-options=att code.c
 080483c4 <main>:
 80483c4:   8d 4c 24 04             lea    0x4(%esp),%ecx
 80483c8:   83 e4 f0                and    $0xfffffff0,%esp
 80483cb:   ff 71 fc                pushl  -0x4(%ecx)
 80483ce:   55                      push   %ebp
 80483cf:   89 e5                   mov    %esp,%ebp
 80483d1:   51                      push   %ecx
 80483d2:   83 ec 04                sub    $0x4,%esp
 80483d5:   c7 04 24 b0 84 04 08    movl   $0x80484b0,(%esp)
 80483dc:   e8 13 ff ff ff          call   80482f4 <puts@plt>
 80483e1:   b8 00 00 00 00          mov    $0x0,%eax
 80483e6:   83 c4 04                add    $0x4,%esp 
 80483e9:   59                      pop    %ecx
 80483ea:   5d                      pop    %ebp
 80483eb:   8d 61 fc                lea    -0x4(%ecx),%esp
 80483ee:   c3                      ret
 80483ef:   90                      nop

i

$ objdump -d --disassembler-options=intel code.c
 080483c4 <main>:
 80483c4:   8d 4c 24 04             lea    ecx,[esp+0x4]
 80483c8:   83 e4 f0                and    esp,0xfffffff0
 80483cb:   ff 71 fc                push   DWORD PTR [ecx-0x4]
 80483ce:   55                      push   ebp
 80483cf:   89 e5                   mov    ebp,esp
 80483d1:   51                      push   ecx
 80483d2:   83 ec 04                sub    esp,0x4
 80483d5:   c7 04 24 b0 84 04 08    mov    DWORD PTR [esp],0x80484b0
 80483dc:   e8 13 ff ff ff          call   80482f4 <puts@plt>
 80483e1:   b8 00 00 00 00          mov    eax,0x0
 80483e6:   83 c4 04                add    esp,0x4
 80483e9:   59                      pop    ecx
 80483ea:   5d                      pop    ebp
 80483eb:   8d 61 fc                lea    esp,[ecx-0x4]
 80483ee:   c3                      ret    
 80483ef:   90                      nop
amaterasu
źródło
Co do ... gcc -S -masm=intel test.cnie dokładnie pracę dla mnie, mam jakąś krzyżówką Intel i AT & T składni tak: mov %rax, QWORD PTR -24[%rbp], zamiast tego: movq -24(%rbp), %rax.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
1
Dobra wskazówka. Należy zauważyć, że działa to również podczas wykonywania równoległego wyjścia .oplików ASM, tj. Via-Wa,-ahls -o yourfile.o yourfile.cpp>yourfile.asm
underscore_d
Przydałaby się -Mopcja, jest taka sama, --disassembler-optionsale znacznie krótsza, np.objdump -d -M intel a.out | less -N
Eric Wang
34

godbolt jest bardzo przydatnym narzędziem, lista zawiera tylko kompilatory C ++, ale możesz użyć -x cflagi, aby traktować kod jako C. Następnie wygeneruje listę zestawień dla twojego kodu obok siebie i możesz użyć Colouriseopcji generowania kolorowe paski, aby wizualnie wskazać, który kod źródłowy odwzorowuje na wygenerowany zespół. Na przykład następujący kod:

#include <stdio.h>

void func()
{
  printf( "hello world\n" ) ;
}

za pomocą następującego wiersza polecenia:

-x c -std=c99 -O3

i Colourisewygeneruje następujące:

wprowadź opis zdjęcia tutaj

Shafik Yaghmour
źródło
Byłoby miło wiedzieć, jak działają filtry godbolt: .LC0, .text, // i Intel. Intel jest łatwy, -masm=intelale co z resztą?
Z bozonem
Myślę, że wyjaśniono to tutaj stackoverflow.com/a/38552509/2542702
Z bozonem Z
godbolt obsługuje C (wraz z mnóstwem innych języków, takich jak Rust, D, Pascal ...). Po prostu jest o wiele mniej kompilatorów C, więc nadal lepiej jest używać kompilatorów C ++ z-x c
phuclv 27.04.19
23

Czy próbowałeś gcc -S -fverbose-asm -O source.cwtedy zajrzeć do wygenerowanego source.spliku asemblera?

Wygenerowany kod asemblera przechodzi do source.s(możesz go przesłonić przez -o asembler-filename ); -fverbose-asmopcja pyta kompilator emitować kilka uwag asemblera „wyjaśniające” wygenerowany kod asemblera. -OOpcja pyta kompilator optymalizacji trochę (może to zoptymalizować więcej z -O2lub -O3).

Jeśli chcesz zrozumieć, co gccsię dzieje, spróbuj przekazać, -fdump-tree-allale zachowaj ostrożność: otrzymasz setki plików zrzutu.

BTW, GCC to rozszerzalne wtyczki lub MELT (język wysokiego poziomu specyficzny dla domeny, aby rozszerzyć GCC; którego porzuciłem w 2017 roku)

Basile Starynkevitch
źródło
może wspomnieć, że wyjście będzie gotowe source.s, ponieważ wiele osób spodziewałoby się wydruku na konsoli.
RubenLaguna,
1
@ ecerulm: -S -o-zrzuca na standardowe wyjście. -masm=inteljest pomocne, jeśli chcesz użyć składni NASM / YASM. (ale używa qword ptr [mem]raczej niż tylko qword, więc bardziej przypomina Intel / MASM niż NASM / YASM). gcc.godbolt.org porządnie zrzuca zrzut: opcjonalnie usuwa wiersze tylko z komentarzami, nieużywane etykiety i dyrektywy asemblera.
Peter Cordes
2
Zapomniałem wspomnieć: jeśli szukasz „podobnego do źródła, ale bez hałasu przechowywania / przeładowywania po każdej linii źródłowej”, to -Ogjest nawet lepiej niż -O1. Oznacza „optymalizuj do debugowania” i tworzy asm bez zbyt wielu trudnych / trudnych do wykonania optymalizacji, które robią wszystko, co mówi źródło. Jest dostępny od gcc4.8, ale clang 3.7 wciąż go nie ma. IDK, jeśli zdecydowali się przeciw temu lub co.
Peter Cordes
19

Możesz użyć gdb do tego typu objdump.

Ten fragment pochodzi z http://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64


Oto przykład pokazujący mieszane źródło + zestaw dla Intel x86:

  (gdb) disas / m main
Zrzut kodu asemblera dla funkcji main:
5 {
0x08048330: push% ebp
0x08048331: mov% esp,% ebp
0x08048333: poniżej 0x8 $% esp
0x08048336: i 0x 0xfffffff0,% esp
0x08048339: poniżej 0x10 $,% esp

6 printf („Cześć. \ N”);
0x0804833c: movl 0x8048440, (% esp)
0x08048343: zadzwoń pod 0x8048284 

7 zwraca 0;
8}
0x08048348: mov 0x0%,% eax
0x0804834d: wyjdź
0x0804834e: ret

Koniec zrzutu asemblera.
Vishal Sagar
źródło
1
zarchiwizowany
vlad4378
Aby przełączyć deasembler GDB na składnię Intela, użyj set disassembly-flavor intelpolecenia.
Ruslan
13

Użyj przełącznika -S (uwaga: duże S) do GCC, a on wyemituje kod asemblera do pliku z rozszerzeniem .s. Na przykład następujące polecenie:

gcc -O2 -S -c foo.c

codymanix
źródło
4

Nie dałem strzału do gcc, ale w przypadku g ++. Poniższe polecenie działa dla mnie. -g dla kompilacji debugowania i -Wa, -adhln jest przekazywany do asemblera w celu wyświetlenia z kodem źródłowym

g ++ -g -Wa, -adhln src.cpp

DAG
źródło
Działa również dla gcc! -Wa, ... dotyczy opcji wiersza poleceń dla części asemblera (uruchom w gcc / g ++ po kompilacji C / ++). Wywołuje jako wewnętrznie (as.exe w systemie Windows). Zobacz> jako --help jako wiersz poleceń, aby uzyskać więcej pomocy
Hartmut Schorrig
0

użyj -Wa, -adhln jako opcji na gcc lub g ++, aby wygenerować wynik listowania na standardowe wyjście.

-Wa, ... dotyczy opcji wiersza poleceń dla części asemblera (uruchom w gcc / g ++ po kompilacji C / ++). Wywołuje jako wewnętrznie (as.exe w systemie Windows). Widzieć

> as --help

jako wiersz poleceń, aby zobaczyć więcej pomocy dla narzędzia asemblera w gcc

Hartmut Schorrig
źródło