1 /* Handle symbol and library versioning.
2    Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20 
21 #include <elf.h>
22 #include <errno.h>
23 #include <libintl.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ldsodefs.h>
27 
28 #include <assert.h>
29 
30 
31 #ifndef VERSYMIDX
32 # define VERSYMIDX(tag)	(DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag))
33 #endif
34 
35 
36 #define make_string(string, rest...) \
37   ({									      \
38     const char *all[] = { string, ## rest };				      \
39     size_t len, cnt;							      \
40     char *result, *cp;							      \
41 									      \
42     len = 1;								      \
43     for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
44       len += strlen (all[cnt]);						      \
45 									      \
46     cp = result = alloca (len);						      \
47     for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
48     {                    						      \
49       cp = strcpy (cp, all[cnt]);					      \
50       cp += strlen (all[cnt]);					              \
51     }                    						      \
52 									      \
53     result;								      \
54   })
55 
56 
57 static inline struct link_map *
find_needed(const char * name,struct link_map * map)58 find_needed (const char *name, struct link_map *map)
59 {
60   struct link_map *tmap;
61   unsigned int n;
62 
63   for (tmap = _dl_loaded; tmap != NULL; tmap = tmap->l_next)
64     if (_dl_name_match_p (name, tmap))
65       return tmap;
66 
67   /* The required object is not in the global scope, look to see if it is
68      a dependency of the current object.  */
69   for (n = 0; n < map->l_searchlist.r_nlist; n++)
70     if (_dl_name_match_p (name, map->l_searchlist.r_list[n]))
71       return map->l_searchlist.r_list[n];
72 
73   /* Should never happen.  */
74   return NULL;
75 }
76 
77 
78 static int
79 internal_function
match_symbol(const char * name,ElfW (Word)hash,const char * string,struct link_map * map,int verbose,int weak)80 match_symbol (const char *name, ElfW(Word) hash, const char *string,
81 	      struct link_map *map, int verbose, int weak)
82 {
83   const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
84   ElfW(Addr) def_offset;
85   ElfW(Verdef) *def;
86   /* Initialize to make the compiler happy.  */
87   const char *errstring = NULL;
88   int result = 0;
89 
90   /* Display information about what we are doing while debugging.  */
91   if (__builtin_expect (_dl_debug_mask & DL_DEBUG_VERSIONS, 0))
92     _dl_debug_printf ("\
93 checking for version `%s' in file %s required by file %s\n",
94 		      string, map->l_name[0] ? map->l_name : _dl_argv[0],
95 		      name);
96 
97   if (__builtin_expect (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL, 0))
98     {
99       /* The file has no symbol versioning.  I.e., the dependent
100 	 object was linked against another version of this file.  We
101 	 only print a message if verbose output is requested.  */
102       if (verbose)
103 	{
104 	  /* XXX We cannot translate the messages.  */
105 	  errstring = make_string ("\
106 no version information available (required by ", name, ")");
107 	  goto call_cerror;
108 	}
109       return 0;
110     }
111 
112   def_offset = map->l_info[VERSYMIDX (DT_VERDEF)]->d_un.d_ptr;
113   assert (def_offset != 0);
114 
115   def = (ElfW(Verdef) *) ((char *) map->l_addr + def_offset);
116   while (1)
117     {
118       /* Currently the version number of the definition entry is 1.
119 	 Make sure all we see is this version.  */
120       if (__builtin_expect (def->vd_version, 1) != 1)
121 	{
122 	  char buf[20];
123 	  buf[sizeof (buf) - 1] = '\0';
124 	  /* XXX We cannot translate the message.  */
125 	  errstring = make_string ("unsupported version of Verdef record");
126 	  result = 1;
127 	  goto call_cerror;
128 	}
129 
130       /* Compare the hash values.  */
131       if (hash == def->vd_hash)
132 	{
133 	  ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux);
134 
135 	  /* To be safe, compare the string as well.  */
136 	  if (__builtin_expect (strcmp (string, strtab + aux->vda_name), 0)
137 	      == 0)
138 	    /* Bingo!  */
139 	    return 0;
140 	}
141 
142       /* If no more definitions we failed to find what we want.  */
143       if (def->vd_next == 0)
144 	break;
145 
146       /* Next definition.  */
147       def = (ElfW(Verdef) *) ((char *) def + def->vd_next);
148     }
149 
150   /* Symbol not found.  If it was a weak reference it is not fatal.  */
151   if (__builtin_expect (weak, 1))
152     {
153       if (verbose)
154 	{
155 	  /* XXX We cannot translate the message.  */
156 	  errstring = make_string ("weak version `", string,
157 				   "' not found (required by ", name, ")");
158 	  goto call_cerror;
159 	}
160       return 0;
161     }
162 
163   /* XXX We cannot translate the message.  */
164   errstring = make_string ("version `", string, "' not found (required by ",
165 			   name, ")");
166   result = 1;
167  call_cerror:
168   _dl_signal_cerror (0, map->l_name[0] ? map->l_name : _dl_argv[0], NULL,
169 		     errstring);
170   return result;
171 }
172 
173 
174 int
175 internal_function
_dl_check_map_versions(struct link_map * map,int verbose,int trace_mode)176 _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
177 {
178   int result = 0;
179   const char *strtab;
180   /* Pointer to section with needed versions.  */
181   ElfW(Dyn) *dyn;
182   /* Pointer to dynamic section with definitions.  */
183   ElfW(Dyn) *def;
184   /* We need to find out which is the highest version index used
185     in a dependecy.  */
186   unsigned int ndx_high = 0;
187   /* Initialize to make the compiler happy.  */
188   const char *errstring = NULL;
189   int errval = 0;
190 
191   /* If we don't have a string table, we must be ok.  */
192   if (map->l_info[DT_STRTAB] == NULL)
193     return 0;
194   strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
195 
196   dyn = map->l_info[VERSYMIDX (DT_VERNEED)];
197   def = map->l_info[VERSYMIDX (DT_VERDEF)];
198 
199   if (dyn != NULL)
200     {
201       /* This file requires special versions from its dependencies.  */
202       ElfW(Verneed) *ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
203 
204       /* Currently the version number of the needed entry is 1.
205 	 Make sure all we see is this version.  */
206       if (__builtin_expect (ent->vn_version, 1) != 1)
207 	{
208 	  char buf[20];
209 	  buf[sizeof (buf) - 1] = '\0';
210 	  /* XXX We cannot translate the message.  */
211 	  errstring = make_string ("unsupported version of Verneed record\n");
212 	call_error:
213 	  _dl_signal_error (errval, (*map->l_name ? map->l_name : _dl_argv[0]),
214 			    NULL, errstring);
215 	}
216 
217       while (1)
218 	{
219 	  ElfW(Vernaux) *aux;
220 	  struct link_map *needed = find_needed (strtab + ent->vn_file, map);
221 
222 	  /* If NEEDED is NULL this means a dependency was not found
223 	     and no stub entry was created.  This should never happen.  */
224 	  assert (needed != NULL);
225 
226 	  /* Make sure this is no stub we created because of a missing
227 	     dependency.  */
228 	  if (__builtin_expect (! trace_mode, 1)
229 	      || ! __builtin_expect (needed->l_faked, 0))
230 	    {
231 	      /* NEEDED is the map for the file we need.  Now look for the
232 		 dependency symbols.  */
233 	      aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
234 	      while (1)
235 		{
236 		  /* Match the symbol.  */
237 		  result |= match_symbol ((*map->l_name
238 					   ? map->l_name : _dl_argv[0]),
239 					  aux->vna_hash,
240 					  strtab + aux->vna_name,
241 					  needed, verbose,
242 					  aux->vna_flags & VER_FLG_WEAK);
243 
244 		  /* Compare the version index.  */
245 		  if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high)
246 		    ndx_high = aux->vna_other & 0x7fff;
247 
248 		  if (aux->vna_next == 0)
249 		    /* No more symbols.  */
250 		    break;
251 
252 		  /* Next symbol.  */
253 		  aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
254 		}
255 	    }
256 
257 	  if (ent->vn_next == 0)
258 	    /* No more dependencies.  */
259 	    break;
260 
261 	  /* Next dependency.  */
262 	  ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
263 	}
264     }
265 
266   /* We also must store the names of the defined versions.  Determine
267      the maximum index here as well.
268 
269      XXX We could avoid the loop by just taking the number of definitions
270      as an upper bound of new indeces.  */
271   if (def != NULL)
272     {
273       ElfW(Verdef) *ent;
274       ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
275       while (1)
276 	{
277 	  if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
278 	    ndx_high = ent->vd_ndx & 0x7fff;
279 
280 	  if (ent->vd_next == 0)
281 	    /* No more definitions.  */
282 	    break;
283 
284 	  ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
285 	}
286     }
287 
288   if (ndx_high > 0)
289     {
290       /* Now we are ready to build the array with the version names
291 	 which can be indexed by the version index in the VERSYM
292 	 section.  */
293       map->l_versions = (struct r_found_version *)
294 	calloc (ndx_high + 1, sizeof (*map->l_versions));
295       if (__builtin_expect (map->l_versions == NULL, 0))
296 	{
297 	  errstring = N_("cannot allocate version reference table");
298 	  errval = ENOMEM;
299 	  goto call_error;
300 	}
301 
302       /* Store the number of available symbols.  */
303       map->l_nversions = ndx_high + 1;
304 
305       /* Compute the pointer to the version symbols.  */
306       map->l_versyms = (void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
307 
308       if (dyn != NULL)
309 	{
310 	  ElfW(Verneed) *ent;
311 	  ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
312 	  while (1)
313 	    {
314 	      ElfW(Vernaux) *aux;
315 	      aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
316 	      while (1)
317 		{
318 		  ElfW(Half) ndx = aux->vna_other & 0x7fff;
319 		  map->l_versions[ndx].hash = aux->vna_hash;
320 		  map->l_versions[ndx].hidden = aux->vna_other & 0x8000;
321 		  map->l_versions[ndx].name = &strtab[aux->vna_name];
322 		  map->l_versions[ndx].filename = &strtab[ent->vn_file];
323 
324 		  if (aux->vna_next == 0)
325 		    /* No more symbols.  */
326 		    break;
327 
328 		  /* Advance to next symbol.  */
329 		  aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
330 		}
331 
332 	      if (ent->vn_next == 0)
333 		/* No more dependencies.  */
334 		break;
335 
336 	      /* Advance to next dependency.  */
337 	      ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
338 	    }
339 	}
340 
341       /* And insert the defined versions.  */
342       if (def != NULL)
343 	{
344 	  ElfW(Verdef) *ent;
345 	  ent = (ElfW(Verdef)  *) (map->l_addr + def->d_un.d_ptr);
346 	  while (1)
347 	    {
348 	      ElfW(Verdaux) *aux;
349 	      aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
350 
351 	      if ((ent->vd_flags & VER_FLG_BASE) == 0)
352 		{
353 		  /* The name of the base version should not be
354 		     available for matching a versioned symbol.  */
355 		  ElfW(Half) ndx = ent->vd_ndx & 0x7fff;
356 		  map->l_versions[ndx].hash = ent->vd_hash;
357 		  map->l_versions[ndx].name = &strtab[aux->vda_name];
358 		  map->l_versions[ndx].filename = NULL;
359 		}
360 
361 	      if (ent->vd_next == 0)
362 		/* No more definitions.  */
363 		break;
364 
365 	      ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
366 	    }
367 	}
368     }
369 
370   return result;
371 }
372 
373 
374 int
375 internal_function
_dl_check_all_versions(struct link_map * map,int verbose,int trace_mode)376 _dl_check_all_versions (struct link_map *map, int verbose, int trace_mode)
377 {
378   struct link_map *l;
379   int result = 0;
380 
381   for (l = map; l != NULL; l = l->l_next)
382     result |= ! l->l_faked && _dl_check_map_versions (l, verbose, trace_mode);
383 
384   return result;
385 }
386