1 /* Create new ELF program header table.
2    Copyright (C) 1999-2010, 2014, 2015 Red Hat, Inc.
3    This file is part of elfutils.
4    Written by Ulrich Drepper <drepper@redhat.com>, 1998.
5 
6    This file is free software; you can redistribute it and/or modify
7    it under the terms of either
8 
9      * the GNU Lesser General Public License as published by the Free
10        Software Foundation; either version 3 of the License, or (at
11        your option) any later version
12 
13    or
14 
15      * the GNU General Public License as published by the Free
16        Software Foundation; either version 2 of the License, or (at
17        your option) any later version
18 
19    or both in parallel, as here.
20 
21    elfutils is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24    General Public License for more details.
25 
26    You should have received copies of the GNU General Public License and
27    the GNU Lesser General Public License along with this program.  If
28    not, see <http://www.gnu.org/licenses/>.  */
29 
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33 
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "libelfP.h"
39 
40 #ifndef LIBELFBITS
41 # define LIBELFBITS 32
42 #endif
43 
44 
ElfW2(LIBELFBITS,Phdr)45 ElfW2(LIBELFBITS,Phdr) *
46 elfw2(LIBELFBITS,newphdr) (Elf *elf, size_t count)
47 {
48   ElfW2(LIBELFBITS,Phdr) *result;
49 
50   if (elf == NULL)
51     return NULL;
52 
53   if (unlikely (elf->kind != ELF_K_ELF))
54     {
55       __libelf_seterrno (ELF_E_INVALID_HANDLE);
56       return NULL;
57     }
58 
59   if (unlikely ((ElfW2(LIBELFBITS,Word)) count != count))
60     {
61       __libelf_seterrno (ELF_E_INVALID_OPERAND);
62       return NULL;
63     }
64 
65   rwlock_wrlock (elf->lock);
66 
67   if (elf->class == 0)
68     elf->class = ELFW(ELFCLASS,LIBELFBITS);
69   else if (unlikely (elf->class != ELFW(ELFCLASS,LIBELFBITS)))
70     {
71       __libelf_seterrno (ELF_E_INVALID_CLASS);
72       result = NULL;
73       goto out;
74     }
75 
76   if (unlikely (elf->state.ELFW(elf,LIBELFBITS).ehdr == NULL))
77     {
78       __libelf_seterrno (ELF_E_WRONG_ORDER_EHDR);
79       result = NULL;
80       goto out;
81     }
82 
83   /* A COUNT of zero means remove existing table.  */
84   if (count == 0)
85     {
86       /* Free the old program header.  */
87       if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL)
88 	{
89 	  if (elf->state.ELFW(elf,LIBELFBITS).phdr_flags & ELF_F_MALLOCED)
90 	    free (elf->state.ELFW(elf,LIBELFBITS).phdr);
91 
92 	  /* Set the pointer to NULL.  */
93 	  elf->state.ELFW(elf,LIBELFBITS).phdr = NULL;
94 	  /* Set the `e_phnum' member to the new value.  */
95 	  elf->state.ELFW(elf,LIBELFBITS).ehdr->e_phnum = 0;
96 	  /* Also clear any old PN_XNUM extended value.  */
97 	  if (elf->state.ELFW(elf,LIBELFBITS).scns.cnt > 0)
98 	    elf->state.ELFW(elf,LIBELFBITS).scns.data[0]
99 	      .shdr.ELFW(e,LIBELFBITS)->sh_info = 0;
100 	  /* Also set the size.  */
101 	  elf->state.ELFW(elf,LIBELFBITS).ehdr->e_phentsize =
102 	    sizeof (ElfW2(LIBELFBITS,Phdr));
103 
104 	  elf->state.ELFW(elf,LIBELFBITS).phdr_flags |= ELF_F_DIRTY;
105 	  elf->flags |= ELF_F_DIRTY;
106 	  __libelf_seterrno (ELF_E_NOERROR);
107 	}
108 
109       result = NULL;
110     }
111   else if (elf->state.ELFW(elf,LIBELFBITS).ehdr->e_phnum != count
112 	   || count == PN_XNUM
113 	   || elf->state.ELFW(elf,LIBELFBITS).phdr == NULL)
114     {
115       if (unlikely (count > SIZE_MAX / sizeof (ElfW2(LIBELFBITS,Phdr))))
116 	{
117 	  __libelf_seterrno (ELF_E_INVALID_INDEX);
118 	  result = NULL;
119 	  goto out;
120 	}
121 
122       Elf_Scn *scn0 = &elf->state.ELFW(elf,LIBELFBITS).scns.data[0];
123       if (unlikely (count >= PN_XNUM && scn0->shdr.ELFW(e,LIBELFBITS) == NULL))
124 	{
125 	  /* Something is wrong with section zero, but we need it to write
126 	     the extended phdr count.  */
127 	  __libelf_seterrno (ELF_E_INVALID_SECTION_HEADER);
128 	  result = NULL;
129 	  goto out;
130 	}
131 
132       /* Allocate a new program header with the appropriate number of
133 	 elements.  */
134       result = (ElfW2(LIBELFBITS,Phdr) *)
135 	realloc (elf->state.ELFW(elf,LIBELFBITS).phdr,
136 		 count * sizeof (ElfW2(LIBELFBITS,Phdr)));
137       if (result == NULL)
138 	__libelf_seterrno (ELF_E_NOMEM);
139       else
140 	{
141 	  /* Now set the result.  */
142 	  elf->state.ELFW(elf,LIBELFBITS).phdr = result;
143 	  if (count >= PN_XNUM)
144 	    {
145 	      /* We have to write COUNT into the zeroth section's sh_info.  */
146 	      if (elf->state.ELFW(elf,LIBELFBITS).scns.cnt == 0)
147 		{
148 		  assert (elf->state.ELFW(elf,LIBELFBITS).scns.max > 0);
149 		  elf->state.ELFW(elf,LIBELFBITS).scns.cnt = 1;
150 		}
151 	      scn0->shdr.ELFW(e,LIBELFBITS)->sh_info = count;
152 	      scn0->shdr_flags |= ELF_F_DIRTY;
153 	      elf->state.ELFW(elf,LIBELFBITS).ehdr->e_phnum = PN_XNUM;
154 	    }
155 	  else
156 	    /* Set the `e_phnum' member to the new value.  */
157 	    elf->state.ELFW(elf,LIBELFBITS).ehdr->e_phnum = count;
158 	  /* Clear the whole memory.  */
159 	  memset (result, '\0', count * sizeof (ElfW2(LIBELFBITS,Phdr)));
160 	  /* Also set the size.  */
161 	  elf->state.ELFW(elf,LIBELFBITS).ehdr->e_phentsize =
162 	    elf_typesize (LIBELFBITS, ELF_T_PHDR, 1);
163 	  /* Remember we allocated the array and mark the structure is
164 	     modified.  */
165 	  elf->state.ELFW(elf,LIBELFBITS).phdr_flags |=
166 	    ELF_F_DIRTY | ELF_F_MALLOCED;
167 	  /* We have to rewrite the entire file if the size of the
168 	     program header is changed.  */
169 	  elf->flags |= ELF_F_DIRTY;
170 	}
171     }
172   else
173     {
174       /* We have the same number of entries.  Just clear the array.  */
175       assert (elf->state.ELFW(elf,LIBELFBITS).ehdr->e_phentsize
176 	      == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));
177 
178       /* Mark the structure as modified.  */
179       elf->state.ELFW(elf,LIBELFBITS).phdr_flags |= ELF_F_DIRTY;
180 
181       result = elf->state.ELFW(elf,LIBELFBITS).phdr;
182       memset (result, '\0', count * sizeof (ElfW2(LIBELFBITS,Phdr)));
183     }
184 
185  out:
186   rwlock_unlock (elf->lock);
187 
188   return result;
189 }
190 INTDEF(elfw2(LIBELFBITS,newphdr))
191