Fix orig_label = label invariant in init_label_map.
[ksplice.git] / objcommon.c
blobf5d10090f7e91a68317246a617a184f1b0195797
1 /* Copyright (C) 2007-2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
2 * Copyright (C) 2008 Anders Kaseorg <andersk@mit.edu>,
3 * Tim Abbott <tabbott@mit.edu>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
16 * 02110-1301, USA.
19 #define _GNU_SOURCE
20 #include "objcommon.h"
21 #include <stdio.h>
23 void vec_do_reserve(void **data, size_t *mem_size, size_t new_size)
25 if (new_size > *mem_size || new_size * 2 < *mem_size) {
26 if (new_size < *mem_size * 2)
27 new_size = *mem_size * 2;
28 *data = realloc(*data, new_size);
29 assert(new_size == 0 || *data != NULL);
30 *mem_size = new_size;
34 void get_syms(bfd *abfd, struct asymbolp_vec *syms)
36 long storage_needed = bfd_get_symtab_upper_bound(abfd);
37 if (storage_needed == 0)
38 return;
39 assert(storage_needed >= 0);
41 vec_init(syms);
42 vec_reserve(syms, storage_needed);
43 vec_resize(syms, bfd_canonicalize_symtab(abfd, syms->data));
44 assert(syms->size >= 0);
47 struct superbfd *fetch_superbfd(bfd *abfd)
49 assert(abfd != NULL);
50 if (abfd->usrdata != NULL)
51 return abfd->usrdata;
53 struct superbfd *sbfd = malloc(sizeof(*sbfd));
54 assert(sbfd != NULL);
56 abfd->usrdata = sbfd;
57 sbfd->abfd = abfd;
58 get_syms(abfd, &sbfd->syms);
59 vec_init(&sbfd->new_syms);
60 sbfd->new_supersects = NULL;
61 return sbfd;
64 struct supersect *fetch_supersect(struct superbfd *sbfd, asection *sect)
66 assert(sect != NULL);
67 assert(!bfd_is_const_section(sect));
68 if (sect->userdata != NULL)
69 return sect->userdata;
71 struct supersect *new = malloc(sizeof(*new));
72 assert(new != NULL);
74 sect->userdata = new;
75 new->parent = sbfd;
76 new->name = sect->name;
77 new->flags = bfd_get_section_flags(sbfd->abfd, sect);
78 new->match = NULL;
79 new->new = false;
80 new->patch = false;
81 new->keep = true;
83 vec_init(&new->contents);
84 vec_resize(&new->contents, bfd_get_section_size(sect));
85 assert(bfd_get_section_contents
86 (sbfd->abfd, sect, new->contents.data, 0, new->contents.size));
87 new->alignment = bfd_get_section_alignment(sbfd->abfd, sect);
89 vec_init(&new->relocs);
90 vec_reserve(&new->relocs, bfd_get_reloc_upper_bound(sbfd->abfd, sect));
91 vec_resize(&new->relocs,
92 bfd_canonicalize_reloc(sbfd->abfd, sect, new->relocs.data,
93 sbfd->syms.data));
94 assert(new->relocs.size >= 0);
95 vec_init(&new->new_relocs);
97 vec_init(&new->syms);
98 asymbol **symp;
99 for (symp = sbfd->syms.data; symp < sbfd->syms.data + sbfd->syms.size;
100 symp++) {
101 asymbol *sym = *symp;
102 if (sym->section == sect)
103 *vec_grow(&new->syms, 1) = sym;
106 return new;
109 struct supersect *new_supersect(struct superbfd *sbfd, const char *name)
111 struct supersect *ss;
112 for (ss = sbfd->new_supersects; ss != NULL; ss = ss->next) {
113 if (strcmp(name, ss->name) == 0)
114 return ss;
117 struct supersect *new = malloc(sizeof(*new));
118 new->parent = sbfd;
119 new->name = name;
120 new->next = sbfd->new_supersects;
121 sbfd->new_supersects = new;
122 new->flags = SEC_ALLOC | SEC_HAS_CONTENTS | SEC_RELOC;
123 new->new = false;
124 new->patch = false;
125 new->keep = true;
127 vec_init(&new->contents);
128 new->alignment = 0;
129 vec_init(&new->relocs);
130 vec_init(&new->new_relocs);
132 new->type = SS_TYPE_KSPLICE;
133 return new;
136 void supersect_move(struct supersect *dest_ss, struct supersect *src_ss)
138 *dest_ss = *src_ss;
139 vec_init(&src_ss->contents);
140 vec_init(&src_ss->relocs);
141 vec_init(&src_ss->new_relocs);
142 vec_init(&src_ss->syms);
145 void *sect_do_grow(struct supersect *ss, size_t n, size_t size, int alignment)
147 if (ss->alignment < ffs(alignment) - 1)
148 ss->alignment = ffs(alignment) - 1;
149 int pad = align(ss->contents.size, alignment) - ss->contents.size;
150 void *out = vec_grow(&ss->contents, pad + n * size);
151 memset(out, 0, pad + n * size);
152 return out + pad;
155 static void mod_relocs(struct arelentp_vec *dest_relocs,
156 struct arelentp_vec *src_relocs,
157 bfd_size_type start, bfd_size_type end,
158 bfd_size_type mod)
160 arelent **relocp;
161 for (relocp = src_relocs->data;
162 relocp < src_relocs->data + src_relocs->size; relocp++) {
163 if ((*relocp)->address >= start && (*relocp)->address < end) {
164 arelent *reloc = malloc(sizeof(*reloc));
165 assert(reloc != NULL);
166 *reloc = **relocp;
167 reloc->address += mod;
168 *vec_grow(dest_relocs, 1) = reloc;
173 static void mod_symbols(struct asymbolp_vec *dest_syms,
174 struct asymbolp_vec *src_syms,
175 bfd_size_type start, bfd_size_type end,
176 bfd_size_type mod)
178 asymbol **symp;
179 for (symp = src_syms->data;
180 symp < src_syms->data + src_syms->size; symp++) {
181 /* must mutate symbols in-place since there are pointers
182 to them in relocations elsewhere */
183 asymbol *sym = *symp;
184 if (sym->value >= start && sym->value < end) {
185 sym->value += mod;
186 *vec_grow(dest_syms, 1) = sym;
191 void sect_do_copy(struct supersect *dest_ss, void *dest,
192 struct supersect *src_ss, const void *src, size_t n)
194 memcpy(dest, src, n);
195 bfd_size_type start = src - src_ss->contents.data;
196 bfd_size_type end = start + n;
197 bfd_size_type mod = (dest - dest_ss->contents.data) -
198 (src - src_ss->contents.data);
199 mod_relocs(&dest_ss->relocs, &src_ss->relocs, start, end, mod);
200 mod_relocs(&dest_ss->new_relocs, &src_ss->new_relocs, start, end, mod);
201 mod_symbols(&dest_ss->syms, &src_ss->syms, start, end, mod);
204 bfd_vma addr_offset(struct supersect *ss, const void *addr)
206 return (void *)addr - ss->contents.data;
209 bfd_vma get_reloc_offset(struct supersect *ss, arelent *reloc, bool adjust_pc)
211 int size = bfd_get_reloc_size(reloc->howto);
213 bfd_vma x = bfd_get(size * 8, ss->parent->abfd,
214 ss->contents.data + reloc->address);
215 x &= reloc->howto->src_mask;
216 x >>= reloc->howto->bitpos;
217 bfd_vma signbit = reloc->howto->dst_mask >> reloc->howto->bitpos;
218 signbit &= ~(signbit >> 1);
219 switch (reloc->howto->complain_on_overflow) {
220 case complain_overflow_signed:
221 case complain_overflow_bitfield:
222 x |= -(x & signbit);
223 break;
224 case complain_overflow_unsigned:
225 break;
226 default:
227 DIE;
229 x <<= reloc->howto->rightshift;
231 bfd_vma add = reloc->addend;
232 if (reloc->howto->pc_relative) {
233 if (!reloc->howto->pcrel_offset)
234 add += reloc->address;
235 if (adjust_pc)
236 add += size;
238 return x + add;
241 bfd_vma read_reloc(struct supersect *ss, const void *addr, size_t size,
242 asymbol **symp)
244 arelent **relocp;
245 bfd_vma val = bfd_get(size * 8, ss->parent->abfd, addr);
246 bfd_vma address = addr_offset(ss, addr);
247 for (relocp = ss->relocs.data;
248 relocp < ss->relocs.data + ss->relocs.size; relocp++) {
249 arelent *reloc = *relocp;
250 if (reloc->address == address) {
251 if (symp != NULL)
252 *symp = *reloc->sym_ptr_ptr;
253 else if (*reloc->sym_ptr_ptr !=
254 bfd_abs_section_ptr->symbol)
255 fprintf(stderr, "warning: unexpected "
256 "non-absolute relocation at %s+%lx\n",
257 ss->name, (unsigned long)address);
258 return get_reloc_offset(ss, reloc, false);
261 if (symp != NULL)
262 *symp = *bfd_abs_section_ptr->symbol_ptr_ptr;
263 return val;
266 char *str_pointer(struct supersect *ss, void *const *addr)
268 asymbol *sym;
269 bfd_vma offset = read_reloc(ss, addr, sizeof(*addr), &sym);
270 char *str;
271 assert(asprintf(&str, "%s+%lx", sym->name, (unsigned long)offset) >= 0);
272 return str;
275 const void *read_pointer(struct supersect *ss, void *const *addr,
276 struct supersect **data_ssp)
278 asymbol *sym;
279 bfd_vma offset = read_reloc(ss, addr, sizeof(*addr), &sym);
280 if (bfd_is_abs_section(sym->section) && sym->value + offset == 0)
281 return NULL;
282 if (bfd_is_const_section(sym->section)) {
283 fprintf(stderr, "warning: unexpected relocation to const "
284 "section at %s+%lx\n", ss->name,
285 (unsigned long)addr_offset(ss, addr));
286 return NULL;
288 struct supersect *data_ss = fetch_supersect(ss->parent, sym->section);
289 if (data_ssp != NULL)
290 *data_ssp = data_ss;
291 return data_ss->contents.data + sym->value + offset;
294 const char *read_string(struct supersect *ss, const char *const *addr)
296 return read_pointer(ss, (void *const *)addr, NULL);
299 struct caller_search {
300 struct superbfd *sbfd;
301 asymbol *sym;
302 asection *calling_section;
303 int count;
306 void search_for_caller(bfd *abfd, asection *sect, void *searchptr)
308 struct caller_search *search = searchptr;
309 struct superbfd *sbfd = search->sbfd;
310 struct supersect *ss = fetch_supersect(sbfd, sect);
311 arelent **relocp;
313 for (relocp = ss->relocs.data;
314 relocp < ss->relocs.data + ss->relocs.size; relocp++) {
315 asymbol *rsym = *(*relocp)->sym_ptr_ptr;
316 if (rsym->section == search->sym->section &&
317 rsym->value + get_reloc_offset(ss, *relocp, true) ==
318 search->sym->value) {
319 if (search->calling_section != sect)
320 search->count++;
321 if (search->calling_section == NULL)
322 search->calling_section = sect;
327 static const char *find_caller(struct supersect *ss, asymbol *sym)
329 struct caller_search search = {
330 .sym = sym,
331 .sbfd = ss->parent,
332 .count = 0,
333 .calling_section = NULL,
336 bfd_map_over_sections(ss->parent->abfd, search_for_caller, &search);
337 if (search.count == 1)
338 return search.calling_section->name;
339 if (search.count == 0)
340 return "*no_caller*";
341 return "*multiple_callers*";
344 asymbol **canonical_symbolp(struct superbfd *sbfd, asymbol *sym)
346 asymbol **csymp;
347 asymbol **ret = NULL;
348 for (csymp = sbfd->syms.data; csymp < sbfd->syms.data + sbfd->syms.size;
349 csymp++) {
350 asymbol *csymtemp = *csymp;
351 if (bfd_is_const_section(sym->section) && sym == csymtemp)
352 return csymp;
353 if ((csymtemp->flags & BSF_DEBUGGING) != 0 ||
354 sym->section != csymtemp->section ||
355 sym->value != csymtemp->value)
356 continue;
357 if ((csymtemp->flags & BSF_GLOBAL) != 0)
358 return csymp;
359 if (ret == NULL)
360 ret = csymp;
362 if (ret != NULL)
363 return ret;
365 /* For section symbols of sections containing no symbols, return the
366 section symbol that relocations are generated against */
367 if ((sym->flags & BSF_SECTION_SYM) != 0)
368 return &sym->section->symbol;
369 return ret;
372 asymbol *canonical_symbol(struct superbfd *sbfd, asymbol *sym)
374 asymbol **symp = canonical_symbolp(sbfd, sym);
375 return symp != NULL ? *symp : NULL;
378 char *static_local_symbol(struct superbfd *sbfd, asymbol *sym)
380 struct supersect *ss = fetch_supersect(sbfd, sym->section);
381 if ((sym->flags & BSF_LOCAL) == 0 || (sym->flags & BSF_OBJECT) == 0)
382 return NULL;
383 char *dot = strrchr(sym->name, '.');
384 if (dot == NULL || dot[1 + strspn(dot + 1, "0123546789")] != '\0')
385 return NULL;
386 char *basename = strndup(sym->name, dot - sym->name);
387 char *mangled_name;
388 if (strcmp(basename, "__func__") == 0 ||
389 strcmp(basename, "__PRETTY_FUNCTION__") == 0)
390 assert(asprintf(&mangled_name, "%s<%s>", basename,
391 (char *)ss->contents.data + sym->value) >= 0);
392 else
393 assert(asprintf(&mangled_name, "%s<%s>", basename,
394 find_caller(ss, sym)) >= 0);
395 return mangled_name;
398 char *symbol_label(struct superbfd *sbfd, asymbol *sym)
400 const char *filename = sbfd->abfd->filename;
401 char *c = strstr(filename, ".KSPLICE");
402 int flen = (c == NULL ? strlen(filename) : c - filename);
404 char *label;
405 if (bfd_is_und_section(sym->section) ||
406 (sym->flags & BSF_GLOBAL) != 0) {
407 label = strdup(sym->name);
408 } else if (bfd_is_const_section(sym->section)) {
409 assert(asprintf(&label, "%s<%.*s>",
410 sym->name, flen, filename) >= 0);
411 } else {
412 asymbol *gsym = canonical_symbol(sbfd, sym);
414 if (gsym == NULL)
415 assert(asprintf(&label, "%s+%lx<%.*s>",
416 sym->section->name,
417 (unsigned long)sym->value,
418 flen, filename) >= 0);
419 else if ((gsym->flags & BSF_GLOBAL) != 0)
420 label = strdup(gsym->name);
421 else if (static_local_symbol(sbfd, gsym))
422 assert(asprintf(&label, "%s+%lx<%.*s>",
423 static_local_symbol(sbfd, gsym),
424 (unsigned long)sym->value,
425 flen, filename) >= 0);
426 else
427 assert(asprintf(&label, "%s<%.*s>",
428 gsym->name, flen, filename) >= 0);
431 return label;