Subversion Repositories Kolibri OS

Rev

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
}