Rev 5197 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5197 | serge | 1 | /* Native Client support for ELF |
6324 | serge | 2 | Copyright (C) 2012-2015 Free Software Foundation, Inc. |
5197 | serge | 3 | |
4 | This file is part of BFD, the Binary File Descriptor library. |
||
5 | |||
6 | This program is free software; you can redistribute it and/or modify |
||
7 | it under the terms of the GNU General Public License as published by |
||
8 | the Free Software Foundation; either version 3 of the License, or |
||
9 | (at your option) any later version. |
||
10 | |||
11 | This program is distributed in the hope that it will be useful, |
||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | GNU General Public License for more details. |
||
15 | |||
16 | You should have received a copy of the GNU General Public License |
||
17 | along with this program; if not, write to the Free Software |
||
18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
||
19 | MA 02111-1307, USA. */ |
||
20 | |||
21 | #include "sysdep.h" |
||
22 | #include "bfd.h" |
||
23 | #include "libbfd.h" |
||
24 | #include "elf-bfd.h" |
||
25 | #include "elf-nacl.h" |
||
26 | #include "elf/common.h" |
||
27 | #include "elf/internal.h" |
||
28 | |||
29 | static bfd_boolean |
||
30 | segment_executable (struct elf_segment_map *seg) |
||
31 | { |
||
32 | if (seg->p_flags_valid) |
||
33 | return (seg->p_flags & PF_X) != 0; |
||
34 | else |
||
35 | { |
||
36 | /* The p_flags value has not been computed yet, |
||
37 | so we have to look through the sections. */ |
||
38 | unsigned int i; |
||
39 | for (i = 0; i < seg->count; ++i) |
||
40 | if (seg->sections[i]->flags & SEC_CODE) |
||
41 | return TRUE; |
||
42 | } |
||
43 | return FALSE; |
||
44 | } |
||
45 | |||
46 | /* Determine if this segment is eligible to receive the file and program |
||
47 | headers. It must be read-only and non-executable. |
||
48 | Its first section must start far enough past the page boundary to |
||
49 | allow space for the headers. */ |
||
50 | static bfd_boolean |
||
51 | segment_eligible_for_headers (struct elf_segment_map *seg, |
||
52 | bfd_vma minpagesize, bfd_vma sizeof_headers) |
||
53 | { |
||
54 | unsigned int i; |
||
55 | if (seg->count == 0 || seg->sections[0]->lma % minpagesize < sizeof_headers) |
||
56 | return FALSE; |
||
57 | for (i = 0; i < seg->count; ++i) |
||
58 | { |
||
59 | if ((seg->sections[i]->flags & (SEC_CODE|SEC_READONLY)) != SEC_READONLY) |
||
60 | return FALSE; |
||
61 | } |
||
62 | return TRUE; |
||
63 | } |
||
64 | |||
65 | |||
66 | /* We permute the segment_map to get BFD to do the file layout we want: |
||
67 | The first non-executable PT_LOAD segment appears first in the file |
||
68 | and contains the ELF file header and phdrs. */ |
||
69 | bfd_boolean |
||
70 | nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info) |
||
71 | { |
||
72 | const struct elf_backend_data *const bed = get_elf_backend_data (abfd); |
||
73 | struct elf_segment_map **m = &elf_seg_map (abfd); |
||
74 | struct elf_segment_map **first_load = NULL; |
||
75 | struct elf_segment_map **last_load = NULL; |
||
76 | bfd_boolean moved_headers = FALSE; |
||
77 | int sizeof_headers; |
||
78 | |||
79 | if (info != NULL && info->user_phdrs) |
||
80 | /* The linker script used PHDRS explicitly, so don't change what the |
||
81 | user asked for. */ |
||
82 | return TRUE; |
||
83 | |||
84 | if (info != NULL) |
||
85 | /* We're doing linking, so evalute SIZEOF_HEADERS as in a linker script. */ |
||
86 | sizeof_headers = bfd_sizeof_headers (abfd, info); |
||
87 | else |
||
88 | { |
||
89 | /* We're not doing linking, so this is objcopy or suchlike. |
||
90 | We just need to collect the size of the existing headers. */ |
||
91 | struct elf_segment_map *seg; |
||
92 | sizeof_headers = bed->s->sizeof_ehdr; |
||
93 | for (seg = *m; seg != NULL; seg = seg->next) |
||
94 | sizeof_headers += bed->s->sizeof_phdr; |
||
95 | } |
||
96 | |||
97 | while (*m != NULL) |
||
98 | { |
||
99 | struct elf_segment_map *seg = *m; |
||
100 | |||
101 | if (seg->p_type == PT_LOAD) |
||
102 | { |
||
103 | bfd_boolean executable = segment_executable (seg); |
||
104 | |||
105 | if (executable |
||
106 | && seg->count > 0 |
||
107 | && seg->sections[0]->vma % bed->minpagesize == 0) |
||
108 | { |
||
109 | asection *lastsec = seg->sections[seg->count - 1]; |
||
110 | bfd_vma end = lastsec->vma + lastsec->size; |
||
111 | if (end % bed->minpagesize != 0) |
||
112 | { |
||
113 | /* This is an executable segment that starts on a page |
||
114 | boundary but does not end on a page boundary. Fill |
||
115 | it out to a whole page with code fill (the tail of |
||
116 | the segment will not be within any section). Thus |
||
117 | the entire code segment can be mapped from the file |
||
118 | as whole pages and that mapping will contain only |
||
119 | valid instructions. |
||
120 | |||
121 | To accomplish this, we must fake out the code in |
||
122 | assign_file_positions_for_load_sections (elf.c) so |
||
123 | that it advances past the rest of the final page, |
||
124 | rather than trying to put the next (unaligned, or |
||
125 | unallocated) section. We do this by appending a |
||
126 | dummy section record to this element in the segment |
||
127 | map. No such output section ever actually exists, |
||
128 | but this gets the layout logic to advance the file |
||
129 | positions past this partial page. Since we are |
||
130 | lying to BFD like this, nothing will ever know to |
||
131 | write the section contents. So we do that by hand |
||
132 | after the fact, in nacl_final_write_processing, below. */ |
||
133 | |||
134 | struct elf_segment_map *newseg; |
||
135 | asection *sec; |
||
136 | struct bfd_elf_section_data *secdata; |
||
137 | |||
138 | BFD_ASSERT (!seg->p_size_valid); |
||
139 | |||
140 | secdata = bfd_zalloc (abfd, sizeof *secdata); |
||
141 | if (secdata == NULL) |
||
142 | return FALSE; |
||
143 | |||
144 | sec = bfd_zalloc (abfd, sizeof *sec); |
||
145 | if (sec == NULL) |
||
146 | return FALSE; |
||
147 | |||
148 | /* Fill in only the fields that actually affect the logic |
||
149 | in assign_file_positions_for_load_sections. */ |
||
150 | sec->vma = end; |
||
151 | sec->lma = lastsec->lma + lastsec->size; |
||
152 | sec->size = bed->minpagesize - (end % bed->minpagesize); |
||
153 | sec->flags = (SEC_ALLOC | SEC_LOAD |
||
154 | | SEC_READONLY | SEC_CODE | SEC_LINKER_CREATED); |
||
155 | sec->used_by_bfd = secdata; |
||
156 | |||
157 | secdata->this_hdr.sh_type = SHT_PROGBITS; |
||
158 | secdata->this_hdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; |
||
159 | secdata->this_hdr.sh_addr = sec->vma; |
||
160 | secdata->this_hdr.sh_size = sec->size; |
||
161 | |||
162 | newseg = bfd_alloc (abfd, |
||
163 | sizeof *newseg + ((seg->count + 1) |
||
164 | * sizeof (asection *))); |
||
165 | if (newseg == NULL) |
||
166 | return FALSE; |
||
167 | memcpy (newseg, seg, |
||
168 | sizeof *newseg + (seg->count * sizeof (asection *))); |
||
169 | newseg->sections[newseg->count++] = sec; |
||
170 | *m = seg = newseg; |
||
171 | } |
||
172 | } |
||
173 | |||
174 | /* First, we're just finding the earliest PT_LOAD. |
||
175 | By the normal rules, this will be the lowest-addressed one. |
||
176 | We only have anything interesting to do if it's executable. */ |
||
177 | last_load = m; |
||
178 | if (first_load == NULL) |
||
179 | { |
||
180 | if (!executable) |
||
181 | goto next; |
||
182 | first_load = m; |
||
183 | } |
||
184 | /* Now that we've noted the first PT_LOAD, we're looking for |
||
185 | the first non-executable PT_LOAD with a nonempty p_filesz. */ |
||
186 | else if (!moved_headers |
||
187 | && segment_eligible_for_headers (seg, bed->minpagesize, |
||
188 | sizeof_headers)) |
||
189 | { |
||
190 | /* This is the one we were looking for! |
||
191 | |||
192 | First, clear the flags on previous segments that |
||
193 | say they include the file header and phdrs. */ |
||
194 | struct elf_segment_map *prevseg; |
||
195 | for (prevseg = *first_load; |
||
196 | prevseg != seg; |
||
197 | prevseg = prevseg->next) |
||
198 | if (prevseg->p_type == PT_LOAD) |
||
199 | { |
||
200 | prevseg->includes_filehdr = 0; |
||
201 | prevseg->includes_phdrs = 0; |
||
202 | } |
||
203 | |||
204 | /* This segment will include those headers instead. */ |
||
205 | seg->includes_filehdr = 1; |
||
206 | seg->includes_phdrs = 1; |
||
207 | |||
208 | moved_headers = TRUE; |
||
209 | } |
||
210 | } |
||
211 | |||
212 | next: |
||
213 | m = &seg->next; |
||
214 | } |
||
215 | |||
216 | if (first_load != last_load && moved_headers) |
||
217 | { |
||
218 | /* Now swap the first and last PT_LOAD segments' |
||
219 | positions in segment_map. */ |
||
220 | struct elf_segment_map *first = *first_load; |
||
221 | struct elf_segment_map *last = *last_load; |
||
222 | *first_load = first->next; |
||
223 | first->next = last->next; |
||
224 | last->next = first; |
||
225 | } |
||
226 | |||
227 | return TRUE; |
||
228 | } |
||
229 | |||
230 | /* After nacl_modify_segment_map has done its work, the file layout has |
||
231 | been done as we wanted. But the PT_LOAD phdrs are no longer in the |
||
232 | proper order for the ELF rule that they must appear in ascending address |
||
233 | order. So find the two segments we swapped before, and swap them back. */ |
||
234 | bfd_boolean |
||
235 | nacl_modify_program_headers (bfd *abfd, struct bfd_link_info *info) |
||
236 | { |
||
237 | struct elf_segment_map **m = &elf_seg_map (abfd); |
||
238 | Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr; |
||
239 | Elf_Internal_Phdr *p = phdr; |
||
240 | |||
241 | if (info != NULL && info->user_phdrs) |
||
242 | /* The linker script used PHDRS explicitly, so don't change what the |
||
243 | user asked for. */ |
||
244 | return TRUE; |
||
245 | |||
246 | /* Find the PT_LOAD that contains the headers (should be the first). */ |
||
247 | while (*m != NULL) |
||
248 | { |
||
249 | if ((*m)->p_type == PT_LOAD && (*m)->includes_filehdr) |
||
250 | break; |
||
251 | |||
252 | m = &(*m)->next; |
||
253 | ++p; |
||
254 | } |
||
255 | |||
256 | if (*m != NULL) |
||
257 | { |
||
258 | struct elf_segment_map **first_load_seg = m; |
||
259 | Elf_Internal_Phdr *first_load_phdr = p; |
||
260 | struct elf_segment_map **next_load_seg = NULL; |
||
261 | Elf_Internal_Phdr *next_load_phdr = NULL; |
||
262 | |||
263 | /* Now move past that first one and find the PT_LOAD that should be |
||
264 | before it by address order. */ |
||
265 | |||
266 | m = &(*m)->next; |
||
267 | ++p; |
||
268 | |||
269 | while (*m != NULL) |
||
270 | { |
||
271 | if (p->p_type == PT_LOAD && p->p_vaddr < first_load_phdr->p_vaddr) |
||
272 | { |
||
273 | next_load_seg = m; |
||
274 | next_load_phdr = p; |
||
275 | break; |
||
276 | } |
||
277 | |||
278 | m = &(*m)->next; |
||
279 | ++p; |
||
280 | } |
||
281 | |||
282 | /* Swap their positions in the segment_map back to how they used to be. |
||
283 | The phdrs have already been set up by now, so we have to slide up |
||
284 | the earlier ones to insert the one that should be first. */ |
||
285 | if (next_load_seg != NULL) |
||
286 | { |
||
287 | Elf_Internal_Phdr move_phdr; |
||
288 | struct elf_segment_map *first_seg = *first_load_seg; |
||
289 | struct elf_segment_map *next_seg = *next_load_seg; |
||
290 | struct elf_segment_map *first_next = first_seg->next; |
||
291 | struct elf_segment_map *next_next = next_seg->next; |
||
292 | |||
293 | if (next_load_seg == &first_seg->next) |
||
294 | { |
||
295 | *first_load_seg = next_seg; |
||
296 | next_seg->next = first_seg; |
||
297 | first_seg->next = next_next; |
||
298 | } |
||
299 | else |
||
300 | { |
||
301 | *first_load_seg = first_next; |
||
302 | *next_load_seg = next_next; |
||
303 | |||
304 | first_seg->next = *next_load_seg; |
||
305 | *next_load_seg = first_seg; |
||
306 | |||
307 | next_seg->next = *first_load_seg; |
||
308 | *first_load_seg = next_seg; |
||
309 | } |
||
310 | |||
311 | move_phdr = *next_load_phdr; |
||
312 | memmove (first_load_phdr + 1, first_load_phdr, |
||
313 | (next_load_phdr - first_load_phdr) * sizeof move_phdr); |
||
314 | *first_load_phdr = move_phdr; |
||
315 | } |
||
316 | } |
||
317 | |||
318 | return TRUE; |
||
319 | } |
||
320 | |||
321 | void |
||
322 | nacl_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED) |
||
323 | { |
||
324 | struct elf_segment_map *seg; |
||
325 | for (seg = elf_seg_map (abfd); seg != NULL; seg = seg->next) |
||
326 | if (seg->p_type == PT_LOAD |
||
327 | && seg->count > 1 |
||
328 | && seg->sections[seg->count - 1]->owner == NULL) |
||
329 | { |
||
330 | /* This is a fake section added in nacl_modify_segment_map, above. |
||
331 | It's not a real BFD section, so nothing wrote its contents. |
||
332 | Now write out its contents. */ |
||
333 | |||
334 | asection *sec = seg->sections[seg->count - 1]; |
||
335 | char *fill; |
||
336 | |||
337 | BFD_ASSERT (sec->flags & SEC_LINKER_CREATED); |
||
338 | BFD_ASSERT (sec->flags & SEC_CODE); |
||
339 | BFD_ASSERT (sec->size > 0); |
||
340 | |||
341 | fill = abfd->arch_info->fill (sec->size, bfd_big_endian (abfd), TRUE); |
||
342 | |||
343 | if (fill == NULL |
||
344 | || bfd_seek (abfd, sec->filepos, SEEK_SET) != 0 |
||
345 | || bfd_bwrite (fill, sec->size, abfd) != sec->size) |
||
346 | { |
||
347 | /* We don't have a proper way to report an error here. So |
||
348 | instead fudge things so that elf_write_shdrs_and_ehdr will |
||
349 | fail. */ |
||
350 | elf_elfheader (abfd)->e_shoff = (file_ptr) -1; |
||
351 | } |
||
352 | |||
353 | free (fill); |
||
354 | } |
||
355 | }>>>> |