Ksplice 0.9.4
[ksplice.git] / objcommon.c
blobffb774d9c6e17c2b159218c078d6c8d551274885
1 /* Copyright (C) 2007-2008 Jeff 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 #define arelentp_init(x) *(x) = NULL
24 IMPLEMENT_HASH_TYPE(arelent *, arelentp_hash, arelentp_hash_init,
25 arelentp_hash_free, arelentp_hash_lookup, arelentp_init);
27 #define label_mapp_init(map) *(map) = NULL
28 IMPLEMENT_HASH_TYPE(struct label_map *, label_mapp_hash, label_mapp_hash_init,
29 label_mapp_hash_free, label_mapp_hash_lookup,
30 label_mapp_init);
32 #define asymbolpp_init(symp) *(symp) = NULL
33 IMPLEMENT_HASH_TYPE(asymbol **, asymbolpp_hash, asymbolpp_hash_init,
34 asymbolpp_hash_free, asymbolpp_hash_lookup, asymbolpp_init);
36 #define string_init(str) *(str) = NULL
37 IMPLEMENT_HASH_TYPE(const char *, string_hash, string_hash_init,
38 string_hash_free, string_hash_lookup, string_init);
40 void vec_do_reserve(void **data, size_t *mem_size, size_t new_size)
42 if (new_size > *mem_size || new_size * 2 < *mem_size) {
43 if (new_size < *mem_size * 2)
44 new_size = *mem_size * 2;
45 *data = realloc(*data, new_size);
46 assert(new_size == 0 || *data != NULL);
47 *mem_size = new_size;
51 void get_syms(bfd *abfd, struct asymbolp_vec *syms)
53 long storage_needed = bfd_get_symtab_upper_bound(abfd);
54 if (storage_needed == 0)
55 return;
56 assert(storage_needed >= 0);
58 vec_init(syms);
59 vec_reserve(syms, storage_needed);
60 vec_resize(syms, bfd_canonicalize_symtab(abfd, syms->data));
61 assert(syms->size >= 0);
64 struct superbfd *fetch_superbfd(bfd *abfd)
66 assert(abfd != NULL);
67 if (abfd->usrdata != NULL)
68 return abfd->usrdata;
70 struct superbfd *sbfd = malloc(sizeof(*sbfd));
71 assert(sbfd != NULL);
73 abfd->usrdata = sbfd;
74 sbfd->abfd = abfd;
75 get_syms(abfd, &sbfd->syms);
76 vec_init(&sbfd->new_syms);
77 sbfd->new_supersects = NULL;
78 return sbfd;
81 struct supersect *fetch_supersect(struct superbfd *sbfd, asection *sect)
83 assert(sect != NULL);
84 assert(!bfd_is_const_section(sect));
85 if (sect->userdata != NULL)
86 return sect->userdata;
88 struct supersect *new = malloc(sizeof(*new));
89 assert(new != NULL);
91 sect->userdata = new;
92 new->parent = sbfd;
93 new->name = sect->name;
94 new->flags = bfd_get_section_flags(sbfd->abfd, sect);
95 new->keep = true;
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 get_reloc_offset(struct supersect *ss, arelent *reloc, bool adjust_pc)
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;
263 if (adjust_pc)
264 add += size;
266 return x + add;
269 arelent *find_reloc(struct supersect *ss, const void *addr)
271 bfd_vma address = addr_offset(ss, addr);
272 char *key = strprintf("%lx", (unsigned long)address);
273 arelent **relocp = arelentp_hash_lookup(&ss->reloc_hash, key, FALSE);
274 free(key);
275 return relocp != NULL ? *relocp : NULL;
278 bfd_vma read_reloc(struct supersect *ss, const void *addr, size_t size,
279 asymbol **symp)
281 bfd_vma val = bfd_get(size * 8, ss->parent->abfd, addr);
282 arelent *reloc = find_reloc(ss, addr);
283 if (reloc == NULL) {
284 if (symp != NULL)
285 *symp = *bfd_abs_section_ptr->symbol_ptr_ptr;
286 return val;
289 if (symp != NULL)
290 *symp = *reloc->sym_ptr_ptr;
291 else if (*reloc->sym_ptr_ptr != bfd_abs_section_ptr->symbol)
292 fprintf(stderr, "warning: unexpected "
293 "non-absolute relocation at %s+%lx\n",
294 ss->name, (unsigned long)addr_offset(ss, addr));
295 return get_reloc_offset(ss, reloc, false);
298 const void *read_pointer(struct supersect *ss, void *const *addr,
299 struct supersect **data_ssp)
301 asymbol *sym;
302 bfd_vma offset = read_reloc(ss, addr, sizeof(*addr), &sym);
303 if (bfd_is_abs_section(sym->section) && sym->value + offset == 0)
304 return NULL;
305 if (bfd_is_const_section(sym->section)) {
306 fprintf(stderr, "warning: unexpected relocation to const "
307 "section at %s+%lx\n", ss->name,
308 (unsigned long)addr_offset(ss, addr));
309 return NULL;
311 struct supersect *data_ss = fetch_supersect(ss->parent, sym->section);
312 if (data_ssp != NULL)
313 *data_ssp = data_ss;
314 return data_ss->contents.data + sym->value + offset;
317 const char *read_string(struct supersect *ss, const char *const *addr)
319 return read_pointer(ss, (void *const *)addr, NULL);