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
19 #include "objcommon.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
,
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
);
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)
55 assert(storage_needed
>= 0);
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
)
66 if (abfd
->usrdata
!= NULL
)
69 struct superbfd
*sbfd
= malloc(sizeof(*sbfd
));
74 get_syms(abfd
, &sbfd
->syms
);
75 vec_init(&sbfd
->new_syms
);
76 sbfd
->new_supersects
= NULL
;
80 struct supersect
*fetch_supersect(struct superbfd
*sbfd
, asection
*sect
)
83 assert(!bfd_is_const_section(sect
));
84 if (sect
->userdata
!= NULL
)
85 return sect
->userdata
;
87 struct supersect
*new = malloc(sizeof(*new));
92 new->name
= sect
->name
;
93 new->flags
= bfd_get_section_flags(sbfd
->abfd
, sect
);
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
,
110 assert(new->relocs
.size
>= 0);
111 vec_init(&new->new_relocs
);
113 vec_init(&new->syms
);
115 for (symp
= sbfd
->syms
.data
; symp
< sbfd
->syms
.data
+ sbfd
->syms
.size
;
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
);
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
,
133 *hash_relocp
= reloc
;
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)
147 struct supersect
*new = malloc(sizeof(*new));
150 new->next
= sbfd
->new_supersects
;
151 sbfd
->new_supersects
= new;
152 new->flags
= SEC_ALLOC
| SEC_HAS_CONTENTS
| SEC_RELOC
;
155 vec_init(&new->contents
);
158 vec_init(&new->relocs
);
159 vec_init(&new->new_relocs
);
161 new->type
= SS_TYPE_KSPLICE
;
165 void supersect_move(struct supersect
*dest_ss
, struct supersect
*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
);
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
,
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
);
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
,
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
) {
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
:
252 case complain_overflow_unsigned
:
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
;
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
);
273 return relocp
!= NULL
? *relocp
: NULL
;
276 bfd_vma
read_reloc(struct supersect
*ss
, const void *addr
, size_t size
,
279 bfd_vma val
= bfd_get(size
* 8, ss
->parent
->abfd
, addr
);
280 arelent
*reloc
= find_reloc(ss
, addr
);
283 *symp
= *bfd_abs_section_ptr
->symbol_ptr_ptr
;
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
)
300 bfd_vma offset
= read_reloc(ss
, addr
, sizeof(*addr
), &sym
);
301 if (bfd_is_abs_section(sym
->section
) && sym
->value
+ offset
== 0)
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
));
309 struct supersect
*data_ss
= fetch_supersect(ss
->parent
, sym
->section
);
310 if (data_ssp
!= NULL
)
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
);