Statically allocate trampoline storage.
[ksplice.git] / objcommon.c
blobdd586087e1af89a2cf73e00ff04cbaa188f43b85
1 /* Copyright (C) 2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License, version 2.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
14 * 02110-1301, USA.
17 #define _GNU_SOURCE
18 #include "objcommon.h"
19 #include <stdio.h>
21 void vec_do_reserve(void **data, size_t *mem_size, size_t new_size)
23 if (new_size > *mem_size || new_size * 2 < *mem_size) {
24 if (new_size < *mem_size * 2)
25 new_size = *mem_size * 2;
26 *data = realloc(*data, new_size);
27 assert(new_size == 0 || *data != NULL);
28 *mem_size = new_size;
32 void get_syms(bfd *abfd, struct asymbolp_vec *syms)
34 long storage_needed = bfd_get_symtab_upper_bound(abfd);
35 if (storage_needed == 0)
36 return;
37 assert(storage_needed >= 0);
39 vec_init(syms);
40 vec_reserve(syms, storage_needed);
41 vec_resize(syms, bfd_canonicalize_symtab(abfd, syms->data));
42 assert(syms->size >= 0);
45 struct superbfd *fetch_superbfd(bfd *abfd)
47 assert(abfd != NULL);
48 if (abfd->usrdata != NULL)
49 return abfd->usrdata;
51 struct superbfd *sbfd = malloc(sizeof(*sbfd));
52 assert(sbfd != NULL);
54 abfd->usrdata = sbfd;
55 sbfd->abfd = abfd;
56 get_syms(abfd, &sbfd->syms);
57 sbfd->new_supersects = NULL;
58 return sbfd;
61 struct supersect *fetch_supersect(struct superbfd *sbfd, asection *sect)
63 assert(sect != NULL);
64 if (sect->userdata != NULL)
65 return sect->userdata;
67 struct supersect *new = malloc(sizeof(*new));
68 assert(new != NULL);
70 sect->userdata = new;
71 new->parent = sbfd;
72 new->name = sect->name;
73 new->flags = bfd_get_section_flags(sbfd->abfd, sect);
75 vec_init(&new->contents);
76 vec_resize(&new->contents, bfd_get_section_size(sect));
77 assert(bfd_get_section_contents
78 (sbfd->abfd, sect, new->contents.data, 0, new->contents.size));
79 new->alignment = bfd_get_section_alignment(sbfd->abfd, sect);
81 vec_init(&new->relocs);
82 vec_reserve(&new->relocs, bfd_get_reloc_upper_bound(sbfd->abfd, sect));
83 vec_resize(&new->relocs,
84 bfd_canonicalize_reloc(sbfd->abfd, sect, new->relocs.data,
85 sbfd->syms.data));
86 assert(new->relocs.size >= 0);
87 vec_init(&new->new_relocs);
89 return new;
92 struct supersect *new_supersect(struct superbfd *sbfd, const char *name)
94 struct supersect *ss;
95 for (ss = sbfd->new_supersects; ss != NULL; ss = ss->next) {
96 if (strcmp(name, ss->name) == 0)
97 return ss;
100 struct supersect *new = malloc(sizeof(*new));
101 new->parent = sbfd;
102 new->name = name;
103 new->next = sbfd->new_supersects;
104 sbfd->new_supersects = new;
105 new->flags = SEC_ALLOC | SEC_HAS_CONTENTS | SEC_RELOC;
107 vec_init(&new->contents);
108 new->alignment = 0;
109 vec_init(&new->relocs);
110 vec_init(&new->new_relocs);
112 return new;
115 void supersect_move(struct supersect *dest_ss, struct supersect *src_ss)
117 *dest_ss = *src_ss;
118 vec_init(&src_ss->contents);
119 vec_init(&src_ss->relocs);
120 vec_init(&src_ss->new_relocs);
123 void *sect_do_grow(struct supersect *ss, size_t n, size_t size, int alignment)
125 if (ss->alignment < ffs(alignment) - 1)
126 ss->alignment = ffs(alignment) - 1;
127 int pad = align(ss->contents.size, alignment) - ss->contents.size;
128 void *out = vec_grow(&ss->contents, pad + n * size);
129 memset(out, 0, pad + n * size);
130 return out + pad;
133 static void mod_relocs(struct arelentp_vec *dest_relocs,
134 struct arelentp_vec *src_relocs,
135 bfd_size_type start, bfd_size_type end,
136 bfd_size_type mod)
138 arelent **relocp;
139 for (relocp = src_relocs->data;
140 relocp < src_relocs->data + src_relocs->size; relocp++) {
141 if ((*relocp)->address >= start && (*relocp)->address < end) {
142 arelent *reloc = malloc(sizeof(*reloc));
143 assert(reloc != NULL);
144 *reloc = **relocp;
145 reloc->address += mod;
146 *vec_grow(dest_relocs, 1) = reloc;
151 void sect_do_copy(struct supersect *dest_ss, void *dest,
152 struct supersect *src_ss, const void *src, size_t n)
154 memcpy(dest, src, n);
155 bfd_size_type start = src - src_ss->contents.data;
156 bfd_size_type end = start + n;
157 bfd_size_type mod = (dest - dest_ss->contents.data) -
158 (src - src_ss->contents.data);
159 mod_relocs(&dest_ss->relocs, &src_ss->relocs, start, end, mod);
160 mod_relocs(&dest_ss->new_relocs, &src_ss->new_relocs, start, end, mod);
163 bfd_vma addr_offset(struct supersect *ss, const void *addr)
165 return (void *)addr - ss->contents.data;
168 bfd_vma get_reloc_offset(struct supersect *ss, arelent *reloc, int adjust_pc)
170 int size = bfd_get_reloc_size(reloc->howto);
172 bfd_vma x = bfd_get(size * 8, ss->parent->abfd,
173 ss->contents.data + reloc->address);
174 x &= reloc->howto->src_mask;
175 x >>= reloc->howto->bitpos;
176 bfd_vma signbit = reloc->howto->dst_mask >> reloc->howto->bitpos;
177 signbit &= ~(signbit >> 1);
178 switch (reloc->howto->complain_on_overflow) {
179 case complain_overflow_signed:
180 case complain_overflow_bitfield:
181 x |= -(x & signbit);
182 break;
183 case complain_overflow_unsigned:
184 break;
185 default:
186 DIE;
188 x <<= reloc->howto->rightshift;
190 bfd_vma add = reloc->addend;
191 if (reloc->howto->pc_relative) {
192 if (!reloc->howto->pcrel_offset)
193 add += reloc->address;
194 if (adjust_pc)
195 add += size;
197 return x + add;
200 bfd_vma read_reloc(struct supersect *ss, const void *addr, size_t size,
201 asymbol **symp)
203 arelent **relocp;
204 bfd_vma val = bfd_get(size * 8, ss->parent->abfd, addr);
205 bfd_vma address = addr_offset(ss, addr);
206 for (relocp = ss->relocs.data;
207 relocp < ss->relocs.data + ss->relocs.size; relocp++) {
208 arelent *reloc = *relocp;
209 if (reloc->address == address) {
210 if (symp != NULL)
211 *symp = *reloc->sym_ptr_ptr;
212 else if (*reloc->sym_ptr_ptr !=
213 bfd_abs_section_ptr->symbol)
214 fprintf(stderr, "warning: unexpected "
215 "non-absolute relocation at %s+%lx\n",
216 ss->name, (unsigned long)address);
217 return get_reloc_offset(ss, reloc, 0);
220 if (symp != NULL)
221 *symp = *bfd_abs_section_ptr->symbol_ptr_ptr;
222 return val;
225 char *str_pointer(struct supersect *ss, void *const *addr)
227 asymbol *sym;
228 bfd_vma offset = read_reloc(ss, addr, sizeof(*addr), &sym);
229 char *str;
230 assert(asprintf(&str, "%s+%lx", sym->name, (unsigned long)offset) >= 0);
231 return str;
234 const void *read_pointer(struct supersect *ss, void *const *addr,
235 struct supersect **data_ssp)
237 asymbol *sym;
238 bfd_vma offset = read_reloc(ss, addr, sizeof(*addr), &sym);
239 struct supersect *data_ss = fetch_supersect(ss->parent, sym->section);
240 if (bfd_is_abs_section(sym->section) && sym->value + offset == 0)
241 return NULL;
242 if (bfd_is_const_section(sym->section)) {
243 fprintf(stderr, "warning: unexpected relocation to const "
244 "section at %s+%lx\n", data_ss->name,
245 (unsigned long)addr_offset(data_ss, addr));
246 return NULL;
248 if (data_ssp != NULL)
249 *data_ssp = data_ss;
250 return data_ss->contents.data + sym->value + offset;
253 const char *read_string(struct supersect *ss, const char *const *addr)
255 return read_pointer(ss, (void *const *)addr, NULL);