Makefile.ksplice: Rewrite ksplice-cow-check as a wrapper function.
[ksplice.git] / objcommon.c
blob9557086a617bcc3795d5e4797a421995f540b4fd
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->symbol = sect->symbol;
97 vec_init(&new->contents);
98 vec_resize(&new->contents, bfd_get_section_size(sect));
99 assert(bfd_get_section_contents
100 (sbfd->abfd, sect, new->contents.data, 0, new->contents.size));
101 new->alignment = bfd_get_section_alignment(sbfd->abfd, sect);
102 new->entsize = sect->entsize;
104 vec_init(&new->relocs);
105 vec_reserve(&new->relocs, bfd_get_reloc_upper_bound(sbfd->abfd, sect));
106 vec_resize(&new->relocs,
107 bfd_canonicalize_reloc(sbfd->abfd, sect, new->relocs.data,
108 sbfd->syms.data));
109 assert(new->relocs.size >= 0);
110 vec_init(&new->new_relocs);
112 vec_init(&new->syms);
113 asymbol **symp;
114 for (symp = sbfd->syms.data; symp < sbfd->syms.data + sbfd->syms.size;
115 symp++) {
116 asymbol *sym = *symp;
117 if (sym->section == sect && (sym->flags & BSF_SECTION_SYM) == 0)
118 *vec_grow(&new->syms, 1) = sym;
121 vec_init(&new->spans);
123 arelentp_hash_init(&new->reloc_hash);
124 arelent **relocp;
125 for (relocp = new->relocs.data;
126 relocp < new->relocs.data + new->relocs.size; relocp++) {
127 arelent *reloc = *relocp;
128 char *key = strprintf("%lx", (unsigned long)reloc->address);
129 arelent **hash_relocp = arelentp_hash_lookup(&new->reloc_hash,
130 key, TRUE);
131 free(key);
132 *hash_relocp = reloc;
135 return new;
138 struct supersect *new_supersect(struct superbfd *sbfd, const char *name)
140 struct supersect *ss;
141 for (ss = sbfd->new_supersects; ss != NULL; ss = ss->next) {
142 if (strcmp(name, ss->name) == 0)
143 return ss;
146 struct supersect *new = malloc(sizeof(*new));
147 new->parent = sbfd;
148 new->name = name;
149 new->next = sbfd->new_supersects;
150 sbfd->new_supersects = new;
151 new->flags = SEC_ALLOC | SEC_HAS_CONTENTS | SEC_RELOC;
152 new->keep = true;
154 vec_init(&new->contents);
155 new->alignment = 0;
156 new->entsize = 0;
157 vec_init(&new->relocs);
158 vec_init(&new->new_relocs);
160 new->type = SS_TYPE_KSPLICE;
161 return new;
164 void supersect_move(struct supersect *dest_ss, struct supersect *src_ss)
166 *dest_ss = *src_ss;
167 vec_init(&src_ss->contents);
168 vec_init(&src_ss->relocs);
169 vec_init(&src_ss->new_relocs);
170 vec_init(&src_ss->syms);
173 void *sect_do_grow(struct supersect *ss, size_t n, size_t size, int alignment)
175 if (ss->alignment < ffs(alignment) - 1)
176 ss->alignment = ffs(alignment) - 1;
177 int pad = align(ss->contents.size, alignment) - ss->contents.size;
178 void *out = vec_grow(&ss->contents, pad + n * size);
179 memset(out, 0, pad + n * size);
180 return out + pad;
183 static void mod_relocs(struct arelentp_vec *dest_relocs,
184 struct arelentp_vec *src_relocs,
185 bfd_size_type start, bfd_size_type end,
186 bfd_size_type mod)
188 arelent **relocp;
189 for (relocp = src_relocs->data;
190 relocp < src_relocs->data + src_relocs->size; relocp++) {
191 if ((*relocp)->address >= start && (*relocp)->address < end) {
192 arelent *reloc = malloc(sizeof(*reloc));
193 assert(reloc != NULL);
194 *reloc = **relocp;
195 reloc->address += mod;
196 *vec_grow(dest_relocs, 1) = reloc;
201 static void mod_symbols(struct asymbolp_vec *dest_syms,
202 struct asymbolp_vec *src_syms,
203 bfd_size_type start, bfd_size_type end,
204 bfd_size_type mod)
206 asymbol **symp;
207 for (symp = src_syms->data;
208 symp < src_syms->data + src_syms->size; symp++) {
209 /* must mutate symbols in-place since there are pointers
210 to them in relocations elsewhere */
211 asymbol *sym = *symp;
212 if (sym->value >= start && sym->value < end) {
213 sym->value += mod;
214 *vec_grow(dest_syms, 1) = sym;
219 void sect_do_copy(struct supersect *dest_ss, void *dest,
220 struct supersect *src_ss, const void *src, size_t n)
222 memcpy(dest, src, n);
223 bfd_size_type start = addr_offset(src_ss, src);
224 bfd_size_type end = start + n;
225 bfd_size_type mod = addr_offset(dest_ss, dest) - start;
226 mod_relocs(&dest_ss->relocs, &src_ss->relocs, start, end, mod);
227 mod_relocs(&dest_ss->new_relocs, &src_ss->new_relocs, start, end, mod);
228 mod_symbols(&dest_ss->syms, &src_ss->syms, start, end, mod);
231 bfd_vma addr_offset(struct supersect *ss, const void *addr)
233 return (void *)addr - ss->contents.data;
236 bfd_vma reloc_offset(struct supersect *ss, arelent *reloc)
238 int size = bfd_get_reloc_size(reloc->howto);
240 bfd_vma x = bfd_get(size * 8, ss->parent->abfd,
241 ss->contents.data + reloc->address);
242 x &= reloc->howto->src_mask;
243 x >>= reloc->howto->bitpos;
244 bfd_vma signbit = reloc->howto->dst_mask >> reloc->howto->bitpos;
245 signbit &= ~(signbit >> 1);
246 switch (reloc->howto->complain_on_overflow) {
247 case complain_overflow_signed:
248 case complain_overflow_bitfield:
249 x |= -(x & signbit);
250 break;
251 case complain_overflow_unsigned:
252 break;
253 default:
254 DIE;
256 x <<= reloc->howto->rightshift;
258 bfd_vma add = reloc->addend;
259 if (reloc->howto->pc_relative) {
260 if (!reloc->howto->pcrel_offset)
261 add += reloc->address;
263 return x + add;
266 arelent *find_reloc(struct supersect *ss, const void *addr)
268 bfd_vma address = addr_offset(ss, addr);
269 char *key = strprintf("%lx", (unsigned long)address);
270 arelent **relocp = arelentp_hash_lookup(&ss->reloc_hash, key, FALSE);
271 free(key);
272 return relocp != NULL ? *relocp : NULL;
275 bfd_vma read_reloc(struct supersect *ss, const void *addr, size_t size,
276 asymbol **symp)
278 bfd_vma val = bfd_get(size * 8, ss->parent->abfd, addr);
279 arelent *reloc = find_reloc(ss, addr);
280 if (reloc == NULL) {
281 if (symp != NULL)
282 *symp = *bfd_abs_section_ptr->symbol_ptr_ptr;
283 return val;
286 if (symp != NULL)
287 *symp = *reloc->sym_ptr_ptr;
288 else if (*reloc->sym_ptr_ptr != bfd_abs_section_ptr->symbol)
289 fprintf(stderr, "warning: unexpected "
290 "non-absolute relocation at %s+%lx\n",
291 ss->name, (unsigned long)addr_offset(ss, addr));
292 return reloc_offset(ss, reloc);
295 const void *read_pointer(struct supersect *ss, void *const *addr,
296 struct supersect **data_ssp)
298 asymbol *sym;
299 bfd_vma offset = read_reloc(ss, addr, sizeof(*addr), &sym);
300 if (bfd_is_abs_section(sym->section) && sym->value + offset == 0)
301 return NULL;
302 if (bfd_is_const_section(sym->section)) {
303 fprintf(stderr, "warning: unexpected relocation to const "
304 "section at %s+%lx\n", ss->name,
305 (unsigned long)addr_offset(ss, addr));
306 return NULL;
308 struct supersect *data_ss = fetch_supersect(ss->parent, sym->section);
309 if (data_ssp != NULL)
310 *data_ssp = data_ss;
311 return data_ss->contents.data + sym->value + offset;
314 const char *read_string(struct supersect *ss, const char *const *addr)
316 return read_pointer(ss, (void *const *)addr, NULL);