History

elf: rewrite version support

so it isn't quadratic in number of symbols. It's still quadratic in number of lib/version combinations (at library load time the sym versions are internalized), but that's much less a problem as in practice only glibc uses sym versioning.

Michael Matz committed 6 years ago

Commit 8a93ce11 parent 25781e41 file · +144 −249Browse files

Unified · Split

tccelf.c+144−249
@@ -43,32 +43,19 @@ ST_DATA Section *symtab_section; · 42 unmodified lines
4343 ST_DATA Section *stab_section, *stabstr_section;
4444
4545 /* elf version information */
46−ST_DATA int nb_version_sym;
47−struct sym_info_struct {
48− char *name;
49− ElfW(Sym) esym;
50−};
51−ST_DATA struct def_need_struct {
52− char *lib;
53− char *version;
54− unsigned int nb_sym_info;
55− struct sym_info_struct *sym_info;
56−} *version_sym;
57−ST_DATA int nb_need;
58−ST_DATA int dt_verneednum;
59−ST_DATA struct need_struct {
60− int offset;
61− char *lib;
62− int n_aux;
63− struct aux_struct {
64− int offset;
46+struct sym_version {
47+ char *lib;
6548 char *version;
66− int other;
67− } *aux;
68−} *need;
69−ST_DATA ElfW(Half) next_version = 1;
70−ST_DATA Section *versym_section;
71−ST_DATA Section *verneed_section;
49+ int out_index;
50+ int prev_same_lib;
51+};
52+static int nb_sym_versions;
53+static struct sym_version *sym_versions;
54+static int nb_sym_to_version;
55+static int *sym_to_version;
56+static int dt_verneednum;
57+static Section *versym_section;
58+static Section *verneed_section;
7259
7360 /* XXX: avoid static variable */
7461 static int new_undef_sym = 0; /* Is there a new undefined sym since last new_undef_sym() */
@@ -139,6 +126,18 @@ ST_FUNC void tccelf_delete(TCCState *s1) · 64 unmodified lines
139126 {
140127 int i;
141128
129+ /* free symbol versions */
130+ for (i = 0; i < nb_sym_versions; i++) {
131+ tcc_free(sym_versions[i].version);
132+ tcc_free(sym_versions[i].lib);
133+ }
134+ tcc_free(sym_versions);
135+ sym_versions = NULL;
136+ nb_sym_versions = 0;
137+ tcc_free(sym_to_version);
138+ sym_to_version = NULL;
139+ nb_sym_to_version = 0;
140+
142141 /* free all sections */
143142 for(i = 1; i < s1->nb_sections; i++)
144143 free_section(s1->sections[i]);
@@ -557,219 +556,72 @@ ST_FUNC void* tcc_get_symbol_err(TCCState *s, const char *name) · 412 unmodified lines
557556 }
558557 #endif
559558
560−ST_FUNC void
561−save_def_need_index (int *n_def_need, struct def_need_struct **def_need,
562− unsigned int n, const char *lib, const char *version)
563−{
564− /* add index in table */
565− if (*n_def_need < n + 1) {
566− *def_need = (struct def_need_struct *)
567− tcc_realloc (*def_need,
568− (n + 1) * sizeof (struct def_need_struct));
569− while (*n_def_need < n + 1) {
570− (*def_need)[*n_def_need].lib = NULL;
571− (*def_need)[*n_def_need].version = NULL;
572− (*def_need)[*n_def_need].nb_sym_info = 0;
573− (*def_need)[*n_def_need].sym_info = NULL;
574− (*n_def_need)++;
575− }
576− }
577− if ((*def_need)[n].lib == NULL) {
578− (*def_need)[n].lib = tcc_strdup (lib);
579− (*def_need)[n].version = tcc_strdup (version);
580− }
581−}
582−
583−ST_FUNC void
584−free_def_need (int n_def_need, struct def_need_struct *def_need)
585−{
586− int i;
587−
588− for (i = 0; i < n_def_need; i++) {
589− tcc_free (def_need[i].lib);
590− tcc_free (def_need[i].version);
591− }
592− tcc_free (def_need);
593−}
594−
595−ST_FUNC void
596−add_version_sym (Section *s, int sym_index, int n_def_need,
597− struct def_need_struct *def_need, const char *name, unsigned int n)
598−{
599− int i, j;
600− ElfW(Sym) *esym;
601−
602− /* check if present */
603− esym = &((ElfW(Sym) *)s->data)[sym_index];
604− for (i = 0; i < nb_version_sym; i++) {
605− for (j = 0; j < version_sym[i].nb_sym_info; j++) {
606− if (strcmp (version_sym[i].sym_info[j].name, name) == 0) {
607− if (memcmp (&version_sym[i].sym_info[j].esym, esym, sizeof (ElfW(Sym))) == 0) {
608− return;
609− }
610− version_sym[i].nb_sym_info--;
611− tcc_free (version_sym[i].sym_info[j].name);
612− for (; j < version_sym[i].nb_sym_info; j++) {
613− version_sym[i].sym_info[j] = version_sym[i].sym_info[j + 1];
614− }
615− }
616− }
617− }
618− /* add new value */
619− for (i = 0; i < nb_version_sym; i++) {
620− if (strcmp (def_need[n].lib,
621− version_sym[i].lib) == 0 &&
622− strcmp (def_need[n].version,
623− version_sym[i].version) == 0) {
624− break;
625− }
626− }
627− if (i == nb_version_sym) {
628− version_sym = (struct def_need_struct *)
629− tcc_realloc (version_sym,
630− (nb_version_sym + 1) * sizeof (struct def_need_struct));
631− version_sym[nb_version_sym].lib = tcc_strdup (def_need[n].lib);
632− version_sym[nb_version_sym].version = tcc_strdup (def_need[n].version);
633− version_sym[nb_version_sym].nb_sym_info = 0;
634− version_sym[nb_version_sym].sym_info = NULL;
635− nb_version_sym++;
636− }
637− version_sym[i].sym_info = (struct sym_info_struct *)
638− tcc_realloc (version_sym[i].sym_info,
639− (version_sym[i].nb_sym_info + 1) * sizeof (struct sym_info_struct));
640− version_sym[i].sym_info[version_sym[i].nb_sym_info].name = tcc_strdup (name);
641− version_sym[i].sym_info[version_sym[i].nb_sym_info].esym = *esym;
642− version_sym[i].nb_sym_info++;
643−}
644−
645−static void
646−add_need_sym (Section *s, const char *soname, const char *version, const char *name, int sym_index)
647−{
648− int i;
649− unsigned long size;
650− void *ptr;
651− int ver_index = 0;
652− struct need_struct *verlib = NULL;
653− ElfW(Half) vers;
654−
655− if (soname) {
656− for (i = 0; i < nb_need; i++) {
657− if (strcmp (soname, need[i].lib) == 0) {
658− verlib = &need[i];
659− break;
660− }
661− }
662− if (verlib == NULL) {
663− need = (struct need_struct *)
664− tcc_realloc (need, (nb_need + 1) * sizeof (struct need_struct));
665− need[nb_need].offset = put_elf_str(s->link, soname);
666− need[nb_need].lib = tcc_strdup (soname);
667− need[nb_need].n_aux = 0;
668− need[nb_need].aux = NULL;
669− verlib = &need[nb_need];
670− nb_need++;
671− }
672− for (i = 0; i < verlib->n_aux; i++) {
673− if (strcmp (version, verlib->aux[i].version) == 0) {
674− ver_index = verlib->aux[i].other;
675− }
676− }
677− if (ver_index == 0) {
678− verlib->aux = (struct aux_struct *)
679− tcc_realloc (verlib->aux, (verlib->n_aux + 1) * sizeof (struct aux_struct));
680− verlib->aux[verlib->n_aux].offset = put_elf_str(s->link, version);
681− verlib->aux[verlib->n_aux].version = tcc_strdup (version);
682− ver_index = verlib->aux[verlib->n_aux].other = ++next_version;
683− verlib->n_aux++;
684− }
685− }
686− /* update versym section */
687− size = versym_section->data_offset / sizeof (vers);
688− for (i = size; i < (sym_index + 1); i++) {
689− ptr = section_ptr_add (versym_section, sizeof (vers));
690− vers = 0;
691− memcpy (ptr, &vers, sizeof (vers));
692− }
693− ptr = versym_section->data + sym_index * sizeof (vers);
694− vers = ver_index;
695− memcpy (ptr, &vers, sizeof (vers));
696−}
697−
698559 static void
699560 version_add (TCCState *s)
700561 {
701− int i, j, found;
562+ int i;
702563 ElfW(Sym) *sym;
703− ElfW(Verneed) verneed;
704− ElfW(Vernaux) aux;
564+ ElfW(Verneed) *vn = NULL;
705565 Section *symtab;
706− int sym_index, end_sym;
566+ int sym_index, end_sym, nb_versions = 2, nb_entries = 0;
567+ ElfW(Half) *versym;
707568 const char *name;
708− void *ptr;
709569
710570 /* add needed symbols */
711571 symtab = s->dynsym;
712572 end_sym = symtab->data_offset / sizeof (ElfSym);
573+ versym = section_ptr_add(versym_section, end_sym * sizeof(ElfW(Half)));
713574 for (sym_index = 0; sym_index < end_sym; ++sym_index) {
575+ int dllindex, verndx;
714576 sym = &((ElfW(Sym) *)symtab->data)[sym_index];
715577 name = (char *) symtab->link->data + sym->st_name;
716− found = 0;
717− for (i = 0; !found && i < nb_version_sym; i++) {
718− for (j = 0; !found && j < version_sym[i].nb_sym_info; j++) {
719− if (strcmp (version_sym[i].sym_info[j].name, name) == 0) {
720− found = 1;
721− add_need_sym (symtab, version_sym[i].lib, version_sym[i].version, name, sym_index);
722− }
723− }
724− }
725− if (!found) {
726− add_need_sym (symtab, NULL, NULL, NULL, sym_index);
727− }
728− symtab = s->dynsym; /* symtab can be realocated by add_need_sym */
578+ dllindex = find_elf_sym(s->dynsymtab_section, name);
579+ verndx = (dllindex && dllindex < nb_sym_to_version)
580+ ? sym_to_version[dllindex] : -1;
581+ if (verndx >= 0) {
582+ if (!sym_versions[verndx].out_index)
583+ sym_versions[verndx].out_index = nb_versions++;
584+ versym[sym_index] = sym_versions[verndx].out_index;
585+ } else
586+ versym[sym_index] = 0;
729587 }
730588 /* generate verneed section */
731− verneed_section->sh_info = nb_need;
732− for (i = 0; i < nb_need; i++) {
733− verneed.vn_version = 1;
734− verneed.vn_cnt = need[i].n_aux;
735− verneed.vn_file = need[i].offset;
736− verneed.vn_aux = sizeof (verneed);
737− verneed.vn_next = i == nb_need - 1 ? 0 : sizeof (verneed) + need[i].n_aux * sizeof (aux);
738− ptr = section_ptr_add (verneed_section, sizeof (verneed));
739− memcpy (ptr, &verneed, sizeof (verneed));
740− for (j = 0; j < need[i].n_aux; j++) {
741− aux.vna_hash = elf_hash (need[i].aux[j].version);
742− aux.vna_flags = 0;
743− aux.vna_other = need[i].aux[j].other;
744− aux.vna_name = need[i].aux[j].offset;
745− aux.vna_next = j == need[i].n_aux - 1 ? 0 : sizeof (aux);
746− ptr = section_ptr_add (verneed_section, sizeof (aux));
747− memcpy (ptr, &aux, sizeof (aux));
748− }
749− }
750− /* free all info */
751− for (i = 0; i < nb_version_sym; i++) {
752− for (j = 0; j < version_sym[i].nb_sym_info; j++) {
753− tcc_free (version_sym[i].sym_info[j].name);
754− }
755− tcc_free (version_sym[i].lib);
756− tcc_free (version_sym[i].version);
757− tcc_free (version_sym[i].sym_info);
758− }
759− tcc_free (version_sym);
760− nb_version_sym = 0;
761− version_sym = NULL;
762− for (i = 0; i < nb_need; i++) {
763− tcc_free (need[i].lib);
764− for (j = 0; j < need[i].n_aux; j++) {
765− tcc_free (need[i].aux[j].version);
766− }
767− tcc_free (need[i].aux);
589+ for (i = nb_sym_versions; i-- > 0;) {
590+ struct sym_version *sv = &sym_versions[i];
591+ int n_same_libs = 0, prev;
592+ size_t vnofs;
593+ ElfW(Vernaux) *vna;
594+ if (sv->out_index < 1)
595+ continue;
596+ vnofs = section_add(verneed_section, sizeof(*vn), 1);
597+ vn = (ElfW(Verneed)*)(verneed_section->data + vnofs);
598+ vn->vn_version = 1;
599+ vn->vn_file = put_elf_str(verneed_section->link, sv->lib);
600+ vn->vn_aux = sizeof (*vn);
601+ do {
602+ prev = sv->prev_same_lib;
603+ if (sv->out_index > 0) {
604+ vna = section_ptr_add(verneed_section, sizeof(*vna));
605+ vna->vna_hash = elf_hash (sv->version);
606+ vna->vna_flags = 0;
607+ vna->vna_other = sv->out_index;
608+ sv->out_index = -2;
609+ vna->vna_name = put_elf_str(verneed_section->link, sv->version);
610+ vna->vna_next = sizeof (*vna);
611+ n_same_libs++;
612+ }
613+ sv = &sym_versions[prev];
614+ } while(prev >= 0);
615+ vna->vna_next = 0;
616+ vn = (ElfW(Verneed)*)(verneed_section->data + vnofs);
617+ vn->vn_cnt = n_same_libs;
618+ vn->vn_next = sizeof(*vn) + n_same_libs * sizeof(*vna);
619+ nb_entries++;
768620 }
769− tcc_free (need);
770− dt_verneednum = nb_need;
771− nb_need = 0;
772− need = NULL;
621+ if (vn)
622+ vn->vn_next = 0;
623+ verneed_section->sh_info = nb_entries;
624+ dt_verneednum = nb_entries;
773625 }
774626
775627 /* add an elf symbol : check if it is already defined and patch
@@ -3052,6 +2904,53 @@ ST_FUNC int tcc_load_archive(TCCState *s1, int fd, int alacarte) · 2276 unmodified lines
30522904 }
30532905
30542906 #ifndef TCC_TARGET_PE
2907+/* Set LV[I] to the global index of sym-version (LIB,VERSION). Maybe resizes
2908+ LV, maybe create a new entry for (LIB,VERSION). */
2909+static void set_ver_to_ver(int *n, int **lv, int i, char *lib, char *version)
2910+{
2911+ while (i >= *n) {
2912+ *lv = tcc_realloc(*lv, (*n + 1) * sizeof(**lv));
2913+ (*lv)[(*n)++] = -1;
2914+ }
2915+ if ((*lv)[i] == -1) {
2916+ int v, prev_same_lib = -1;
2917+ for (v = 0; v < nb_sym_versions; v++) {
2918+ if (strcmp(sym_versions[v].lib, lib))
2919+ continue;
2920+ prev_same_lib = v;
2921+ if (!strcmp(sym_versions[v].version, version))
2922+ break;
2923+ }
2924+ if (v == nb_sym_versions) {
2925+ sym_versions = tcc_realloc (sym_versions,
2926+ (v + 1) * sizeof(*sym_versions));
2927+ sym_versions[v].lib = tcc_strdup(lib);
2928+ sym_versions[v].version = tcc_strdup(version);
2929+ sym_versions[v].out_index = 0;
2930+ sym_versions[v].prev_same_lib = prev_same_lib;
2931+ nb_sym_versions++;
2932+ }
2933+ (*lv)[i] = v;
2934+ }
2935+}
2936+
2937+/* Associates symbol SYM_INDEX (in dynsymtab) with sym-version index
2938+ VERNDX. */
2939+static void
2940+set_sym_version(int sym_index, int verndx)
2941+{
2942+ if (sym_index >= nb_sym_to_version) {
2943+ int newelems = sym_index ? sym_index * 2 : 1;
2944+ sym_to_version = tcc_realloc(sym_to_version,
2945+ newelems * sizeof(*sym_to_version));
2946+ memset(sym_to_version + nb_sym_to_version, -1,
2947+ (newelems - nb_sym_to_version) * sizeof(*sym_to_version));
2948+ nb_sym_to_version = newelems;
2949+ }
2950+ if (sym_to_version[sym_index] < 0)
2951+ sym_to_version[sym_index] = verndx;
2952+}
2953+
30552954 /* load a DLL and all referenced DLLs. 'level = 0' means that the DLL
30562955 is referenced by the user (so it should be added as DT_NEEDED in
30572956 the generated ELF file) */
@@ -3066,12 +2965,11 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) · 8 unmodified lines
30662965 ElfW(Verdef) *verdef, *vdef;
30672966 ElfW(Verneed) *verneed, *vneed;
30682967 ElfW(Half) *versym, *vsym;
3069− unsigned char *dynstr;
2968+ char *dynstr;
30702969 uint32_t next;
30712970 int sym_index;
30722971 char *lib, *version;
3073− int n_def_need = 0;
3074− struct def_need_struct *def_need = NULL;
2972+ int nb_local_ver = 0, *local_ver = NULL;
30752973 const char *name, *soname;
30762974 DLLReference *dllref;
30772975
@@ -3129,7 +3027,7 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) · 51 unmodified lines
31293027
31303028 for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
31313029 if (dt->d_tag == DT_SONAME) {
3132− soname = (char *) dynstr + dt->d_un.d_val;
3030+ soname = dynstr + dt->d_un.d_val;
31333031 }
31343032 }
31353033
@@ -3164,20 +3062,17 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) · 28 unmodified lines
31643062 vdef->vd_version, vdef->vd_flags, vdef->vd_ndx,
31653063 vdef->vd_hash);
31663064 #endif
3167− for (i = 0; i < vdef->vd_cnt; i++) {
3168− version = (char *) dynstr + verdaux->vda_name;
3065+ if (vdef->vd_cnt) {
3066+ version = dynstr + verdaux->vda_name;
31693067
3170− if (lib == NULL) {
3068+ if (lib == NULL)
31713069 lib = version;
3172− }
3173− else {
3174− save_def_need_index (&n_def_need, &def_need,
3175− vdef->vd_ndx, lib, version);
3176− }
3070+ else
3071+ set_ver_to_ver(&nb_local_ver, &local_ver, vdef->vd_ndx,
3072+ lib, version);
31773073 #if DEBUG_VERSION
31783074 printf (" verdaux(%u): %s\n", vdef->vd_ndx, version);
31793075 #endif
3180− verdaux = (ElfW(Verdaux) *) (((char *) verdaux) + verdaux->vda_next);
31813076 }
31823077 next = vdef->vd_next;
31833078 vdef = (ElfW(Verdef) *) (((char *) vdef) + next);
@@ -3189,15 +3084,15 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) · 5 unmodified lines
31893084 ElfW(Vernaux) *vernaux =
31903085 (ElfW(Vernaux) *) (((char *) vneed) + vneed->vn_aux);
31913086
3192− lib = (char *) dynstr + vneed->vn_file;
3087+ lib = dynstr + vneed->vn_file;
31933088 #if DEBUG_VERSION
31943089 printf ("verneed: %u %s\n", vneed->vn_version, lib);
31953090 #endif
31963091 for (i = 0; i < vneed->vn_cnt; i++) {
31973092 if ((vernaux->vna_other & 0x8000) == 0) { /* hidden */
3198− version = (char *) dynstr + vernaux->vna_name;
3199− save_def_need_index (&n_def_need, &def_need,
3200− vernaux->vna_other, lib, version);
3093+ version = dynstr + vernaux->vna_name;
3094+ set_ver_to_ver(&nb_local_ver, &local_ver, vernaux->vna_other,
3095+ lib, version);
32013096 #if DEBUG_VERSION
32023097 printf (" vernaux(%u): %u %u %s\n",
32033098 vernaux->vna_other, vernaux->vna_hash,
@@ -3212,10 +3107,11 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) · 8 unmodified lines
32123107 }
32133108
32143109 #if DEBUG_VERSION
3215− for (i = 0; i < n_def_need; i++) {
3216− if (def_need[i].lib) {
3110+ for (i = 0; i < nb_local_ver; i++) {
3111+ if (local_ver[i] > 0) {
32173112 printf ("%d: lib: %s, version %s\n",
3218− i, def_need[i].lib, def_need[i].version);
3113+ i, sym_versions[local_ver[i]].lib,
3114+ sym_version[local_ver[i]].version);
32193115 }
32203116 }
32213117 #endif
@@ -3231,13 +3127,12 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) · 9 unmodified lines
32313127 sym_bind = ELFW(ST_BIND)(sym->st_info);
32323128 if (sym_bind == STB_LOCAL)
32333129 continue;
3234− name = (char *) dynstr + sym->st_name;
3130+ name = dynstr + sym->st_name;
32353131 sym_index = set_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size,
32363132 sym->st_info, sym->st_other, sym->st_shndx, name);
3237− if (versym && n_def_need && (*vsym & 0x8000) == 0 &&
3238− *vsym > 0 && *vsym < n_def_need &&
3239− def_need[*vsym].lib) {
3240− add_version_sym (s1->dynsymtab_section, sym_index, n_def_need, def_need, name, *vsym);
3133+ if (versym && (*vsym & 0x8000) == 0 &&
3134+ *vsym > 0 && *vsym < nb_local_ver) {
3135+ set_sym_version(sym_index, local_ver[*vsym]);
32413136 }
32423137 }
32433138
@@ -3245,7 +3140,7 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) · 1 unmodified line
32453140 for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
32463141 switch(dt->d_tag) {
32473142 case DT_NEEDED:
3248− name = (char *) dynstr + dt->d_un.d_val;
3143+ name = dynstr + dt->d_un.d_val;
32493144 for(j = 0; j < s1->nb_loaded_dlls; j++) {
32503145 dllref = s1->loaded_dlls[j];
32513146 if (!strcmp(name, dllref->name))
@@ -3262,7 +3157,7 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) · 10 unmodified lines
32623157 }
32633158 ret = 0;
32643159 the_end:
3265− free_def_need (n_def_need, def_need);
3160+ tcc_free(local_ver);
32663161 tcc_free(dynstr);
32673162 tcc_free(dynsym);
32683163 tcc_free(dynamic);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t