1 /* $OpenBSD: copy_elf.c,v 1.7 2020/04/28 04:17:42 deraadt Exp $ */
2
3 /*
4 * Copyright (c) 2013 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <stdio.h>
20 #include <err.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <elf.h>
26
27 #if defined(ELFSIZE) && (ELFSIZE == 32)
28 #define elfoff2h(x) letoh32(x)
29 #define h2elfoff(x) htole32(x)
30 #elif defined(ELFSIZE) && (ELFSIZE == 64)
31 #define elfoff2h(x) letoh64(x)
32 #define h2elfoff(x) htole64(x)
33 #else
34 #error "unknown elf size"
35 #endif
36
37 struct image_header;
38
39 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
40
41 extern u_long copy_data(int, const char *, int, const char *, u_long,
42 struct image_header *, Elf_Word);
43 u_long copy_mem(void *, int, const char *, u_long, struct image_header *,
44 Elf_Word);
45 extern u_long fill_zeroes(int, const char *, u_long, struct image_header *,
46 Elf_Word);
47
48 u_long
ELFNAME(copy_elf)49 ELFNAME(copy_elf)(int ifd, const char *iname, int ofd, const char *oname,
50 u_long crc, struct image_header *ih)
51 {
52 ssize_t nbytes;
53 Elf_Ehdr ehdr, elf;
54 Elf_Phdr phdr;
55 Elf_Addr vaddr, ovaddr, svaddr, off, ssym;
56 Elf_Shdr *shp, *wshp;
57 Elf_Addr esym = 0, esymval;
58 int i, sz, havesyms;
59
60 nbytes = read(ifd, &ehdr, sizeof ehdr);
61 if (nbytes == -1)
62 err(1, "%s", iname);
63 if (nbytes != sizeof ehdr)
64 return 0;
65
66 elf = ehdr;
67
68 if (lseek(ifd, (off_t)elfoff2h(elf.e_shoff), SEEK_SET) == -1)
69 err(1, "%s unable to seek to section header", iname);
70
71 sz = letoh16(elf.e_shnum) * sizeof(Elf_Shdr);
72 shp = calloc(sz, 1);
73 if (read(ifd, shp, sz) != sz)
74 err(1, "%s: read section headers", iname);
75
76 wshp = calloc(sz, 1);
77 memcpy(wshp, shp, sz);
78
79 /* first walk the load sections to find the kernel addresses */
80 /* next we walk the sections to find the
81 * location of esym (first address of data space
82 */
83 for (i = 0; i < letoh16(ehdr.e_phnum); i++) {
84 if (lseek(ifd, elfoff2h(ehdr.e_phoff) + i *
85 letoh16(ehdr.e_phentsize), SEEK_SET) == (off_t)-1)
86 err(1, "%s", iname);
87 if (read(ifd, &phdr, sizeof phdr) != sizeof(phdr))
88 err(1, "%s", iname);
89 /* assumes it loads in incrementing address order */
90 if (letoh32(phdr.p_type) == PT_LOAD)
91 vaddr = elfoff2h(phdr.p_vaddr) +
92 elfoff2h(phdr.p_memsz);
93 }
94
95 /* ok, we need to write the elf header and section header
96 * which contains info about the not yet written section data
97 * however due to crc the data all has to be written in order
98 * which means walking the structures twice once to precompute
99 * the data, once to write the data.
100 */
101 ssym = vaddr;
102 vaddr += roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr));
103 off = roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr));
104 for (i = 0; i < letoh16(elf.e_shnum); i++) {
105 if (esym == 0 && elfoff2h(shp[i].sh_flags) & SHF_WRITE &&
106 elfoff2h(shp[i].sh_flags) & SHF_ALLOC)
107 esym = elfoff2h(shp[i].sh_addr);
108
109 if (letoh32(shp[i].sh_type) == SHT_SYMTAB ||
110 letoh32(shp[i].sh_type) == SHT_STRTAB) {
111 #ifdef DEBUG
112 fprintf(stderr, "shdr %d %d/%d off %lx\n", i,
113 letoh32(shp[i].sh_type), roundup(elfoff2h(shp[i].sh_size),
114 sizeof(Elf_Addr)), off);
115 #endif
116 /* data is at shp[i].sh_offset of len shp[i].sh_size */
117 wshp[i].sh_offset = h2elfoff(off);
118 off += roundup(elfoff2h(shp[i].sh_size),
119 sizeof(Elf_Addr));
120 vaddr += roundup(elfoff2h(shp[i].sh_size),
121 sizeof(Elf_Addr));
122 }
123 }
124 esymval = vaddr;
125 #ifdef DEBUG
126 fprintf(stderr, "esymval %lx size %ld\n", esymval, esymval - ssym);
127 #endif
128
129 for (i = 0; i < letoh16(ehdr.e_phnum); i++) {
130 #ifdef DEBUG
131 fprintf(stderr, "phdr %d/%d\n", i, letoh16(ehdr.e_phnum));
132 #endif
133 if (lseek(ifd, elfoff2h(ehdr.e_phoff) + i *
134 letoh16(ehdr.e_phentsize), SEEK_SET) == (off_t)-1)
135 err(1, "%s", iname);
136 if (read(ifd, &phdr, sizeof phdr) != sizeof(phdr))
137 err(1, "%s", iname);
138
139 #ifdef DEBUG
140 fprintf(stderr,
141 "vaddr %p type %#x offset %p filesz %p memsz %p\n",
142 elfoff2h(phdr.p_vaddr), letoh32(phdr.p_type),
143 elfoff2h(phdr.p_offset), elfoff2h(phdr.p_filesz),
144 elfoff2h(phdr.p_memsz));
145 #endif
146
147 switch (letoh32(phdr.p_type)) {
148 case PT_LOAD:
149 break;
150 case PT_NULL:
151 case PT_NOTE:
152 case PT_OPENBSD_RANDOMIZE:
153 #ifdef DEBUG
154 fprintf(stderr, "skipping segment type %#x\n",
155 letoh32(phdr.p_type));
156 #endif
157 continue;
158 default:
159 errx(1, "unexpected segment type %#x",
160 letoh32(phdr.p_type));
161 }
162
163 if (i == 0)
164 vaddr = elfoff2h(phdr.p_vaddr);
165 else if (vaddr != elfoff2h(phdr.p_vaddr)) {
166 #ifdef DEBUG
167 fprintf(stderr, "gap %p->%p\n", vaddr,
168 elfoff2h(phdr.p_vaddr));
169 #endif
170 /* fill the gap between the previous phdr if any */
171 crc = fill_zeroes(ofd, oname, crc, ih,
172 elfoff2h(phdr.p_vaddr) - vaddr);
173 vaddr = elfoff2h(phdr.p_vaddr);
174 }
175
176 if (elfoff2h(phdr.p_filesz) != 0) {
177 #ifdef DEBUG
178 fprintf(stderr, "copying %p from infile %p\n",
179 elfoff2h(phdr.p_filesz), elfoff2h(phdr.p_offset));
180 #endif
181 /* esym will be in the data portion of a region */
182 if (esym >= elfoff2h(phdr.p_vaddr) &&
183 esym < elfoff2h(phdr.p_vaddr) +
184 elfoff2h(phdr.p_filesz)) {
185 /* load the region up to the esym
186 * (may be empty)
187 */
188 Elf_Addr loadlen = esym -
189 elfoff2h(phdr.p_vaddr);
190
191 if (lseek(ifd, elfoff2h(phdr.p_offset),
192 SEEK_SET) == (off_t)-1)
193 err(1, "%s", iname);
194 crc = copy_data(ifd, iname, ofd, oname, crc,
195 ih, loadlen);
196
197 crc = copy_mem(&esymval, ofd, oname, crc, ih,
198 sizeof(esymval));
199
200 if (lseek(ifd, elfoff2h(phdr.p_offset) +
201 loadlen + sizeof(esymval), SEEK_SET) ==
202 (off_t)-1)
203 err(1, "%s", iname);
204 crc = copy_data(ifd, iname, ofd, oname, crc,
205 ih, elfoff2h(phdr.p_filesz) - loadlen -
206 sizeof(esymval));
207 } else {
208
209 if (lseek(ifd, elfoff2h(phdr.p_offset),
210 SEEK_SET) == (off_t)-1)
211 err(1, "%s", iname);
212 crc = copy_data(ifd, iname, ofd, oname, crc,
213 ih, elfoff2h(phdr.p_filesz));
214 }
215 if (elfoff2h(phdr.p_memsz) - elfoff2h(phdr.p_filesz)
216 != 0) {
217 #ifdef DEBUG
218 fprintf(stderr, "zeroing %p\n",
219 elfoff2h(phdr.p_memsz) -
220 elfoff2h(phdr.p_filesz));
221 #endif
222 crc = fill_zeroes(ofd, oname, crc, ih,
223 elfoff2h(phdr.p_memsz) -
224 elfoff2h(phdr.p_filesz));
225 }
226 ovaddr = vaddr + elfoff2h(phdr.p_memsz);
227 } else {
228 ovaddr = vaddr;
229 }
230 /*
231 * If p_filesz == 0, this is likely .bss, which we do not
232 * need to provide. If it's not the last phdr, the gap
233 * filling code will output the necessary zeroes anyway.
234 */
235 vaddr += elfoff2h(phdr.p_memsz);
236 }
237
238 vaddr = roundup(vaddr, sizeof(Elf_Addr));
239 if (vaddr != ovaddr) {
240 #ifdef DEBUG
241 fprintf(stderr, "gap %p->%p\n", vaddr, elfoff2h(phdr.p_vaddr));
242 #endif
243 /* fill the gap between the previous phdr if not aligned */
244 crc = fill_zeroes(ofd, oname, crc, ih, vaddr - ovaddr);
245 }
246
247 for (havesyms = i = 0; i < letoh16(elf.e_shnum); i++)
248 if (letoh32(shp[i].sh_type) == SHT_SYMTAB)
249 havesyms = 1;
250
251 if (havesyms == 0)
252 return crc;
253
254 elf.e_phoff = 0;
255 elf.e_shoff = h2elfoff(sizeof(Elf_Ehdr));
256 elf.e_phentsize = 0;
257 elf.e_phnum = 0;
258 crc = copy_mem(&elf, ofd, oname, crc, ih, sizeof(elf));
259 crc = copy_mem(wshp, ofd, oname, crc, ih, sz);
260 off = sizeof(elf) + sz;
261 vaddr += sizeof(elf) + sz;
262
263 off = roundup((sizeof(Elf_Ehdr) + sz), sizeof(Elf_Addr));
264 for (i = 0; i < letoh16(elf.e_shnum); i++) {
265 if (letoh32(shp[i].sh_type) == SHT_SYMTAB ||
266 letoh32(shp[i].sh_type) == SHT_STRTAB) {
267 Elf_Addr align;
268 /* data is at shp[i].sh_offset of len shp[i].sh_size */
269 if (lseek(ifd, elfoff2h(shp[i].sh_offset), SEEK_SET)
270 == -1)
271 err(1, "%s", iname);
272
273 off += elfoff2h(shp[i].sh_size);
274 vaddr += elfoff2h(shp[i].sh_size);
275 crc = copy_data(ifd, iname, ofd, oname, crc, ih,
276 elfoff2h(shp[i].sh_size));
277 align = roundup(elfoff2h(shp[i].sh_size),
278 sizeof(Elf_Addr)) - elfoff2h(shp[i].sh_size);
279 if (align != 0) {
280 vaddr += align;
281 crc = fill_zeroes(ofd, oname, crc, ih, align);
282 }
283 }
284 }
285
286 if (vaddr != esymval)
287 warnx("esymval and vaddr mismatch %llx %llx\n",
288 (long long)esymval, (long long)vaddr);
289
290 return crc;
291 }
292