summary refs log tree commit diff
path: root/elf/dl-deps.c
blob: 3fbb3dbf92833cbd1299d4529341ce289090e12e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/* Load the dependencies of a mapped object.
   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <link.h>
#include <errno.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/* Whether an shared object references one or more auxiliary objects
   is signaled by the AUXTAG entry in l_info.  */
#define AUXTAG	(DT_NUM + DT_PROCNUM + DT_VERSIONTAGNUM \
		 + DT_EXTRATAGIDX (DT_AUXILIARY))


/* When loading auxiliary objects we must ignore errors.  It's ok if
   an object is missing.  */
struct openaux_args
  {
    /* The arguments to openaux.  */
    struct link_map *map;
    int trace_mode;
    const char *strtab;
    const ElfW(Dyn) *d;

    /* The return value of openaux.  */
    struct link_map *aux;
  };

static void
openaux (void *a)
{
  struct openaux_args *args = (struct openaux_args *) a;

  args->aux = _dl_map_object (args->map, args->strtab + args->d->d_un.d_val,
			      (args->map->l_type == lt_executable
			       ? lt_library : args->map->l_type),
			      args->trace_mode);
}



/* We use a very special kind of list to track the two kinds paths
   through the list of loaded shared objects.  We have to

   - produce a flat list with unique members of all involved objects

   - produce a flat list of all shared objects.
*/
struct list
  {
    int done;			/* Nonzero if this map was processed.  */
    struct link_map *map;	/* The data.  */

    struct list *unique;	/* Elements for normal list.  */
    struct list *dup;		/* Elements in complete list.  */
  };


void
_dl_map_object_deps (struct link_map *map,
		     struct link_map **preloads, unsigned int npreloads,
		     int trace_mode)
{
  struct list known[1 + npreloads + 1];
  struct list *runp, *head, *utail, *dtail;
  unsigned int nlist, nduplist, i;

  inline void preload (struct link_map *map)
    {
      known[nlist].done = 0;
      known[nlist].map = map;

      known[nlist].unique = &known[nlist + 1];
      known[nlist].dup = &known[nlist + 1];

      ++nlist;
      /* We use `l_reserved' as a mark bit to detect objects we have
	 already put in the search list and avoid adding duplicate
	 elements later in the list.  */
      map->l_reserved = 1;
    }

  /* No loaded object so far.  */
  nlist = 0;

  /* First load MAP itself.  */
  preload (map);

  /* Add the preloaded items after MAP but before any of its dependencies.  */
  for (i = 0; i < npreloads; ++i)
    preload (preloads[i]);

  /* Terminate the lists.  */
  known[nlist - 1].unique = NULL;
  known[nlist - 1].dup = NULL;

  /* Pointer to the first member of the unique and duplicate list.  */
  head = known;

  /* Pointer to last unique object.  */
  utail = &known[nlist - 1];
  /* Pointer to last loaded object.  */
  dtail = &known[nlist - 1];

  /* Until now we have the same number of libraries in the normal and
     the list with duplicates.  */
  nduplist = nlist;

  /* Process each element of the search list, loading each of its
     auxiliary objects and immediate dependencies.  Auxiliary objects
     will be added in the list before the object itself and
     dependencies will be appended to the list as we step through it.
     This produces a flat, ordered list that represents a
     breadth-first search of the dependency tree.

     The whole process is complicated by the fact that we better
     should use alloca for the temporary list elements.  But using
     alloca means we cannot use recursive function calls.  */
  for (runp = known; runp; )
    {
      struct link_map *l = runp->map;

      if (l->l_info[AUXTAG] || l->l_info[DT_NEEDED])
	{
	  const char *strtab = ((void *) l->l_addr
				+ l->l_info[DT_STRTAB]->d_un.d_ptr);
	  struct openaux_args args;
	  struct list *orig;
	  const ElfW(Dyn) *d;

	  /* Mark map as processed.  */
	  runp->done = 1;

	  args.strtab = strtab;
	  args.map = l;
	  args.trace_mode = trace_mode;
	  orig = runp;

	  for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
	    if (d->d_tag == DT_NEEDED)
	      {
		/* Map in the needed object.  */
		struct link_map *dep
		  = _dl_map_object (l, strtab + d->d_un.d_val,
				    l->l_type == lt_executable ? lt_library :
				    l->l_type, trace_mode);
		/* Allocate new entry.  */
		struct list *newp = alloca (sizeof (struct list));

		/* Add it in any case to the duplicate list.  */
		newp->map = dep;
		newp->dup = NULL;
		dtail->dup = newp;
		dtail = newp;
		++nduplist;

		if (dep->l_reserved)
		  /* This object is already in the search list we are
		     building.  Don't add a duplicate pointer.
		     Release the reference just added by
		     _dl_map_object.  */
		  --dep->l_opencount;
		else
		  {
		    /* Append DEP to the unique list.  */
		    newp->done = 0;
		    newp->unique = NULL;
		    utail->unique = newp;
		    utail = newp;
		    ++nlist;
		    /* Set the mark bit that says it's already in the list.  */
		    dep->l_reserved = 1;
		  }
	      }
	    else if (d->d_tag == DT_AUXILIARY)
	      {
		char *errstring;
		const char *objname;

		/* Store the tag in the argument structure.  */
		args.d = d;

		if (_dl_catch_error (&errstring, &objname, openaux, &args))
		  {
		    /* We are not interested in the error message.  */
		    assert (errstring != NULL);
		    free (errstring);
		  }
		else
		  {
		    /* The auxiliary object is actually available.
		       Incorporate the map in all the lists.  */

		    /* Allocate new entry.  This always has to be done.  */
		    struct list *newp = alloca (sizeof (struct list));

		    /* Copy the content of the current entry over.  */
		    memcpy (newp, orig, sizeof (*newp));

		    /* Initialize new entry.  */
		    orig->done = 0;
		    orig->map = args.aux;
		    orig->dup = newp;

		    /* We must handle two situations here: the map is new,
		       so we must add it in all three lists.  If the map
		       is already known, we have two further possibilities:
		       - if the object is before the current map in the
		         search list, we do nothing.  It is already found
			 early
		       - if the object is after the current one, we must
		         move it just before the current map to make sure
			 the symbols are found early enough
		    */
		    if (args.aux->l_reserved)
		      {
			/* The object is already somewhere in the
			   list.  Locate it first.  */
			struct list *late;

			/* This object is already in the search list
			   we are building.  Don't add a duplicate
			   pointer.  Release the reference just added
			   by _dl_map_object.  */
			--args.aux->l_opencount;

			for (late = orig; late->unique; late = late->unique)
			  if (late->unique->map == args.aux)
			    break;

			if (late->unique)
			  {
			    /* The object is somewhere behind the current
			       position in the search path.  We have to
			       move it to this earlier position.  */
			    orig->unique = newp;

			    /* Now remove the later entry from the unique
			       list.  */
			    late->unique = late->unique->unique;

			    /* We must move the earlier in the chain.  */
			    if (args.aux->l_prev)
			      args.aux->l_prev->l_next = args.aux->l_next;
			    if (args.aux->l_next)
			      args.aux->l_next->l_prev = args.aux->l_prev;

			    args.aux->l_prev = newp->map->l_prev;
			    newp->map->l_prev = args.aux;
			    if (args.aux->l_prev != NULL)
			      args.aux->l_prev->l_next = args.aux;
			    args.aux->l_next = newp->map;
			  }
			else
			  {
			    /* The object must be somewhere earlier in
			       the list.  That's good, we only have to
			       insert an entry for the duplicate list.  */
			    orig->unique = NULL;	/* Never used.  */

			    /* Now we have a problem.  The element pointing
			       to ORIG in the unique list must point to
			       NEWP now.  This is the only place where we
			       need this backreference and this situation
			       is really not that frequent.  So we don't
			       use a double-linked list but instead search
			       for the preceding element.  */
			    late = head;
			    while (late->unique != orig)
			      late = late->unique;
			    late->unique = newp;
			  }
		      }
		    else
		      {
			/* This is easy.  We just add the symbol right
			   here.  */
			orig->unique = newp;
			++nlist;
			/* Set the mark bit that says it's already in
			   the list.  */
			args.aux->l_reserved = 1;

			/* The only problem is that in the double linked
			   list of all objects we don't have this new
			   object at the correct place.  Correct this
			   here.  */
			if (args.aux->l_prev)
			  args.aux->l_prev->l_next = args.aux->l_next;
			if (args.aux->l_next)
			  args.aux->l_next->l_prev = args.aux->l_prev;

			args.aux->l_prev = newp->map->l_prev;
			newp->map->l_prev = args.aux;
			if (args.aux->l_prev != NULL)
			  args.aux->l_prev->l_next = args.aux;
			args.aux->l_next = newp->map;
		      }

		    /* Move the tail pointers if necessary.  */
		    if (orig == utail)
		      utail = newp;
		    if (orig == dtail)
		      dtail = newp;

		    /* Move on the insert point.  */
		    orig = newp;

		    /* We always add an entry to the duplicate list.  */
		    ++nduplist;
		  }
	      }
	}
      else
	/* Mark as processed.  */
	runp->done = 1;

      /* If we have no auxiliary objects just go on to the next map.  */
      if (runp->done)
	do
	  runp = runp->unique;
	while (runp && runp->done);
    }

  /* Store the search list we built in the object.  It will be used for
     searches in the scope of this object.  */
  map->l_searchlist = malloc (nlist * sizeof (struct link_map *));
  if (map->l_searchlist == NULL)
    _dl_signal_error (ENOMEM, map->l_name,
		      "cannot allocate symbol search list");
  map->l_nsearchlist = nlist;

  for (nlist = 0, runp = head; runp; runp = runp->unique)
    {
      map->l_searchlist[nlist++] = runp->map;

      /* Now clear all the mark bits we set in the objects on the search list
	 to avoid duplicates, so the next call starts fresh.  */
      runp->map->l_reserved = 0;
    }

  map->l_ndupsearchlist = nduplist;
  if (nlist == nduplist)
    map->l_dupsearchlist = map->l_searchlist;
  else
    {
      map->l_dupsearchlist = malloc (nduplist * sizeof (struct link_map *));
      if (map->l_dupsearchlist == NULL)
	_dl_signal_error (ENOMEM, map->l_name,
			  "cannot allocate symbol search list");

      for (nlist = 0, runp = head; runp; runp = runp->dup)
	map->l_dupsearchlist[nlist++] = runp->map;
    }
}