xref: /dragonfly/contrib/gdb-7/bfd/elf-nacl.c (revision cfd1aba3)
1 /* Native Client support for ELF
2    Copyright 2012 Free Software Foundation, Inc.
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 "elf-bfd.h"
24 #include "elf-nacl.h"
25 #include "elf/common.h"
26 #include "elf/internal.h"
27 
28 static bfd_boolean
29 segment_executable (struct elf_segment_map *seg)
30 {
31   if (seg->p_flags_valid)
32     return (seg->p_flags & PF_X) != 0;
33   else
34     {
35       /* The p_flags value has not been computed yet,
36          so we have to look through the sections.  */
37       unsigned int i;
38       for (i = 0; i < seg->count; ++i)
39         if (seg->sections[i]->flags & SEC_CODE)
40           return TRUE;
41     }
42   return FALSE;
43 }
44 
45 /* Determine if this segment is eligible to receive the file and program
46    headers.  It must be read-only, non-executable, and have contents.
47    Its first section must start far enough past the page boundary to
48    allow space for the headers.  */
49 static bfd_boolean
50 segment_eligible_for_headers (struct elf_segment_map *seg,
51                               bfd_vma maxpagesize, bfd_vma sizeof_headers)
52 {
53   bfd_boolean any_contents = FALSE;
54   unsigned int i;
55   if (seg->count == 0 || seg->sections[0]->lma % maxpagesize < 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       if (seg->sections[i]->flags & SEC_HAS_CONTENTS)
62         any_contents = TRUE;
63     }
64   return any_contents;
65 }
66 
67 
68 /* We permute the segment_map to get BFD to do the file layout we want:
69    The first non-executable PT_LOAD segment appears first in the file
70    and contains the ELF file header and phdrs.  */
71 bfd_boolean
72 nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info)
73 {
74   struct elf_segment_map **m = &elf_seg_map (abfd);
75   struct elf_segment_map **first_load = NULL;
76   struct elf_segment_map **last_load = NULL;
77   bfd_boolean moved_headers = FALSE;
78   int sizeof_headers = info == NULL ? 0 : bfd_sizeof_headers (abfd, info);
79   bfd_vma maxpagesize = get_elf_backend_data (abfd)->maxpagesize;
80 
81   if (info != NULL && info->user_phdrs)
82     /* The linker script used PHDRS explicitly, so don't change what the
83        user asked for.  */
84     return TRUE;
85 
86   while (*m != NULL)
87     {
88       struct elf_segment_map *seg = *m;
89 
90       if (seg->p_type == PT_LOAD)
91         {
92           /* First, we're just finding the earliest PT_LOAD.
93              By the normal rules, this will be the lowest-addressed one.
94              We only have anything interesting to do if it's executable.  */
95           last_load = m;
96           if (first_load == NULL)
97             {
98               if (!segment_executable (*m))
99                 return TRUE;
100               first_load = m;
101             }
102           /* Now that we've noted the first PT_LOAD, we're looking for
103              the first non-executable PT_LOAD with a nonempty p_filesz.  */
104           else if (!moved_headers
105                    && segment_eligible_for_headers (seg, maxpagesize,
106                                                     sizeof_headers))
107             {
108               /* This is the one we were looking for!
109 
110                  First, clear the flags on previous segments that
111                  say they include the file header and phdrs.  */
112               struct elf_segment_map *prevseg;
113               for (prevseg = *first_load;
114                    prevseg != seg;
115                    prevseg = prevseg->next)
116                 if (prevseg->p_type == PT_LOAD)
117                   {
118                     prevseg->includes_filehdr = 0;
119                     prevseg->includes_phdrs = 0;
120                   }
121 
122               /* This segment will include those headers instead.  */
123               seg->includes_filehdr = 1;
124               seg->includes_phdrs = 1;
125 
126               moved_headers = TRUE;
127             }
128         }
129 
130       m = &seg->next;
131     }
132 
133   if (first_load != last_load && moved_headers)
134     {
135       /* Now swap the first and last PT_LOAD segments'
136          positions in segment_map.  */
137       struct elf_segment_map *first = *first_load;
138       struct elf_segment_map *last = *last_load;
139       *first_load = first->next;
140       first->next = last->next;
141       last->next = first;
142     }
143 
144   return TRUE;
145 }
146 
147 /* After nacl_modify_segment_map has done its work, the file layout has
148    been done as we wanted.  But the PT_LOAD phdrs are no longer in the
149    proper order for the ELF rule that they must appear in ascending address
150    order.  So find the two segments we swapped before, and swap them back.  */
151 bfd_boolean
152 nacl_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
153 {
154   struct elf_segment_map **m = &elf_seg_map (abfd);
155   Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr;
156   Elf_Internal_Phdr *p = phdr;
157 
158   if (info != NULL && info->user_phdrs)
159     /* The linker script used PHDRS explicitly, so don't change what the
160        user asked for.  */
161     return TRUE;
162 
163   /* Find the PT_LOAD that contains the headers (should be the first).  */
164   while (*m != NULL)
165     {
166       if ((*m)->p_type == PT_LOAD && (*m)->includes_filehdr)
167         break;
168 
169       m = &(*m)->next;
170       ++p;
171     }
172 
173   if (*m != NULL)
174     {
175       struct elf_segment_map **first_load_seg = m;
176       Elf_Internal_Phdr *first_load_phdr = p;
177       struct elf_segment_map **next_load_seg = NULL;
178       Elf_Internal_Phdr *next_load_phdr = NULL;
179 
180       /* Now move past that first one and find the PT_LOAD that should be
181          before it by address order.  */
182 
183       m = &(*m)->next;
184       ++p;
185 
186       while ((*m) != NULL)
187         {
188           if (p->p_type == PT_LOAD && p->p_vaddr < first_load_phdr->p_vaddr)
189             {
190               next_load_seg = m;
191               next_load_phdr = p;
192               break;
193             }
194 
195           m = &(*m)->next;
196           ++p;
197         }
198 
199       /* Swap their positions in the segment_map back to how they used to be.
200          The phdrs have already been set up by now, so we have to slide up
201          the earlier ones to insert the one that should be first.  */
202       if (next_load_seg != NULL)
203         {
204           Elf_Internal_Phdr move_phdr;
205           struct elf_segment_map *first_seg = *first_load_seg;
206           struct elf_segment_map *next_seg = *next_load_seg;
207           struct elf_segment_map *first_next = first_seg->next;
208           struct elf_segment_map *next_next = next_seg->next;
209 
210           first_seg->next = next_next;
211           *first_load_seg = next_seg;
212 
213           next_seg->next = first_next;
214           *next_load_seg = first_seg;
215 
216           move_phdr = *next_load_phdr;
217           memmove (first_load_phdr + 1, first_load_phdr,
218                    (next_load_phdr - first_load_phdr) * sizeof move_phdr);
219           *first_load_phdr = move_phdr;
220         }
221     }
222 
223   return TRUE;
224 }
225