about summary refs log tree commit diff
path: root/elf/dl-lookup.c
blob: 90c9b6a18df1617e634fe4119a4af1005e0bcc07 (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
/* Look up a symbol in the loaded objects.
Copyright (C) 1995, 1996 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., 675 Mass Ave,
Cambridge, MA 02139, USA.  */

#include <stddef.h>
#include <libelf.h>
#include <link.h>
#include <assert.h>

/* Search loaded objects' symbol tables for a definition of the symbol
   UNDEF_NAME.  If NOPLT is nonzero, then a PLT entry cannot satisfy the
   reference; some different binding must be found.  */

Elf32_Addr
_dl_lookup_symbol (const char *undef_name, const Elf32_Sym **ref,
		   struct link_map *symbol_scope,
		   const char *reference_name,
		   int noplt)
{
  unsigned long int hash = elf_hash (undef_name);
  struct link_map *map;
  struct
    {
      Elf32_Addr a;
      const Elf32_Sym *s;
    } weak_value = { 0, NULL };

  /* Search the relevant loaded objects for a definition.  */
  for (map = symbol_scope; map; map = map->l_next)
    {
      const Elf32_Sym *symtab;
      const char *strtab;
      Elf32_Word symidx;

      symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
      strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);

      /* Search the appropriate hash bucket in this object's symbol table
	 for a definition for the same symbol name.  */
      for (symidx = map->l_buckets[hash % map->l_nbuckets];
	   symidx != STN_UNDEF;
	   symidx = map->l_chain[symidx])
	{
	  const Elf32_Sym *sym = &symtab[symidx];

	  if (sym->st_value == 0 || /* No value.  */
	      (noplt && sym->st_shndx == SHN_UNDEF)) /* Unwanted PLT entry.  */
	    continue;

	  switch (ELF32_ST_TYPE (sym->st_info))
	    {
	    case STT_NOTYPE:
	    case STT_FUNC:
	    case STT_OBJECT:
	      break;
	    default:
	      /* Not a code/data definition.  */
	      continue;
	    }

	  if (sym != *ref && strcmp (strtab + sym->st_name, undef_name))
	    /* Not the symbol we are looking for.  */
	    continue;

	  switch (ELF32_ST_BIND (sym->st_info))
	    {
	    case STB_GLOBAL:
	      /* Global definition.  Just what we need.  */
	      *ref = sym;
	      return map->l_addr;
	    case STB_WEAK:
	      /* Weak definition.  Use this value if we don't find another.  */
	      if (! weak_value.s)
		{
		  weak_value.s = sym;
		  weak_value.a = map->l_addr;
		}
	      break;
	    default:
	      /* Local symbols are ignored.  */
	      break;
	    }
	}
    }

  if (weak_value.s == NULL && ELF32_ST_BIND ((*ref)->st_info) != STB_WEAK)
    {
      const char msg[] = "undefined symbol: ";
      char buf[sizeof msg + strlen (undef_name)];
      memcpy (buf, msg, sizeof msg - 1);
      memcpy (&buf[sizeof msg - 1], undef_name, sizeof buf - sizeof msg + 1);
      _dl_signal_error (0, reference_name, buf);
    }

  *ref = weak_value.s;
  return weak_value.a;
}


/* Cache the location of MAP's hash table.  */

void
_dl_setup_hash (struct link_map *map)
{
  Elf32_Word *hash = (void *) map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr;
  Elf32_Word nchain;
  map->l_nbuckets = *hash++;
  nchain = *hash++;
  map->l_buckets = hash;
  hash += map->l_nbuckets;
  map->l_chain = hash;
}