(rtld_map): New static variable.
[kopensolaris-gnu/glibc.git] / elf / dl-reloc.c
1 /* Relocate a shared object and resolve its references to other loaded objects.
2 Copyright (C) 1995 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB.  If
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, USA.  */
19
20 #include <link.h>
21 #include <sys/types.h>
22 #include <sys/mman.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include "dynamic-link.h"
26
27
28 void
29 _dl_relocate_object (struct link_map *l, int lazy)
30 {
31   const size_t pagesize = getpagesize ();
32
33   if (l->l_relocated)
34     return;
35
36   if (l->l_info[DT_TEXTREL])
37     {
38       /* Bletch.  We must make read-only segments writable
39          long enough to relocate them.  */
40       const Elf32_Phdr *ph;
41       for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
42         if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0)
43           {
44             caddr_t mapstart = ((caddr_t) l->l_addr +
45                                 (ph->p_vaddr & ~(pagesize - 1)));
46             caddr_t mapend = ((caddr_t) l->l_addr +
47                               ((ph->p_vaddr + ph->p_memsz + pagesize - 1)
48                                & ~(pagesize - 1)));
49             if (mprotect (mapstart, mapend - mapstart,
50                           PROT_READ|PROT_WRITE) < 0)
51               _dl_signal_error (errno, l->l_name,
52                                 "cannot make segment writable for relocation");
53           }
54     }
55
56   {
57     struct link_map *real_next, *scope;
58
59     const char *strtab
60       = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
61
62
63     Elf32_Addr resolve (const Elf32_Sym **ref, Elf32_Addr r_offset)
64       {
65         return _dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope,
66                                   l->l_name, (*ref)->st_value == r_offset);
67       }
68
69     real_next = l->l_next;
70     if (l->l_info[DT_SYMBOLIC])
71       {
72         if (l->l_prev)
73           l->l_prev->l_next = real_next;
74         l->l_next = _dl_loaded;
75         scope = l;
76       }
77     else
78       scope = _dl_loaded;
79
80     if (l->l_type == lt_interpreter)
81       /* We cannot be lazy when relocating the dynamic linker itself.  It
82          was previously relocated eagerly (allowing us to be running now),
83          and needs always to be fully relocated so it can run without the
84          aid of run-time fixups (because it's the one to do them), so we
85          must always re-relocate its PLT eagerly.  */
86       lazy = 0;
87
88     ELF_DYNAMIC_RELOCATE (l, lazy, resolve);
89
90     /* Restore list frobnication done above for DT_SYMBOLIC.  */
91     l->l_next = real_next;
92     if (l->l_prev)
93       l->l_prev->l_next = l;
94   }
95
96   if (l->l_info[DT_JMPREL] && lazy)
97     /* Set up the PLT so its unrelocated entries will
98        jump to _dl_runtime_resolve, which will relocate them.  */
99     elf_machine_runtime_setup (l);
100
101   l->l_relocated = 1;
102
103   if (l->l_info[DT_TEXTREL])
104     {
105       /* Undo the protection change we made before relocating.  */
106       const Elf32_Phdr *ph;
107       for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
108         if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0)
109           {
110             caddr_t mapstart = ((caddr_t) l->l_addr +
111                                 (ph->p_vaddr & ~(pagesize - 1)));
112             caddr_t mapend = ((caddr_t) l->l_addr +
113                               ((ph->p_vaddr + ph->p_memsz + pagesize - 1)
114                                & ~(pagesize - 1)));
115             int prot = 0;
116             if (ph->p_flags & PF_R)
117               prot |= PROT_READ;
118             if (ph->p_flags & PF_X)
119               prot |= PROT_EXEC;
120             if (mprotect (mapstart, mapend - mapstart, prot) < 0)
121               _dl_signal_error (errno, l->l_name,
122                                 "can't restore segment prot after reloc");
123           }
124     }
125
126 }