Să se implementeze sub forma unei biblioteci partajate/dinamice un loader de fișiere executabile în format ELF pentru Linux. Loader-ul va încărca fișierul executabil în memorie pagină cu pagină, folosind un mecanism de tipul demand paging - o pagină va fi încărcată doar în momentul în care este nevoie de ea. Pentru simplitate, loader-ul va rula doar executabile statice - care nu sunt link-ate cu biblioteci partajate/dinamice.
Pentru a rula un fișier executabil, loader-ul va executa următorii pași:
Interfața de utilizare a bibliotecii loader-ului este prezentată în cadrul fișierul header loader.h
. Acesta conține funcții de inițializare a loaderului(so_init_loader
) și de executare a binarului (so_execute
).
/* initializes the loader */ int so_init_loader(void); /* runs an executable specified in the path */ int so_execute(char *path, char *argv[]);
so_init_loader
realizează inițializarea bibliotecii. În cadrul funcției se va realiza, în general, înregistrarea page fault handler-ului sub forma unei rutine pentru tratarea semnalului SIGSEGV sau a unui handler de excepție.so_execute
realizează parsarea binarului specificat în path
și rularea primei instrucțiuni (entry point) din executabil.Pentru a ușura realizarea temei, vă punem la dispoziție în scheletul de cod un parser pentru ELF (Linux).
typedef struct so_seg { /* virtual address */ uintptr_t vaddr; /* size inside the executable file */ unsigned int file_size; /* size in memory (can be larger than file_size) */ unsigned int mem_size; /* offset in file */ unsigned int offset; /* permissions */ unsigned int perm; /* custom data */ void *data; } so_seg_t; typedef struct so_exec { /* base adress */ uintptr_t base_addr; /* address of entry point */ uintptr_t entry; /* number of segments */ int segments_no; /* array of segments */ so_seg_t *segments; } so_exec_t; /* parse an executable file */ so_exec_t *so_parse_exec(char *path); /* * start an executable file, previously parsed in a so_exec_t structure * (jumps to the executable's entry point) */ void so_start_exec(so_exec_t *exec, char *argv[]);
Interfața de parser pune la dispoziție două funcții:
so_parse_exec
- parsează executabilul și întoarce o structură de tipul so_exec_t
. Aceasta poate fi folosită în continuare pentru a identifica segmentele executabilului și atributele lui.so_start_exec
- pregătește environment-ul programului și începe execuția lui.Structurile folosit de interfață sunt:
so_exec_t
- descrie structura executabilului:base_addr
- indică adresa la care ar trebui încărcat executabilulentry
- adresa primei instrucțiuni executate de către executabilsegments_no
- numărul de segmente din executabilsegments
- un vector (de dimensiunea segments_no
) care conține segmentele executabiluluiso_seg_t
- descrie un segment din cadrul executabiluluivaddr
- adresa la care ar trebui încărcat segmentulfile_size
- dimensiunea în cadrul fișierului a segmentuluimem_size
- dimensiunea ocupată de segment în memorie; dimensiunea segmentului în memorie poate să fie mai mare decât dimensiunea în fișier (spre exemplu pentru segmentul bss
); în cazul acesta, diferența între spațiul din memorie și spațiul din fișier, trebuie zeroizatăoffset
- offsetul din cadrul fișierului la care începe segmentulperm
- o mască de biți reprezentând permisiunile pe care trebuie să le aibă paginile din segmentul curentPERM_R
- permisiuni de citirePERM_W
- permisiuni de srcrierePERM_X
- permisiuni de execuțiedata
- un pointer opac pe care îl puteți folosi să atașați informații proprii legate de segmentul curent (spre exemplu, puteți stoca aici informații despre paginile din segment deja mapate)În imaginea de mai jos aveți o reprezentare grafică a unui segment.
so_exec_t
și so_seg_t
);data
al unui segment;SIGSEGV
folosind apeluri din familia sigaction.sa_sigaction
al structurii struct sigaction
.si_addr
din cadrul structurii siginfo_t
.MAP_FIXED
, apoi copiați în pagină datele din executabilscanf
/printf
), funcțiile de alocare/eliberare de memorie (malloc
/free
) și funcțiile de lucru cu șiruri de caractere (strcat
, strdup
etc.)fopen
, fread
, fwrite
, fclose
nu trebuie folosite; în locul acestora folosiți open
, read
, write
, close
.#include <signal.h> ... /* SIGUSR2 handler */ static void usr2_handler(int signum) { /* actions that should be taken when the signal signum is received */ ... } int main(void) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_RESETHAND; /* restore handler to previous state */ sa.sa_handler = usr2_handler; sigaction(SIGSEGV, &sa, NULL); return 0; }
so_test_prog
) din cadrul scheletului.libso_loader.so
.
loader.h
) sau alte headere prezente?Pentru întrebări sau nelămuriri legate de temă folosiți forumul temei.
ATENȚIE să nu postați imagini cu părți din soluția voastră pe forumul pus la dispoziție sau orice alt canal public de comunicație. Dacă veți face acest lucru, vă asumați răspunderea dacă veți primi copiat pe temă.