Adjust for monotone.
[platform/upstream/elfutils.git] / libdwfl / relocate.c
1 /* Relocate debug information.
2    Copyright (C) 2005 Red Hat, Inc.
3
4    This program is Open Source software; you can redistribute it and/or
5    modify it under the terms of the Open Software License version 1.0 as
6    published by the Open Source Initiative.
7
8    You should have received a copy of the Open Software License along
9    with this program; if not, you may obtain a copy of the Open Software
10    License version 1.0 from http://www.opensource.org/licenses/osl.php or
11    by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
12    3001 King Ranch Road, Ukiah, CA 95482.   */
13
14 #include "libdwflP.h"
15
16 typedef uint8_t GElf_Byte;
17
18 /* Adjust *VALUE to add the load address of the SHNDX section.
19    We update the section header in place to cache the result.  */
20
21 Dwfl_Error
22 internal_function_def
23 __libdwfl_relocate_value (Dwfl_Module *mod, size_t symshstrndx,
24                           Elf32_Word shndx, GElf_Addr *value)
25 {
26   Elf_Scn *refscn = elf_getscn (mod->symfile->elf, shndx);
27   GElf_Shdr refshdr_mem, *refshdr = gelf_getshdr (refscn, &refshdr_mem);
28   if (refshdr == NULL)
29     return DWFL_E_LIBELF;
30
31   if ((refshdr->sh_flags & SHF_ALLOC) && refshdr->sh_addr == 0)
32     {
33       /* This is a loaded section.  Find its actual
34          address and update the section header.  */
35       const char *name = elf_strptr (mod->symfile->elf, symshstrndx,
36                                      refshdr->sh_name);
37       if (name == NULL)
38         return DWFL_E_LIBELF;
39
40       if ((*mod->dwfl->callbacks->section_address) (MODCB_ARGS (mod), name,
41                                                     &refshdr->sh_addr))
42         return CBFAIL;
43
44       if (refshdr->sh_addr == 0)
45         /* The callback resolved this to zero, indicating it wasn't
46            really loaded but we don't really care.  Mark it so we
47            don't check it again for the next relocation.  */
48         refshdr->sh_flags &= ~SHF_ALLOC;
49
50       /* Update the in-core file's section header to show the final
51          load address (or unloadedness).  This serves as a cache,
52          so we won't get here again for the same section.  */
53       if (! gelf_update_shdr (refscn, refshdr))
54         return DWFL_E_LIBELF;
55     }
56
57   /* Apply the adjustment.  */
58   *value += refshdr->sh_addr;
59   return DWFL_E_NOERROR;
60 }
61
62 Dwfl_Error
63 internal_function_def
64 __libdwfl_relocate (Dwfl_Module *mod)
65 {
66   assert (mod->isrel);
67
68   GElf_Ehdr ehdr_mem;
69   const GElf_Ehdr *ehdr = gelf_getehdr (mod->debug.elf, &ehdr_mem);
70   if (ehdr == NULL)
71     return DWFL_E_LIBELF;
72
73   size_t symshstrndx, d_shstrndx;
74   if (elf_getshstrndx (mod->symfile->elf, &symshstrndx) < 0)
75     return DWFL_E_LIBELF;
76   if (mod->symfile == &mod->debug)
77     d_shstrndx = symshstrndx;
78   else if (elf_getshstrndx (mod->debug.elf, &d_shstrndx) < 0)
79     return DWFL_E_LIBELF;
80
81   /* Look at each section in the debuginfo file, and process the
82      relocation sections for debugging sections.  */
83   Dwfl_Error result = DWFL_E_NO_DWARF;
84   Elf_Scn *scn = NULL;
85   while ((scn = elf_nextscn (mod->debug.elf, scn)) != NULL)
86     {
87       GElf_Shdr shdr_mem;
88       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
89
90       if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
91         {
92           /* It's a relocation section.  First, fetch the name of the
93              section these relocations apply to.  */
94
95           Elf_Scn *tscn = elf_getscn (mod->debug.elf, shdr->sh_info);
96           if (tscn == NULL)
97             return DWFL_E_LIBELF;
98
99           GElf_Shdr tshdr_mem;
100           GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
101           const char *tname = elf_strptr (mod->debug.elf, d_shstrndx,
102                                           tshdr->sh_name);
103           if (tname == NULL)
104             return DWFL_E_LIBELF;
105
106           if (! ebl_debugscn_p (mod->ebl, tname))
107             /* This relocation section is not for a debugging section.
108                Nothing to do here.  */
109             continue;
110
111           /* Fetch the section data that needs the relocations applied.  */
112           Elf_Data *tdata = elf_rawdata (tscn, NULL);
113           if (tdata == NULL)
114             return DWFL_E_LIBELF;
115
116           /* Apply one relocation.  Returns true for any invalid data.  */
117           Dwfl_Error relocate (GElf_Addr offset, const GElf_Sxword *addend,
118                                int rtype, int symndx)
119             {
120               /* First, resolve the symbol to an absolute value.  */
121               GElf_Addr value;
122               inline Dwfl_Error adjust (GElf_Word shndx)
123                 {
124                   return __libdwfl_relocate_value (mod, symshstrndx,
125                                                    shndx, &value);
126                 }
127
128               if (symndx == STN_UNDEF)
129                 /* When strip removes a section symbol referring to a
130                    section moved into the debuginfo file, it replaces
131                    that symbol index in relocs with STN_UNDEF.  We
132                    don't actually need the symbol, because those relocs
133                    are always references relative to the nonallocated
134                    debugging sections, which start at zero.  */
135                 value = 0;
136               else
137                 {
138                   GElf_Sym sym_mem;
139                   GElf_Word shndx;
140                   GElf_Sym *sym = gelf_getsymshndx (mod->symdata,
141                                                     mod->symxndxdata,
142                                                     symndx, &sym_mem,
143                                                     &shndx);
144                   if (sym == NULL)
145                     return DWFL_E_LIBELF;
146
147                   value = sym->st_value;
148                   if (sym->st_shndx != SHN_XINDEX)
149                     shndx = sym->st_shndx;
150                   switch (shndx)
151                     {
152                     case SHN_ABS:
153                       break;
154
155                     case SHN_UNDEF:
156                     case SHN_COMMON:
157                       return DWFL_E_RELUNDEF;
158
159                     default:
160                       {
161                         Dwfl_Error error = adjust (shndx);
162                         if (error != DWFL_E_NOERROR)
163                           return error;
164                         break;
165                       }
166                     }
167                 }
168
169               /* These are the types we can relocate.  */
170 #define TYPES           DO_TYPE (BYTE, Byte) DO_TYPE (HALF, Half) \
171                         DO_TYPE (WORD, Word) DO_TYPE (SWORD, Sword) \
172                         DO_TYPE (XWORD, Xword) DO_TYPE (SXWORD, Sxword)
173               size_t size;
174               Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
175               switch (type)
176                 {
177 #define DO_TYPE(NAME, Name)                                                   \
178                     case ELF_T_##NAME:                                        \
179                       size = sizeof (GElf_##Name);                            \
180                       break;
181                   TYPES
182 #undef DO_TYPE
183                     default:
184                   return DWFL_E_BADRELTYPE;
185                 }
186
187               if (offset + size >= tdata->d_size)
188                 return DWFL_E_BADRELOFF;
189
190 #define DO_TYPE(NAME, Name) GElf_##Name Name;
191               union { TYPES } tmpbuf;
192 #undef DO_TYPE
193               Elf_Data tmpdata =
194                 {
195                   .d_type = type,
196                   .d_buf = &tmpbuf,
197                   .d_size = size,
198                   .d_version = EV_CURRENT,
199                 };
200               Elf_Data rdata =
201                 {
202                   .d_type = type,
203                   .d_buf = tdata->d_buf + offset,
204                   .d_size = size,
205                   .d_version = EV_CURRENT,
206                 };
207
208               /* XXX check for overflow? */
209               if (addend)
210                 {
211                   /* For the addend form, we have the value already.  */
212                   value += *addend;
213                   switch (type)
214                     {
215 #define DO_TYPE(NAME, Name)                                                   \
216                         case ELF_T_##NAME:                                    \
217                           tmpbuf.Name = value;                                \
218                           break;
219                       TYPES
220 #undef DO_TYPE
221                         default:
222                       abort ();
223                     }
224                 }
225               else
226                 {
227                   /* Extract the original value and apply the reloc.  */
228                   Elf_Data *d = gelf_xlatetom (mod->main.elf, &tmpdata, &rdata,
229                                                ehdr->e_ident[EI_DATA]);
230                   if (d == NULL)
231                     return DWFL_E_LIBELF;
232                   assert (d == &tmpdata);
233                   switch (type)
234                     {
235 #define DO_TYPE(NAME, Name)                                                   \
236                         case ELF_T_##NAME:                                    \
237                           tmpbuf.Name += (GElf_##Name) value;                 \
238                           break;
239                       TYPES
240 #undef DO_TYPE
241                         default:
242                       abort ();
243                     }
244                 }
245
246               /* Now convert the relocated datum back to the target
247                  format.  This will write into rdata.d_buf, which
248                  points into the raw section data being relocated.  */
249               Elf_Data *s = gelf_xlatetof (mod->main.elf, &rdata, &tmpdata,
250                                            ehdr->e_ident[EI_DATA]);
251               if (s == NULL)
252                 return DWFL_E_LIBELF;
253               assert (s == &rdata);
254
255               /* We have applied this relocation!  */
256               return DWFL_E_NOERROR;
257             }
258
259           /* Fetch the relocation section and apply each reloc in it.  */
260           Elf_Data *reldata = elf_getdata (scn, NULL);
261           if (reldata == NULL)
262             return DWFL_E_LIBELF;
263
264           result = DWFL_E_NOERROR;
265           size_t nrels = shdr->sh_size / shdr->sh_entsize;
266           if (shdr->sh_type == SHT_REL)
267             for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
268               {
269                 GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem);
270                 if (r == NULL)
271                   return DWFL_E_LIBELF;
272                 result = relocate (r->r_offset, NULL,
273                                    GELF_R_TYPE (r->r_info),
274                                    GELF_R_SYM (r->r_info));
275               }
276           else
277             for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
278               {
279                 GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx,
280                                                        &rela_mem);
281                 if (r == NULL)
282                   return DWFL_E_LIBELF;
283                 result = relocate (r->r_offset, &r->r_addend,
284                                    GELF_R_TYPE (r->r_info),
285                                    GELF_R_SYM (r->r_info));
286               }
287           if (result != DWFL_E_NOERROR)
288             break;
289         }
290     }
291
292   return result;
293 }