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->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
,
109 assert(new->relocs
.size
>= 0);
110 vec_init(&new->new_relocs
);
112 vec_init(&new->syms
);
114 for (symp
= sbfd
->syms
.data
; symp
< sbfd
->syms
.data
+ sbfd
->syms
.size
;
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
);
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
,
132 *hash_relocp
= reloc
;
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)
146 struct supersect
*new = malloc(sizeof(*new));
149 new->next
= sbfd
->new_supersects
;
150 sbfd
->new_supersects
= new;
151 new->flags
= SEC_ALLOC
| SEC_HAS_CONTENTS
| SEC_RELOC
;
154 vec_init(&new->contents
);
157 vec_init(&new->relocs
);
158 vec_init(&new->new_relocs
);
160 new->type
= SS_TYPE_KSPLICE
;
164 void supersect_move(struct supersect
*dest_ss
, struct supersect
*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
);
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
,
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
);
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
,
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
) {
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
:
251 case complain_overflow_unsigned
:
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
;
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
);
272 return relocp
!= NULL
? *relocp
: NULL
;
275 bfd_vma
read_reloc(struct supersect
*ss
, const void *addr
, size_t size
,
278 bfd_vma val
= bfd_get(size
* 8, ss
->parent
->abfd
, addr
);
279 arelent
*reloc
= find_reloc(ss
, addr
);
282 *symp
= *bfd_abs_section_ptr
->symbol_ptr_ptr
;
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
)
299 bfd_vma offset
= read_reloc(ss
, addr
, sizeof(*addr
), &sym
);
300 if (bfd_is_abs_section(sym
->section
) && sym
->value
+ offset
== 0)
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
));
308 struct supersect
*data_ss
= fetch_supersect(ss
->parent
, sym
->section
);
309 if (data_ssp
!= NULL
)
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
);