This shows you the differences between two versions of the page.
iocla:laboratoare:laborator-04 [2021/10/31 19:16] ionut.mihalache1506 |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Laborator 04: Crearea și analiza unui executabil ====== | ||
- | <HTML> | ||
- | <!-- Convert to DokuWiki format using Pandoc: pandoc -f markdown_github-hard_line_breaks -t dokuwiki README.md --> | ||
- | |||
- | </HTML> | ||
- | ===== Assembly / Asamblare ===== | ||
- | |||
- | Assembly / Asamblare este penultima etapă a procesului de compilare în sens larg. | ||
- | În urma finalizării acestei etape rezultatul va fi crearea a unul sau mai multe fișiere obiect. Fișierele obiect pot conține mai multe lucruri, | ||
- | printre care: | ||
- | |||
- | - nume de simboluri | ||
- | - constante folosite în cadrul programului | ||
- | - cod compilat | ||
- | - valori de tip import/export care vor fi „rezolvate” în etapa de linking | ||
- | |||
- | Pentru a crea un fișier obiect în cazul în care se utilizează compilatorul ''%%gcc%%'' se folosește opțiunea ''%%-c%%'' așa cum puteți vedea și în secțiunea [[#invocarea-linker-ului|Invocarea linker-ului]] de mai jos. | ||
- | |||
- | ===== Linking / Legare ===== | ||
- | |||
- | Linking / Legare este ultima etapă a procesului de compilare în sens larg. | ||
- | La finalul acestei etape va rezulta un fișier executabil prin unificarea(„legarea”) mai multor fișiere obiect care pot avea la bază limbaje de | ||
- | programare de nivel înalt diferite; tot ceea ce contează este ca fișierele obiect să fie create în mod corespunzător pentru ca linker-ul să le | ||
- | poată „interpreta”. | ||
- | |||
- | Pentru a obține un fișier executabil din fișiere obiect, linker-ul realizează următoarele acțiuni: | ||
- | |||
- | - rezolvarea simbolurilor (//symbol resolution//): localizarea simbolurilor nedefinite ale unui fișier obiect în alte fișiere obiect | ||
- | - unificarea secțiunilor: unificarea secțiunilor de același tip din diferite fișiere obiect într-o singură secțiune în fișierul executabil | ||
- | - stabilirea adreselor secțiunilor și simbolurilor (//address binding//): după unificare se pot stabili adresele efective ale simbolurilor în cadrul fișierului executabil | ||
- | - relocarea simbolurilor (//relocation//): odată stabilite adresele simbolurilor, trebuie actualizate, în executabil, instrucțiunile și datele care referă adresele acelor simboluri | ||
- | - stabilirea unui punct de intrare în program (//entry point//): adică adresa primei instrucțiuni ce va fi executată | ||
- | |||
- | ===== Invocarea linker-ului ===== | ||
- | |||
- | Linker-ul este, în general, invocat de utilitarul de compilare (''%%gcc%%'', ''%%clang%%'', ''%%cl%%''). | ||
- | Astfel, invocarea linker-ului este transparentă utilizatorului. | ||
- | În cazuri specifice, precum crearea unei imagini de kernel sau imagini pentru sisteme încorporate, utilizatorul va invoca direct linkerul. | ||
- | |||
- | Dacă avem un fișier ''%%app.c%%'' cod sursă C, vom folosi compilatorul pentru a obține fișierul obiect ''%%app.o%%'': | ||
- | |||
- | <code> | ||
- | gcc -c -o app.o app.c | ||
- | |||
- | </code> | ||
- | Apoi pentru a obține fișierul executabil ''%%app%%'' din fișierul obiect ''%%app.o%%'', folosim tot utilitarul ''%%gcc%%'': | ||
- | |||
- | <code> | ||
- | gcc -o app app.o | ||
- | |||
- | </code> | ||
- | În spate, ''%%gcc%%'' va invoca linker-ul și va construi executabilul ''%%app%%''. | ||
- | Linker-ul va face legătura și cu biblioteca standard C (libc). | ||
- | |||
- | Procesul de linking va funcționa doar dacă fișierul ''%%app.c%%'' are definită funcția ''%%main()%%'', funcția principală a programului. | ||
- | Fișierele linkate trebuie să aibă o singură funcție ''%%main()%%'' pentru a putea obține un executabil. | ||
- | |||
- | Dacă avem mai multe fișiere sursă C, invocăm compilatorul pentru fiecare fișier și apoi linker-ul: | ||
- | |||
- | <code> | ||
- | gcc -c -o helpers.o helpers.c | ||
- | gcc -c -o app.o app.c | ||
- | gcc -o app app.o helpers.o | ||
- | |||
- | </code> | ||
- | Ultima comandă este comanda de linking, care leagă fișierele obiect ''%%app.o%%'' și ''%%helpers.o%%'' în fișierul executabil ''%%app%%''. | ||
- | |||
- | În cazul fișierelor sursă C++, vom folosi comanda ''%%g++%%'': | ||
- | |||
- | <code> | ||
- | g++ -c -o helpers.o helpers.cpp | ||
- | g++ -c -o app.o app.cpp | ||
- | g++ -o app app.o helpers.o | ||
- | |||
- | </code> | ||
- | Putem folosi și comanda ''%%gcc%%'' pentru linking, cu precizarea linkării cu biblioteca standard C++ (libc++): | ||
- | |||
- | <code> | ||
- | gcc -o app app.o helpers.o -lstdc++ | ||
- | |||
- | </code> | ||
- | Utilitarul de linkare este, în Linux, ''%%ld%%'' și este invocat în mod transparent de ''%%gcc%%'' sau ''%%g++%%''. | ||
- | Pentru a vedea cum este invocat linker-ul, folosim opțiunea ''%%-v%%'' a utilitarului ''%%gcc%%'', care va avea un rezultat asemănător cu: | ||
- | |||
- | <code> | ||
- | /usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so | ||
- | -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccwnf5NM.res | ||
- | -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc | ||
- | -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_i386 --hash-style=gnu | ||
- | --as-needed -dynamic-linker /lib/ld-linux.so.2 -z relro -o hello | ||
- | /usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu/crt1.o | ||
- | /usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/32/crtbegin.o | ||
- | -L/usr/lib/gcc/x86_64-linux-gnu/7/32 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu | ||
- | -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib32 -L/lib/i386-linux-gnu -L/lib/../lib32 -L/usr/lib/i386-linux-gnu | ||
- | -L/usr/lib/../lib32 -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu | ||
- | -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. -L/lib/i386-linux-gnu -L/usr/lib/i386-linux-gnu hello.o -lgcc --push-state | ||
- | --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state | ||
- | /usr/lib/gcc/x86_64-linux-gnu/7/32/crtend.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../i386-linux-gnu/crtn.o | ||
- | COLLECT_GCC_OPTIONS='-no-pie' '-m32' '-v' '-o' 'hello' '-mtune=generic' '-march=i686' | ||
- | |||
- | </code> | ||
- | Utilitarul ''%%collect2%%'' este, de fapt, un wrapper peste utilitarul ''%%ld%%''. | ||
- | Rezultatul rulării comeznii este unul complex. | ||
- | O invocare "manuală" a comenzii ''%%ld%%'' ar avea forma: | ||
- | |||
- | <code> | ||
- | ld -dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -o app /usr/lib32/crt1.o /usr/lib32/crti.o app.o helpers.o -lc /usr/lib32/crtn.o | ||
- | |||
- | </code> | ||
- | Argumentele comenzii de mai sus au semnificația: | ||
- | |||
- | * ''%%-dynamic-linker /lib/ld-linux.so.2%%'': precizează loaderul / linkerul dinamic folosit pentru încărcarea executabilului dinamic | ||
- | * ''%%-m elf_i386%%'': se linkează fișiere pentru arhitectura x86 (32 de biți, i386) | ||
- | * ''%%/usr/lib32/crt1.o%%'', ''%%/usr/lib32/crti.o%%'', ''%%/usr/lib32/crtn.o%%'': reprezintă biblioteca de runtime C (''%%crt%%'' - //C runtime//) care oferă suportul necesar pentru a putea încărca executabilul | ||
- | * ''%%-lc%%'': se linkează biblioteca standard C (libc) | ||
- | |||
- | ===== Inspectarea fișierelor ===== | ||
- | |||
- | Pentru a urmări procesul de linking, folosim utilitare de analiză statică precum ''%%nm%%'', ''%%objdump%%'', ''%%readelf%%''. | ||
- | |||
- | Folosim utilitarul ''%%nm%%'' pentru a afișa simbolurile dintr-un fișier obiect sau un fișier executabil: | ||
- | |||
- | <code> | ||
- | $ nm hello.o | ||
- | 00000000 T main | ||
- | U puts | ||
- | |||
- | $ nm hello | ||
- | 0804a01c B __bss_start | ||
- | 0804a01c b completed.7283 | ||
- | 0804a014 D __data_start | ||
- | 0804a014 W data_start | ||
- | 08048370 t deregister_tm_clones | ||
- | 08048350 T _dl_relocate_static_pie | ||
- | 080483f0 t __do_global_dtors_aux | ||
- | 08049f10 t __do_global_dtors_aux_fini_array_entry | ||
- | 0804a018 D __dso_handle | ||
- | 08049f14 d _DYNAMIC | ||
- | 0804a01c D _edata | ||
- | 0804a020 B _end | ||
- | 080484c4 T _fini | ||
- | 080484d8 R _fp_hw | ||
- | 08048420 t frame_dummy | ||
- | 08049f0c t __frame_dummy_init_array_entry | ||
- | 0804861c r __FRAME_END__ | ||
- | 0804a000 d _GLOBAL_OFFSET_TABLE_ | ||
- | w __gmon_start__ | ||
- | 080484f0 r __GNU_EH_FRAME_HDR | ||
- | 080482a8 T _init | ||
- | 08049f10 t __init_array_end | ||
- | 08049f0c t __init_array_start | ||
- | 080484dc R _IO_stdin_used | ||
- | 080484c0 T __libc_csu_fini | ||
- | 08048460 T __libc_csu_init | ||
- | U __libc_start_main@@GLIBC_2.0 | ||
- | 08048426 T main | ||
- | U puts@@GLIBC_2.0 | ||
- | 080483b0 t register_tm_clones | ||
- | 08048310 T _start | ||
- | 0804a01c D __TMC_END__ | ||
- | 08048360 T __x86.get_pc_thunk.bx | ||
- | |||
- | </code> | ||
- | Comanda ''%%nm%%'' afișează trei coloane: | ||
- | |||
- | * adresa simbolului | ||
- | * secțiunea și tipul unde se găsește simbolul | ||
- | * numele simbolului | ||
- | |||
- | Un simbol este numele unei variabile globale sau a unei funcții. | ||
- | Este folosit de linker pentru a face conexiunile între diferite module obiect. | ||
- | Simbolurile nu sunt necesare pentru executabile, de aceea executabilele pot fi stripped. | ||
- | |||
- | Adresa simbolului este, de fapt, offsetul în cadrul unei secțiuni pentru fișierele obiect. | ||
- | Și este adresa efectivă pentru executabile. | ||
- | |||
- | A doua coloana precizează secțiunea și tipul simbolului. | ||
- | Dacă este vorba de majusculă, atunci simbolul este exportat, este un simbol ce poate fi folosit de un alt modul. | ||
- | Dacă este vorba de literă mică, atunci simbolul nu este exportat, este propriu modulului obiect, nefolosibil în alte module. | ||
- | Astfel: | ||
- | |||
- | * ''%%d%%'': simbolul este în zona de date inițializate (''%%.data%%''), neexportat | ||
- | * ''%%D%%'': simbolul este în zona de date inițializate (''%%.data%%''), exportat | ||
- | * ''%%t%%'': simbolul este în zona de cod (''%%.text%%''), neexportat | ||
- | * ''%%T%%'': simbolul este în zona de cod (''%%.text%%''), exportat | ||
- | * ''%%r%%'': simbolul este în zona de date read-only (''%%.rodata%%''), neexportat | ||
- | * ''%%R%%'': simbolul este în zona de date read-only (''%%.rodata%%''), exportat | ||
- | * ''%%b%%'': simbolul este în zona de date neinițializate (''%%.bss%%''), neexportat | ||
- | * ''%%B%%'': simbolul este în zona de date neinițializate (''%%.bss%%''), exportat | ||
- | * ''%%U%%'': simbolul este nedefinit (este folosit în modulul curent, dar este definit în alt modul) | ||
- | |||
- | Alte informații se găsesc în pagina de manual a utilitarul ''%%nm%%''. | ||
- | |||
- | Cu ajutorul comenzii ''%%objdump%%'' dezasamblăm codul fișierelor obiect și a fișierelor executabile. | ||
- | Putem vedea, astfel, codul în limbaj de asamblare și funcționarea modulelor. | ||
- | |||
- | Comanda ''%%readelf%%'' este folosită pentru inspectarea fișierelor obiect sau executabile. | ||
- | Cu ajutorul comenzii ''%%readelf%%'' putem să vedem headerul fișierelor. | ||
- | O informație importantă în headerul fișierelor executabile o reprezintă entry pointul, adresa primei instrucțiuni executate: | ||
- | |||
- | <code> | ||
- | $ readelf -h hello | ||
- | ELF Header: | ||
- | Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 | ||
- | Class: ELF32 | ||
- | Data: 2's complement, little endian | ||
- | Version: 1 (current) | ||
- | OS/ABI: UNIX - System V | ||
- | ABI Version: 0 | ||
- | Type: EXEC (Executable file) | ||
- | Machine: Intel 80386 | ||
- | Version: 0x1 | ||
- | Entry point address: 0x8048310 | ||
- | Start of program headers: 52 (bytes into file) | ||
- | Start of section headers: 8076 (bytes into file) | ||
- | Flags: 0x0 | ||
- | Size of this header: 52 (bytes) | ||
- | Size of program headers: 32 (bytes) | ||
- | Number of program headers: 9 | ||
- | Size of section headers: 40 (bytes) | ||
- | Number of section headers: 35 | ||
- | Section header string table index: 34 | ||
- | |||
- | </code> | ||
- | Cu ajutorul comenzii ''%%readelf%%'' putem vedea secțiunile unui executabil / fișier obiect: | ||
- | |||
- | <code> | ||
- | $ readelf -S hello | ||
- | There are 35 section headers, starting at offset 0x1f8c: | ||
- | Section Headers: | ||
- | [Nr] Name Type Addr Off Size ES Flg Lk Inf Al | ||
- | [ 0] NULL 00000000 000000 000000 00 0 0 0 | ||
- | [ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1 | ||
- | [ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4 | ||
- | [ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4 | ||
- | [...] | ||
- | |||
- | </code> | ||
- | Tot cu ajutorul comenzii ''%%readelf%%'' putem lista (//dump//) conținutul unei anumite secțiuni: | ||
- | |||
- | <code> | ||
- | $ readelf -x .rodata hello | ||
- | |||
- | Hex dump of section '.rodata': | ||
- | 0x080484d8 03000000 01000200 48656c6c 6f2c2057 ........Hello, W | ||
- | 0x080484e8 6f726c64 2100 orld!. | ||
- | |||
- | </code> | ||
- | ===== Exerciții ===== | ||
- | |||
- | <note> În cadrul laboratoarelor vom folosi repository-ul de Git de IOCLA: [[https://github.com/systems-cs-pub-ro/iocla|https://github.com/systems-cs-pub-ro/iocla]]. | ||
- | Repository-ul este clonat pe desktopul mașinii virtuale. | ||
- | Pentru a îl actualiza, folosiți comanda ''%%git pull origin master%%'' din interiorul directorului în care se află repository-ul (''%%~/Desktop/iocla%%''). | ||
- | Recomandarea este să îl actualizați cât mai frecvent, înainte să începeți lucrul, pentru a vă asigura că aveți versiunea cea mai recentă. | ||
- | Dacă doriți să descărcați repository-ul în altă locație, folosiți comanda ''%%git clone https://github.com/systems-cs-pub-ro/iocla ${target}%%'' | ||
- | Pentru mai multe informații despre folosirea utilitarului ''%%git%%'', urmați ghidul de la [[https://gitimmersion.com/|Git Immersion]]. </note> | ||
- | |||
- | <note> | ||
- | <HTML>Cele mai multe dintre exerciții se desfășoară pe o arhitectură x86 (32 de biți, i386). | ||
- | Pentru a putea compila / linka pe 32 de biți atunci când sistemul vostru este pe 64 de biți, aveți nevoie de pachete specifice. | ||
- | Pe o distribuție Debian / Ubuntu, instalați pachetele folosind comanda: | ||
- | |||
- | <code> | ||
- | sudo apt install gcc-multilib libc6-dev-i386 | ||
- | |||
- | </code></HTML></note> | ||
- | |||
- | **Pentru exersarea informațiilor legate de linking, parcurgem mai multe exerciții.** | ||
- | **În cea mai mare parte, acestea sunt dedicate observării procesului de linking, cele marcate cu sufixul ''%%-tut%%'' sau ''%%-obs%%''.** | ||
- | **Unele exerciții necesită modificări pentru a repara probleme legate de linking, cele marcate cu sufixul ''%%-fix%%'', altele au drept scop exersarea unor noțiuni (cele marcate cu sufixul ''%%-diy%%'') sau dezvoltarea / completarea unor fișiere (cele marcate cu sufixul ''%%-dev%%'').** | ||
- | **Fiecare exercițiu se găsește într-un director indexat; cele mai multe fișiere cod sursă și fișiere ''%%Makefile%%'' sunt deja prezente.** | ||
- | |||
- | ==== 00. Folosirea variabilelor ==== | ||
- | |||
- | Accesăm directorul ''%%00-vars-obs/%%''. | ||
- | Vrem să urmărim folosirea variabilelor globale, exportate și neexportate. | ||
- | |||
- | În fișierul ''%%hidden.c%%'' avem variabila statică (neexportată) ''%%hidden_value%%''. | ||
- | Variabila este modificată și citită cu ajutorul unor funcții neexportate: ''%%init()%%'', ''%%get()%%'', ''%%set()%%''. | ||
- | |||
- | În fișierul ''%%plain.c%%'' avem variabila exportată ''%%age%%''. | ||
- | Aceasta poate fi modificată și citită direct. | ||
- | |||
- | Aceste variabile sunt folosite direct (''%%age%%'') sau indirect (''%%hidden_value%%'') în fișierul ''%%main.c%%''. | ||
- | Pentru folosirea lor, se declară funcțiile și variabilele în fișierul ''%%ops.h%%''. | ||
- | Declararea unei funcții se face prin precizarea antetului; declararea unei variabile se face prin prefixarea cu ''%%extern%%''. | ||
- | |||
- | **Compilați și rulați programul obținut pe baza fișierelor de mai sus.** | ||
- | |||
- | ==== 01. Linkarea unui singur fișier ==== | ||
- | |||
- | Accesăm directorul ''%%01-one-tut/%%''. | ||
- | Vrem să urmărim comenzile de linkare pentru un singur fișier cod sursă C. | ||
- | Fișierul sursă este ''%%hello.c%%''. | ||
- | |||
- | În cele trei subdirectoare, se găsesc fișierele de suport pentru următoarele scenarii: | ||
- | |||
- | * ''%%a-dynamic/%%'': crearea unui fișier executabil dinamic | ||
- | * ''%%b-static/%%'': crearea unui fișier executabil static | ||
- | * ''%%c-standalone/%%'': creare unui fișier executabil standalone, fără biblioteca standard C | ||
- | |||
- | **În fiecare subdirector folosim comanda ''%%make%%'' pentru a compila fișierul executabil ''%%hello%%''.** | ||
- | **Folosim comanda ''%%file hello%%'' pentru a urmări daca fișierul este compilat dinamic sau static.** | ||
- | |||
- | În fișierele ''%%Makefile%%'', comanda de linkare folosește ''%%gcc%%''. | ||
- | Este comentată o comandă echivalentă care folosește direct ''%%ld%%''. | ||
- | **Pentru a urmări folosirea directă a ''%%ld%%'', putem comenta comanda ''%%gcc%%'' și decomenta comanda ''%%ld%%''. Folosim iarăși comanda ''%%file hello%%''.** | ||
- | |||
- | În cazul ''%%c-standalone/%%'', pentru că nu folosim biblioteca standard C sau bibliotecă runtime C, trebuie să înlocuim funcționalitățile acestora. | ||
- | Funcționalitățile sunt înlocuite în fișierul ''%%start.asm%%'' și ''%%puts.asm%%''. | ||
- | Aceste fișiere implementează, respectiv, funcția / simbolul ''%%_start%%'' și funcția ''%%puts%%''. | ||
- | Funcția / simbolul ''%%_start%%'' este, în mod implicit, entry pointul unui program executabil. | ||
- | Funcția ''%%_start%%'' este responsabilă pentru apelul funcției ''%%main%%'' și încheierea programului. | ||
- | Pentru că nu există bibliotecă standard, aceste două fișiere sunt scrise în limbaj de asamblare și folosesc apeluri de sistem. | ||
- | |||
- | **Adăugați, în fișierul ''%%Makefile%%'' din directorul ''%%c-standalone/%%'', o comandă care folosește explicit ''%%ld%%'' pentru linkare.** | ||
- | |||
- | **Extra**: Accesați directorul ''%%01-one-diy/%%''. | ||
- | Vrem să compilăm și linkăm fișierele cod sursă din fiecare subdirector, asemănător cu ceea ce am făcut anterior. Copiați fișierele ''%%Makefile%%'' și actualizați-le în fiecare subdirector pentru a obține fișierul executabil. | ||
- | |||
- | ==== 02. Linkarea mai multor fișiere ==== | ||
- | |||
- | Accesăm directorul ''%%02-multiple-tut/%%''. | ||
- | Vrem să urmărim comenzile de linkare din fișiere multiple cod sursă C: ''%%main.c%%'', ''%%add.c%%'', ''%%sub.c%%''. | ||
- | |||
- | La fel ca în exercițiile de mai sus, sunt trei subdirectoare pentru trei scenarii diferite: | ||
- | |||
- | * ''%%a-no-header/%%'': declararea funcțiilor externe se face direct în fișierul sursă C (''%%main.c%%'') | ||
- | * ''%%b-header/%%'': declararea funcțiilor externe se face într-un fișier header separat (''%%ops.h%%'') | ||
- | * ''%%c-lib/%%'': declararea funcțiilor externe se face într-un fișier header separat, iar linkarea se face folosind o bibliotecă statică | ||
- | |||
- | În fiecare subdirector folosim comanda ''%%make%%'' pentru a compila fișierul executabil ''%%main%%''. | ||
- | |||
- | **Extra**: Accesați directorul ''%%02-multiple-diy/%%''. | ||
- | Vrem să compilăm și linkăm fișierele cod sursă din fiecare subdirector, asemănător cu ceea ce am făcut anterior. Copiați fișierele ''%%Makefile%%'' și actualizați-le în fiecare subdirector pentru a obține fișierul executabil. | ||
- | |||
- | ==== 03. Repararea entry pointului ==== | ||
- | |||
- | Accesați directorul ''%%03-entry-fix/%%''. | ||
- | Vrem să urmărim probleme de definire a funcției ''%%main()%%''. | ||
- | |||
- | Accesați subdirectorul ''%%a-c/%%''. | ||
- | Rulați comanda ''%%make%%'', interpretați eroarea întâlnită și rezolvați-o prin editarea fișierului ''%%hello.c%%''. | ||
- | |||
- | Accesați subdirectorul ''%%b-asm/%%''. | ||
- | Rulați comanda ''%%make%%'', interpretați eroarea întâlnită și rezolvați-o prin editarea fișierului ''%%hello.asm%%''. | ||
- | |||
- | În subdirectoarele ''%%c-extra-nolibc/%%'' și ''%%d-extra-libc/%%'' veți găsi soluții care nu modifică codul sursă al ''%%hello.c%%''. | ||
- | Aceste soluții modifică, în schimb, sistemul de build pentru a folosi altă funcție, diferită de ''%%main()%%'', ca prima funcție a programului. | ||
- | |||
- | **Extra**: Accesați directorul ''%%03-entry-2-fix/%%''. | ||
- | Rulați comanda ''%%make%%'', interpretați eroarea întâlnită și rezolvați-o prin editarea fișierului ''%%hello.c%%''. | ||
- | |||
- | ==== 04. Folosire simboluri (variabile și funcții) ==== | ||
- | |||
- | Accesați directorul ''%%04-var-func-fix/%%''. | ||
- | Rulați comanda ''%%make%%'' și rulați executabilul obținut. Interpretați erorile/eroarea întâlnite/întâlnită și rezolvați-le/rezolvați-o prin editarea fișierelor sursă. | ||
- | |||
- | ==== 05. Reparare problemă cu bibliotecă ==== | ||
- | |||
- | Accesați directorul ''%%05-lib-fix/%%''. | ||
- | Rulați comanda ''%%make%%'', interpretați eroarea întâlnită și rezolvați-o prin editarea fișierului ''%%Makefile%%''. | ||
- | Urmăriți fișierul ''%%Makefile%%'' din directorul ''%%02-multiple-tut/c-lib/%%''. | ||
- | |||
- | ==== 06. Linkare fișier obiect (fără fișier cod sursă) ==== | ||
- | |||
- | Accesați directorul ''%%06-obj-link-dev/%%''. | ||
- | Fișierul ''%%shop.o%%'' expune o interfață (funcții și variabile) care permite afișarea unor mesaje. | ||
- | Editați fișierul ''%%main.c%%'' pentru a apela corespunzător interfața expusă și pentru a afișa mesajele: | ||
- | |||
- | <code> | ||
- | price is 21 | ||
- | quantity is 42 | ||
- | |||
- | </code> | ||
- | Explorați interfața și conținutul funcțiilor din fisierul ''%%shop.o%%'' folosind ''%%nm%%'' și ''%%objdump%%''. | ||
- | |||
- | ==== Bonus. Utilizare cod python în C ==== | ||
- | |||
- | <note info> | ||
- | <HTML> | ||
- | În cadrul acestui exercițiu veți vedea un exemplu de ceea ce se poate face în urma legării unui anumit tip de fișiere obiect, și anume biblioteci; pentru acest exercițiu este vorba de biblioteca python$(PYTHON_VERSION), unde <b>PYTHON_VERSION</b> poate să fie diferit în funcție de soluția propusă de fiecare. | ||
- | Pentru a putea să compilați surse veți avea nevoie de versiunea de dezvoltare pentru python; pentru instalare folosiți comanda:<code>sudo apt-get install python$(PYTHON_VERSION)-dev</code>. | ||
- | Înlocuiți $(PYTHON_VERSION) cu versiunea pe care o doriți(<b>3.8 sau mai recentă</b>) | ||
- | </HTML> | ||
- | </note> | ||
- | |||
- | Accesați directorul ''%%bonus-c-python%%''. | ||
- | Fișierul main.c are un exemplu de cum se execută o funcție simplă de afișare a unui mesaj scrisă într-un modul python separat. Plecând de la exemplul prezentat, creați o funcție în modulul numit ''%%my_module.py%%'' aflat în directorul ''%%python-modules%%'', care primește doi parametri reprezentând două șiruri de caractere și întoarce **poziția primei apariții a celui de-al doilea șir în cadrul primului**, dacă al doilea șir este un subșir al primului șir și **-1** în caz contrar. | ||
- | |||
- | <HTML> | ||
- | <pre> | ||
- | Dacă funcția creată este denumită <b>subsir</b> atunci <b>subsir('123456789', '89')</b> va întoarce 7 iar <b>subsir('123', '4')</b> va întoarce -1. Practic semnătura este de forma <b>subsir(haystack, needle)</b>. | ||
- | </pre> | ||
- | |||
- | </HTML> | ||
- | Rezultatul funcției scrisă în python va fi preluat în codul C și se va afișa un mesaj corespunzător. Urmăriți comentariile cu **TODO** din fișierele ''%%main.c%%'' și ''%%my_module.py%%''. | ||
- | |||
- | <note> | ||
- | Puteți să urmăriți și exemplele de [[https://www.codeproject.com/Articles/820116/Embedding-Python-program-in-a-C-Cplusplus-code|aici]] și/sau [[https://www.xmodulo.com/embed-python-code-in-c.html|aici]] pentru a vedea cum să preluați rezultatul funcției scrisă în python. De asemenea puteți consulta documentația de [[https://docs.python.org/3/c-api/long.html|aici]] pentru a vedea cum să faceți conversia rezultatului la un tip de date din C. </note> | ||
- | |||
- | <note> | ||
- | Atenție la versiunea de python pe care o folosiți; nu este recomandată o anumită versiune însă trebuie să aveți în vedere că în funcție de soluția voastră este posibil să fie nevoie să folosiți versiune specifică. Makefile-ul folosește versiunea **3.9**. </note> |