Uwaga Z powyższego wyjścia wstępnie przetworzonego pliku, który od naszego programu zażądał stdio.nagłówek h należy włączyć do naszego źródła, które z kolei zażądało całej masy innych plików nagłówkowych.
Kompilacja
następnym krokiem jest wzięcie wstępnie przetworzonego pliku jako wejścia, skompilowanie go i wytworzenie pośredniego skompilowanego wyjścia. Plik wyjściowy dla tego etapu tworzy kod zespołu, który jest zależny od maszyny.
używając flagi” – S ” z gcc możemy przekonwertować wstępnie przetworzony kod źródłowy C na język assembly bez tworzenia pliku obiektowego:
$ gcc -S HelloWorld.i -o HelloWorld.s
chociaż nie jestem zbytnio zainteresowany programowaniem na poziomie montażu, ale szybkie spojrzenie konkluduje, że ten wynik na poziomie montażu jest w jakiejś formie instrukcji, które asembler może zrozumieć i przekształcić go w język na poziomie maszynowym.
assembling
jak wszyscy wiemy ,maszyny mogą zrozumieć tylko język binarny, więc teraz potrzebujemy asemblera, który konwertuje kod assemblingu w „HelloWorld.c ” plik w kodzie binarnym.
asembler był jednym z pierwszych narzędzi programistycznych opracowanych po wynalezieniu komputera cyfrowego.
jeśli są jakieś wywołania funkcji zewnętrznych w kodzie asemblera, asembler pozostawia adresy funkcji zewnętrznych nieokreślone, które zostaną później wypełnione przez łącznik.
asembler można wywołać jak pokazano poniżej. Używając flagi „- c ” w gcc możemy przekonwertować kod złożenia na kod poziomu maszynowego:
$ gcc -c HelloWorld.c -o HelloWorld.o
jedyne co możemy wyjaśnić patrząc na HelloWorld.o plik dotyczy łańcucha ELF w pierwszej linii. Elf oznacza format wykonywalny i linkable.
plik obiektowy i plik wykonywalny są dostępne w kilku formatach, takich jak ELF (Format wykonywalny i łączący) i COFF (wspólny format pliku Obiektowego). Na przykład ELF jest używany w systemach Linux, podczas gdy COFF jest używany w systemach Windows.
jest to stosunkowo nowy format dla plików obiektowych i plików wykonywalnych na poziomie maszynowym, które są produkowane przez gcc. Wcześniej używany był format znany jako.out. Mówi się, że format ELF jest bardziej wyrafinowany niż format.out (możemy zagłębić się w format ELF w innym przyszłym artykule).
jeśli skompilujesz kod bez podania nazwy pliku wyjściowego, utworzony plik wyjściowy ma nazwę 'a.out’, ale format został zmieniony na ELF. Po prostu domyślna nazwa pliku wykonywalnego pozostaje taka sama.
linkowanie
jest to ostatnia faza, w której wszystkie połączenia wywołań funkcji z ich definicjami są wykonywane. Linker wie, gdzie wszystkie te funkcje są zaimplementowane (asembler pozostawił adres wszystkich funkcji zewnętrznych do wywołania). Do tego etapu GCC nie wie o funkcji takiej jak printf ().Asembler pozostawiłby adres funkcji do wywołania, a Linker wykonałby końcowy proces wypełniania tych adresów rzeczywistymi definicjami. Linker wykonuje również za nas kilka dodatkowych zadań. Łączy nasz program ze standardowymi procedurami, które są potrzebne do uruchomienia naszego programu. Tak więc ostateczny Rozmiar pliku wykonywalnego jest znacznie większy niż plik wejściowy!
cały proces łączenia jest obsługiwany przez gcc i wywoływany w następujący sposób:
$ gcc -o Output HelloWorld.c
powyższe polecenie uruchamia plik ” HelloWorld.c „i tworzy końcowy plik wykonywalny „Output”.
jak widać, plik wyjściowy jest domyślnie plikiem wykonywalnym z uprawnieniami-rwxrwxr-x ,oznacza to tylko,że ma uprawnienia wykonywalne dla wszystkich użytkowników(właściciela, grupy i innych). Jeśli uruchomisz ten plik wykonywalny po prostu wpisując ’./ Output ’ otrzymujesz ostateczne wyjście naszego programu !
więc teraz wiemy, jak program C zostanie przekonwertowany na plik wykonywalny . Nieco głębiej zagłębimy się w programowanie C w nadchodzących artykułach. Do tego czasu, szczęśliwej Nauki! 🙂