1 /* Copyright (C) 2007-2008 Jeffrey Brian 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
20 #include "objcommon.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
,
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
);
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)
56 assert(storage_needed
>= 0);
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
)
67 if (abfd
->usrdata
!= NULL
)
70 struct superbfd
*sbfd
= malloc(sizeof(*sbfd
));
75 get_syms(abfd
, &sbfd
->syms
);
76 vec_init(&sbfd
->new_syms
);
77 sbfd
->new_supersects
= NULL
;
81 struct supersect
*fetch_supersect(struct superbfd
*sbfd
, asection
*sect
)
84 assert(!bfd_is_const_section(sect
));
85 if (sect
->userdata
!= NULL
)
86 return sect
->userdata
;
88 struct supersect
*new = malloc(sizeof(*new));
93 new->name
= sect
->name
;
94 new->flags
= bfd_get_section_flags(sbfd
->abfd
, sect
);
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
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
:
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
;
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
);
275 return relocp
!= NULL
? *relocp
: NULL
;
278 bfd_vma
read_reloc(struct supersect
*ss
, const void *addr
, size_t size
,
281 bfd_vma val
= bfd_get(size
* 8, ss
->parent
->abfd
, addr
);
282 arelent
*reloc
= find_reloc(ss
, addr
);
285 *symp
= *bfd_abs_section_ptr
->symbol_ptr_ptr
;
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
)
302 bfd_vma offset
= read_reloc(ss
, addr
, sizeof(*addr
), &sym
);
303 if (bfd_is_abs_section(sym
->section
) && sym
->value
+ offset
== 0)
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
));
311 struct supersect
*data_ss
= fetch_supersect(ss
->parent
, sym
->section
);
312 if (data_ssp
!= NULL
)
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
);