Test CONFIG_FUNCTION_TRACER instead of CONFIG_FTRACE.
[ksplice.git] / objcommon.c
blob9e033d01dbeed0956184d9eb8c82b2d371fb69bb
1 /* Copyright (C) 2007-2009 Ksplice, Inc.
2 * Authors: Jeff Arnold, Anders Kaseorg, Tim Abbott
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
15 * 02110-1301, USA.
18 #define _GNU_SOURCE
19 #include "objcommon.h"
20 #include <stdio.h>
22 #define arelentp_init(x) *(x) = NULL
23 IMPLEMENT_HASH_TYPE(arelent *, arelentp_hash, arelentp_hash_init,
24 arelentp_hash_free, arelentp_hash_lookup, arelentp_init);
26 #define label_mapp_init(map) *(map) = NULL
27 IMPLEMENT_HASH_TYPE(struct label_map *, label_mapp_hash, label_mapp_hash_init,
28 label_mapp_hash_free, label_mapp_hash_lookup,
29 label_mapp_init);
31 #define asymbolpp_init(symp) *(symp) = NULL
32 IMPLEMENT_HASH_TYPE(asymbol **, asymbolpp_hash, asymbolpp_hash_init,
33 asymbolpp_hash_free, asymbolpp_hash_lookup, asymbolpp_init);
35 #define string_init(str) *(str) = NULL
36 IMPLEMENT_HASH_TYPE(const char *, string_hash, string_hash_init,
37 string_hash_free, string_hash_lookup, string_init);
39 void vec_do_reserve(void **data, size_t *mem_size, size_t new_size)
41 if (new_size > *mem_size || new_size * 2 < *mem_size) {
42 if (new_size < *mem_size * 2)
43 new_size = *mem_size * 2;
44 *data = realloc(*data, new_size);
45 assert(new_size == 0 || *data != NULL);
46 *mem_size = new_size;
50 void get_syms(bfd *abfd, struct asymbolp_vec *syms)
52 long storage_needed = bfd_get_symtab_upper_bound(abfd);
53 if (storage_needed == 0)
54 return;
55 assert(storage_needed >= 0);
57 vec_init(syms);
58 vec_reserve(syms, storage_needed);
59 vec_resize(syms, bfd_canonicalize_symtab(abfd, syms->data));
60 assert(syms->size >= 0);
63 struct superbfd *fetch_superbfd(bfd *abfd)
65 assert(abfd != NULL);
66 if (abfd->usrdata != NULL)
67 return abfd->usrdata;
69 struct superbfd *sbfd = malloc(sizeof(*sbfd));
70 assert(sbfd != NULL);
72 abfd->usrdata = sbfd;
73 sbfd->abfd = abfd;
74 get_syms(abfd, &sbfd->syms);
75 vec_init(&sbfd->new_syms);
76 sbfd->new_supersects = NULL;
77 return sbfd;
80 struct supersect *fetch_supersect(struct superbfd *sbfd, asection *sect)
82 assert(sect != NULL);
83 assert(!bfd_is_const_section(sect));
84 if (sect->userdata != NULL)
85 return sect->userdata;
87 struct supersect *new = malloc(sizeof(*new));
88 assert(new != NULL);
90 sect->userdata = new;
91 new->parent = sbfd;
92 new->name = sect->name;
93 new->flags = bfd_get_section_flags(sbfd->abfd, sect);
94 new->keep = true;
95 new->match_data_early = false;
96 new->symbol = sect->symbol;
98 vec_init(&new->contents);
99 vec_resize(&new->contents, bfd_get_section_size(sect));
100 assert(bfd_get_section_contents
101 (sbfd->abfd, sect, new->contents.data, 0, new->contents.size));
102 new->alignment = bfd_get_section_alignment(sbfd->abfd, sect);
103 new->entsize = sect->entsize;
105 vec_init(&new->relocs);
106 vec_reserve(&new->relocs, bfd_get_reloc_upper_bound(sbfd->abfd, sect));
107 vec_resize(&new->relocs,
108 bfd_canonicalize_reloc(sbfd->abfd, sect, new->relocs.data,
109 sbfd->syms.data));
110 assert(new->relocs.size >= 0);
111 vec_init(&new->new_relocs);
113 vec_init(&new->syms);
114 asymbol **symp;
115 for (symp = sbfd->syms.data; symp < sbfd->syms.data + sbfd->syms.size;
116 symp++) {
117 asymbol *sym = *symp;
118 if (sym->section == sect && (sym->flags & BSF_SECTION_SYM) == 0)
119 *vec_grow(&new->syms, 1) = sym;
122 vec_init(&new->spans);
124 arelentp_hash_init(&new->reloc_hash);
125 arelent **relocp;
126 for (relocp = new->relocs.data;
127 relocp < new->relocs.data + new->relocs.size; relocp++) {
128 arelent *reloc = *relocp;
129 char *key = strprintf("%lx", (unsigned long)reloc->address);
130 arelent **hash_relocp = arelentp_hash_lookup(&new->reloc_hash,
131 key, TRUE);
132 free(key);
133 *hash_relocp = reloc;
136 return new;
139 struct supersect *new_supersect(struct superbfd *sbfd, const char *name)
141 struct supersect *ss;
142 for (ss = sbfd->new_supersects; ss != NULL; ss = ss->next) {
143 if (strcmp(name, ss->name) == 0)
144 return ss;
147 struct supersect *new = malloc(sizeof(*new));
148 new->parent = sbfd;
149 new->name = name;
150 new->next = sbfd->new_supersects;
151 sbfd->new_supersects = new;
152 new->flags = SEC_ALLOC | SEC_HAS_CONTENTS | SEC_RELOC;
153 new->keep = true;
155 vec_init(&new->contents);
156 new->alignment = 0;
157 new->entsize = 0;
158 vec_init(&new->relocs);
159 vec_init(&new->new_relocs);
161 new->type = SS_TYPE_KSPLICE;
162 return new;
165 void supersect_move(struct supersect *dest_ss, struct supersect *src_ss)
167 *dest_ss = *src_ss;
168 vec_init(&src_ss->contents);
169 vec_init(&src_ss->relocs);
170 vec_init(&src_ss->new_relocs);
171 vec_init(&src_ss->syms);
174 void *sect_do_grow(struct supersect *ss, size_t n, size_t size, int alignment)
176 if (ss->alignment < ffs(alignment) - 1)
177 ss->alignment = ffs(alignment) - 1;
178 int pad = align(ss->contents.size, alignment) - ss->contents.size;
179 void *out = vec_grow(&ss->contents, pad + n * size);
180 memset(out, 0, pad + n * size);
181 return out + pad;
184 static void mod_relocs(struct arelentp_vec *dest_relocs,
185 struct arelentp_vec *src_relocs,
186 bfd_size_type start, bfd_size_type end,
187 bfd_size_type mod)
189 arelent **relocp;
190 for (relocp = src_relocs->data;
191 relocp < src_relocs->data + src_relocs->size; relocp++) {
192 if ((*relocp)->address >= start && (*relocp)->address < end) {
193 arelent *reloc = malloc(sizeof(*reloc));
194 assert(reloc != NULL);
195 *reloc = **relocp;
196 reloc->address += mod;
197 *vec_grow(dest_relocs, 1) = reloc;
202 static void mod_symbols(struct asymbolp_vec *dest_syms,
203 struct asymbolp_vec *src_syms,
204 bfd_size_type start, bfd_size_type end,
205 bfd_size_type mod)
207 asymbol **symp;
208 for (symp = src_syms->data;
209 symp < src_syms->data + src_syms->size; symp++) {
210 /* must mutate symbols in-place since there are pointers
211 to them in relocations elsewhere */
212 asymbol *sym = *symp;
213 if (sym->value >= start && sym->value < end) {
214 sym->value += mod;
215 *vec_grow(dest_syms, 1) = sym;
220 void sect_do_copy(struct supersect *dest_ss, void *dest,
221 struct supersect *src_ss, const void *src, size_t n)
223 memcpy(dest, src, n);
224 bfd_size_type start = addr_offset(src_ss, src);
225 bfd_size_type end = start + n;
226 bfd_size_type mod = addr_offset(dest_ss, dest) - start;
227 mod_relocs(&dest_ss->relocs, &src_ss->relocs, start, end, mod);
228 mod_relocs(&dest_ss->new_relocs, &src_ss->new_relocs, start, end, mod);
229 mod_symbols(&dest_ss->syms, &src_ss->syms, start, end, mod);
232 bfd_vma addr_offset(struct supersect *ss, const void *addr)
234 return (void *)addr - ss->contents.data;
237 bfd_vma reloc_offset(struct supersect *ss, arelent *reloc)
239 int size = bfd_get_reloc_size(reloc->howto);
241 bfd_vma x = bfd_get(size * 8, ss->parent->abfd,
242 ss->contents.data + reloc->address);
243 x &= reloc->howto->src_mask;
244 x >>= reloc->howto->bitpos;
245 bfd_vma signbit = reloc->howto->dst_mask >> reloc->howto->bitpos;
246 signbit &= ~(signbit >> 1);
247 switch (reloc->howto->complain_on_overflow) {
248 case complain_overflow_signed:
249 case complain_overflow_bitfield:
250 x |= -(x & signbit);
251 break;
252 case complain_overflow_unsigned:
253 break;
254 default:
255 DIE;
257 x <<= reloc->howto->rightshift;
259 bfd_vma add = reloc->addend;
260 if (reloc->howto->pc_relative) {
261 if (!reloc->howto->pcrel_offset)
262 add += reloc->address;
264 return x + add;
267 arelent *find_reloc(struct supersect *ss, const void *addr)
269 bfd_vma address = addr_offset(ss, addr);
270 char *key = strprintf("%lx", (unsigned long)address);
271 arelent **relocp = arelentp_hash_lookup(&ss->reloc_hash, key, FALSE);
272 free(key);
273 return relocp != NULL ? *relocp : NULL;
276 bfd_vma read_reloc(struct supersect *ss, const void *addr, size_t size,
277 asymbol **symp)
279 bfd_vma val = bfd_get(size * 8, ss->parent->abfd, addr);
280 arelent *reloc = find_reloc(ss, addr);
281 if (reloc == NULL) {
282 if (symp != NULL)
283 *symp = *bfd_abs_section_ptr->symbol_ptr_ptr;
284 return val;
287 if (symp != NULL)
288 *symp = *reloc->sym_ptr_ptr;
289 else if (*reloc->sym_ptr_ptr != bfd_abs_section_ptr->symbol)
290 fprintf(stderr, "warning: unexpected "
291 "non-absolute relocation at %s+%lx\n",
292 ss->name, (unsigned long)addr_offset(ss, addr));
293 return reloc_offset(ss, reloc);
296 const void *read_pointer(struct supersect *ss, void *const *addr,
297 struct supersect **data_ssp)
299 asymbol *sym;
300 bfd_vma offset = read_reloc(ss, addr, sizeof(*addr), &sym);
301 if (bfd_is_abs_section(sym->section) && sym->value + offset == 0)
302 return NULL;
303 if (bfd_is_const_section(sym->section)) {
304 fprintf(stderr, "warning: unexpected relocation to const "
305 "section at %s+%lx\n", ss->name,
306 (unsigned long)addr_offset(ss, addr));
307 return NULL;
309 struct supersect *data_ss = fetch_supersect(ss->parent, sym->section);
310 if (data_ssp != NULL)
311 *data_ssp = data_ss;
312 return data_ss->contents.data + sym->value + offset;
315 const char *read_string(struct supersect *ss, const char *const *addr)
317 return read_pointer(ss, (void *const *)addr, NULL);