de control vizibilitate pentru numele simbolice ale bibliotecilor partajate Partea 1

Liu Chzhipen. Software Developer, IBM China

Care este numele simbolice și vizibilitatea acestora

nume simbolic (simbol) - .. Acesta este unul dintre conceptele de bază atunci când vine vorba de fișierele obiect, layout-ul de cod, etc. De fapt, în C / C simbol limba ++ este un subiect adecvat pentru majoritatea variabilelor de utilizator, numele de funcții, decorate într-un tip de spațiu de nume clasa / struct / numele, și așa mai departe .. de exemplu, C / C ++ compilator poate genera cod în fișierul obiect în cazurile în care au fost definite variabilele globale non-statice și funcții non-statice, permițând linkerul să decidă dacă aceleași date sau prog Codul ammny utilizat în diferite module (fișiere obiect, biblioteci dinamice partajate, fișiere executabile) sau nu.

Cu toate că în diferite module pot fi folosite împreună ca variabile și funcții definite domeniul de aplicare al variabilelor este mai frecventă în fișiere obiect. De exemplu, puteți declara o variabilă în fișierul A.C ca aceasta:

În fișierul B.c declară aceeași variabilă, după cum urmează:

De ce este necesar pentru a controla vizibilitatea numelor simbolice

Pe platforme diferite compilator XL C / C ++ poate lucra în diferite moduri: se poate face caracterele pe toate modulele sau exportate, sau nu exportate. De exemplu, dacă creați biblioteci partajate în ELF-format (Format și executabile) Legarea toate simbolurile sunt exportate platforma implicită IBM PowerLinux ™. La crearea XCOFF-biblioteci pe AIX pe platforma POWER, versiunea actuală a XL C / C ++ compilator nu poate exporta simboluri în cazul în care nu utilizați instrumente suplimentare. Când acest cod dezvoltatorii pot folosi alte moduri de a face vizibilă fiecare caracter special, în mod individual (ne spunem, în al doilea articol din această serie). De obicei, cu toate acestea, nu este recomandat să exporte toate simbolurile conținute în modulele de program. Puteți exporta doar caracterele necesare. Acest lucru nu numai că va spori siguranța bibliotecii, dar va reduce link-ul dinamic.

În cazul în care decizia este de a exporta toate numele simbolice, există o probabilitate mare de conflicte în timp link-ul, mai ales în cazul în care diversele module dezvoltate de persoane diferite. Deoarece numele simbolic este un concept de nivel scăzut, aceasta nu se aplică în domeniul de aplicare. De îndată ce un aspect a două biblioteci ce conțin simboluri cu același nume, compilatorul poate înlocui unul dintre aceste caractere cu un alt simbol cu ​​același nume (din fericire, avertismentul afișat sau mesaj de eroare). În cele mai multe cazuri, din punct de vedere al bibliotecii care nu sunt destinate pentru dezvoltatori vooblsche astfel de simboluri pentru a fi utilizate. Prin urmare, în aceste situații este mult mai bine de a crea doar un număr limitat de clare (și bine concepute) numele personajelor.

In fiecare zi, pentru aplicații de viteză dezvoltate în C ++, cere mai mult. Din cauza dependenței de alte biblioteci folosind caracteristici specifice și C ++ (de exemplu, modele) compilator și linker-ul sunt folosite și pentru a genera o cantitate foarte mare de nume de caractere. În consecință, toate simbolurile exportate încetinește cererea și de a crește cantitatea de memorie ocupată. Export doar un număr limitat de caractere poate reduce timpul de încărcare și legătura între bibliotecile dinamice partajate. În plus, aceasta permite compilatorul pentru a genera cod mai eficient și optim.

Dezavantajele menționate mai sus explică de ce este necesar să se determine vizibilitatea simbolului. În acest articol, vă vom arăta cum să gestionați numele simbolice obiect dinamic Shared (DSO). Această problemă poate fi rezolvată în moduri diferite, și vă vom spune care este calea cea mai potrivită pentru fiecare platformă.

Metode de control Simbol vizibilitate

În această secțiune, vom folosi următorul cod în limba C ++:

Listarea 1. A.C

În fișierul A.C am definit o variabilă numită myintvar și două ale căror nume func0 și func1. În mod implicit, atunci când creați o bibliotecă partajată pe compilator AIX-platformă și linker cu CreateExportList instrument de a face toate cele trei simboluri vizibile. Acest lucru poate fi verificat prin referire la numele simbolice încărcător de masă folosind utilitarele basculante:

Aici înțelesul „EXP“ înseamnă că numele simbolic este „exportat“ (exportate); Numele func0 și funcțiile func1 decorate în conformitate cu regulile de decor C ++ (așa cum s-ar putea ghici). Parametrul -t benă comandă afișează conținutul numelor încărcător tabela de simboluri (Symbol Loader tabelul de informații), care ar trebui să fie utilizate de linker-ul dinamic. În acest exemplu, toate numele de fișiere A.C. de caractere au fost exportate Cu toate acestea, dezvoltatorul de bibliotecă poate decide să exporte numai func1 funcția. nume simbol global și func0 funcția myintvar poate doar să mențină sau să schimbe starea sa internă sau să fie utilizate la nivel local. Astfel, dezvoltatorul este important pentru a le face invizibile.

Există cel puțin trei moduri de a rezolva această problemă: utilizarea cuvântului cheie static. definiție de vizibilitate atribut pentru compilatorul GNU și utilizarea tabelelor de export. Fiecare dintre ele are propriile sale caracteristici unice și potențiale dezavantaje. Considerăm acum toate cele trei metode.

1. Utilizarea cuvântului cheie static

Cuvântul cheie statică în limbajul C / C ++ poate fi supraîncărcat de cuvinte cheie, deoarece aceasta poate însemna atât domeniul de aplicare și variabila de stocare. Putem spune că domeniul de aplicare al acestui cuvânt cheie dezactivează legătura externă pentru numele caracterului. Acest lucru înseamnă că, pentru numele de caractere cu cuvântul cheie static nu obligatoriu se va efectua, deoarece compilatorul nu lasă pentru verigii nici o informație despre el. Această metodă este implementată în nivelul limbajului de programare, și este cel mai simplu mod de a ascunde simbolul.

Adăugați cuvântul cheie static în exemplul anterior:

Listarea 2. B.c

Acum, dacă ne uităm la încărcător de masă nume simbolice după crearea bibliotecii partajate, veți vedea rezultatul așteptat:

În acest exemplu, a fost exportat numai func1 funcție. Cu toate acestea, deși cuvântul cheie static și poate ascunde de caractere, aceasta impune o limitare: poate fi utilizat numai variabile sau funcții în acest domeniu de aplicare fișier, în care sunt definite. Astfel, dacă definim variabila ca

și apoi utilizați B.c fișier libtest.a încerca să creeze o bibliotecă de obiecte și a.o b.o, linker-ul va da o eroare cu privire la imposibilitatea de a lega myintvar variabile. B.c definit în fișierul, deoarece definiția ei nu se găsește în altă parte. Acest lucru împiedică schimbul de date sau cod într-un singur modul, care necesită de obicei un dezvoltator. Astfel, această metodă este mai evidentă prin mijloacele de control ale variabilelor și funcții într-un fișier, mai degrabă decât de control de nivel scăzut înseamnă vizibilitate a simbolului. În practică, majoritatea dezvoltatorilor nu folosesc cuvinte cheie statică pentru a controla vizibilitatea simbolului, astfel încât să ne considerăm a doua metodă.

2. Definirea atributelor de vizibilitate (pentru compilator GNU)

Următoarea metodă de control de caractere de vizibilitate este de a utiliza vizibilitatea atributului. Acest atribut este setat cu interfața aplicației binare (Application Binary Interface, ABI) ELF format. În general, această interfață definește patru clase, dar în cele mai multe cazuri, sunt utilizate pe scară largă, doar două dintre ele:

  • STV_DEFAULT - indică faptul că simbolurile sunt exportate, care este vizibil peste tot ...
  • STV_HIDDEN - indică faptul că personajele nu sunt exportate și nu pot fi utilizate în alte obiecte.

Vă rugăm să rețineți că ABI-interfață este o extensie a GNU compilator C / C ++. În acest moment, ceea ce face utilizatorii PowerLinux pot folosi ca un atribut GNU pentru nume de caractere. Să considerăm un exemplu pentru acest caz:

Pentru a determina atributul GNU, trebuie să utilizați un __attribute__ parametru de proiectare și inclus în paranteze duble. Pentru a ascunde numele simbolice, puteți specifica valoarea vizibilității ( „ascunse“). În exemplul nostru, am făcut-o pentru variabila funcția myintvar și func0. Ca urmare, acestea nu pot fi exportate în bibliotecă, dar pot fi utilizate în fișierele sursă. De fapt, caracterele ascunse nu vor apărea în tabela de simboluri dinamice, dar va fi prezentă în tabela de simboluri, conceput pentru legarea statică. Acest algoritm bine definit poate lucra cu siguranta pentru a rezolva problema noastră. Este evident că această metodă este mai bună decât metoda de a folosi cuvântul cheie static.

De asemenea, în interfața ELF ABI definește următoarele moduri de vedere:

  • STV_PROTECTED: simbol este vizibil în afara obiectului executabil sau partajat curent, dar nu poate fi înlocuit. Cu alte cuvinte, în cazul în care un simbol protejat (protejat) al bibliotecii partajate fiind accesat de un cod extern, codul se va referi întotdeauna la un simbol al unei biblioteci partajate, chiar dacă caracterul același nume a fost definit într-un fișier executabil.
  • STV_INTERNAL: caracter nu este disponibil în afara bibliotecii executabil sau partajat curent.

Rețineți că, în prezent, această metodă nu este acceptată de compilator XL C / C ++ chiar platforma PowerLinux. Din fericire, există o altă cale de ieșire.

3. Utilizați tabelele de export

Cele două soluții anterioare aplicate la nivel de cod sursă, cu numai compilator eficient. Cu toate acestea, utilizatorii trebuie să fie în măsură să pună aceeași sarcină în fața verigii, deoarece apariția simbolului este folosit în principal pentru legarea dinamică. Decizie, aplicabil pentru linker-ul este de a utiliza tabelele de export.

tabelul de export pot fi generate automat de compilator (sau instrumente terță parte, de exemplu, CreateExportlist) la momentul creației, fie scrise manual de către dezvoltatorul bibliotecii partajate. Folosind parametrul personalizat este transmis tabelul de export agent de legătură ca date de intrare. Cu toate acestea, din moment ce toate lucrările de către dezvoltatorii de conducător auto compilator rareori să acorde o atenție la opțiuni pentru setări avansate.

Principiul de funcționare al exportului de tabele este că acestea indică în mod clar linker care simbolurile pot fi exportate din fișierele obiect printr-un fișier extern. Membrii GNU numesc aceste fișiere externe „Harta de export“. putem crea următoarea hartă export pentru exemplul nostru:

Acest design spune linker care este exportat doar simbolic numele func1. și toate celelalte simboluri (marcate cu *) sunt locale. func0 simboluri locale și myintvar pot fi definite în mod explicit (local: func0; myintvar;), dar este evident că este mai convenabil de a folosi rezumarea un asterisc (*). În general, se recomandă să utilizați un asterisc pentru a rezuma simbolurile locale și explicit specificați doar acele simboluri care urmează a fi exportate, deoarece această metodă este mai sigură. Deci, nu veți uita niciodată că aceste sau alte nume ar trebui să fie locale, și, în plus, ar exclude posibilitatea dublării numelor simbolice în diferite tabele, ceea ce poate duce la consecințe nedorite.

Pentru a crea un obiect partajat folosind această metodă, trebuie să specificați exportați fișierul hartă folosind opțiunea linker --version-script:

Pentru a citi ELF-obiect folosind utilitarul readelf cu opțiunea -s, utilizați comanda readelf -s mylib.so.

Acest lucru va arăta că accesul global este disponibil numai la funcția func1 (secțiunea intrările .dynsym), și celelalte simboluri nu sunt disponibile și sunt locale.

Linker IBM AIX sistem de operare folosit tabelul de export similare. Pentru a fi mai precis, acestea sunt numite pe fișiere de export AIX.

Creați un fișier de export este foarte simplu. Pentru a face acest lucru, trebuie doar să listeze caracterele pe care doriți să le exportați, fișierul de export. În exemplul nostru ar arăta astfel:

Astfel, atunci când specificați fișierul de export cu opțiunile linker, numai numele simbolic pe care dorim să exporte, se adaugă la „masa simbolului încărcător“ XCOFF-obiect, și toate celelalte simboluri sunt neexportate.

AIX Versiunea 6.1 sau mai mare poate fi, de asemenea, adăugate la fișierul de export atributele de vizibilitate care apar în modul Simbol. Linker AIX poate rula cu următoarele patru tipuri:

Diferența dintre export și atributele ascunse este evident că este imposibil să vorbim despre atributele exportate și protejate. Pentru mai multe informații cu privire la deplasarea simbolurilor vor fi discutate în următoarea secțiune a acestui articol.

Deci, toate aceste cuvinte cheie pot fi utilizate în fișierul de export. Adăugarea de ei prin spațiu după numele simbolului, se poate realiza diferite grade de controale de vizibilitate. În exemplul nostru, vom specifica următoarele atribute (pentru versiunea de AIX 6.1 sau mai mare):

Aceasta spune linkerul că func1__Fi numele simbolului (de exemplu, func1 ..) vor fi exportate, iar celelalte simboluri - nr.

Este posibil să fi observat că, spre deosebire de hărți de export GNU simbolurile de fișier de export sunt decorate. Decorat cu nume simbolice pot fi sub formă incomodă, dacă programatorii nu avea grijă despre regulile de decor. Cu toate acestea, ea ajută linker-ul pentru a efectua rapid rezoluția de nume. Elimină acest inconvenient pe AIX ajută utilitate specială.

Pe scurt, în cazul în care dezvoltatorul utilizează -qmkshrobj opțiune atunci când apelarea XL C / C ++ compilator. începe utilitate CreateExportList. cu rezultatul că, după generarea unui fișier obiect este generat automat exportați un fișier care conține numele de caractere decorate. Compilatorul transmite apoi linker parametri fișier de export pentru procesarea simbolului vizibilitate. Revenind la exemplul nostru, executați următoarea comandă:

Ca rezultat, vom obține libtest.a bibliotecă în care sunt exportate toate caracterele (acțiunea implicită). Desi nu am atins obiectivul nostru, întregul proces este transparent pentru dezvoltator. În schimb, putem crea un fișier de export utilizând instrumente CreateExportList și să fie în măsură să editați manual fișierul de export. De exemplu, dacă doriți să ne înregistrați fișierul de a exporta exportfile nume. compilatorul XL C / C ++ aveți nevoie pentru a trece opțiunea qexpfile = exportfile.

În acest caz, veți găsi toate simbolurile, după cum se arată mai jos:

În funcție de cerințele noastre, putem fie pur și simplu șterge rândul cu numele și func0 myintvar. sau să le adăugați cuvântul cheie ascunse. Apoi, puteți salva fișierul de export, și se transferă fișierul rezultat la linkerul folosind opțiunile -Fi: exportfile.

In tot acest proces este finalizat. Acum, obiectul dinamic partajat rezultat va conține numele exportat func1__Fi (de exemplu, func1 ..):

O altă opțiune este de a genera în mod explicit un fișier de export folosind utilitarul CreateExportList. așa cum se arată mai jos:

În acest exemplu, totul funcționează în același mod ca și în anul precedent.

Când se utilizează noul format în AIX Versiunea 6.1 și mai sus pentru a adăuga cuvinte cheie pentru fiecare nume simbolic poate necesita o mulțime de timp și efort. În acest sens, următoarele versiuni de compilator XL C / C ++ este planificată o serie de îmbunătățiri care fac viața mai ușoară pentru dezvoltatori (să-i spunem, în al doilea articol din această serie).

În cazul utilizării la export toate tabelele de informații stocate în tabele, precum și necesitatea de a modifica codul sursă nu este disponibil. Acest lucru vă permite să împartă activitatea privind scrierea de cod, și să lucreze pe dezvoltarea bibliotecilor. Cu toate acestea, atunci când se utilizează această metodă, s-ar putea întâlni o problemă. Deoarece fișierul sursă nu se schimba codul binar generat de compilator, acesta poate să nu fie optimă. Compilatorul poate să nu fie suficiente informații pentru a optimiza aceste simboluri care nu au fost exportate. Acest lucru poate duce fie la o creștere a dimensiunii binare sau încetini procesul de rezolvare a simbolului. Din fericire, pentru cele mai multe aplicații nu este o problemă prea gravă.

Tabelul următor prezintă o comparație între cele de mai sus trei metode.

Tabelul 1 Avantajele și dezavantajele fiecărei metode

articole similare