xref: /illumos-gate/usr/src/cmd/sgs/libelf/common/clscook.c (revision 8808ac5dae118369991f158b6ab736cb2691ecde)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1988 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * This stuff used to live in cook.c, but was moved out to
32  * facilitate dual (Elf32 and Elf64) compilation.  See block
33  * comment in cook.c for more info.
34  */
35 
36 #include <string.h>
37 #include <ar.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <sys/sysmacros.h>
41 #include "decl.h"
42 #include "member.h"
43 #include "msg.h"
44 
45 /*
46  * This module is compiled twice, the second time having
47  * -D_ELF64 defined.  The following set of macros, along
48  * with machelf.h, represent the differences between the
49  * two compilations.  Be careful *not* to add any class-
50  * dependent code (anything that has elf32 or elf64 in the
51  * name) to this code without hiding it behind a switch-
52  * able macro like these.
53  */
54 #if	defined(_ELF64)
55 #define	Snode		Snode64
56 #define	ELFCLASS	ELFCLASS64
57 #define	ElfField	Elf64
58 #define	_elf_snode_init	_elf64_snode_init
59 #define	_elf_prepscan	_elf64_prepscan
60 #define	_elf_cookscn	_elf64_cookscn
61 #define	_elf_mtype	_elf64_mtype
62 #define	_elf_msize	_elf64_msize
63 #define	elf_fsize	elf64_fsize
64 #define	_elf_snode	_elf64_snode
65 #define	_elf_ehdr	_elf64_ehdr
66 #define	elf_xlatetom	elf64_xlatetom
67 #define	_elf_phdr	_elf64_phdr
68 #define	_elf_shdr	_elf64_shdr
69 #define	_elf_prepscn	_elf64_prepscn
70 
71 #else  /* Elf32 */
72 #define	Snode		Snode32
73 #define	ELFCLASS	ELFCLASS32
74 #define	ElfField	Elf32
75 #define	_elf_snode_init	_elf32_snode_init
76 #define	_elf_prepscan	_elf32_prepscan
77 #define	_elf_cookscn	_elf32_cookscn
78 #define	_elf_mtype	_elf32_mtype
79 #define	_elf_msize	_elf32_msize
80 #define	elf_fsize	elf32_fsize
81 #define	_elf_snode	_elf32_snode
82 #define	_elf_ehdr	_elf32_ehdr
83 #define	elf_xlatetom	elf32_xlatetom
84 #define	_elf_phdr	_elf32_phdr
85 #define	_elf_shdr	_elf32_shdr
86 #define	_elf_prepscn	_elf32_prepscn
87 
88 #endif /* _ELF64 */
89 
90 
91 static Okay
92 _elf_prepscn(Elf *elf, size_t cnt)
93 {
94 	NOTE(ASSUMING_PROTECTED(*elf))
95 	Elf_Scn *	s;
96 	Elf_Scn *	end;
97 
98 	if (cnt == 0)
99 		return (OK_YES);
100 
101 	if ((s = malloc(cnt * sizeof (Elf_Scn))) == 0) {
102 		_elf_seterr(EMEM_SCN, errno);
103 		return (OK_NO);
104 	}
105 	NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*s))
106 	elf->ed_scntabsz = cnt;
107 	end = s + cnt;
108 	elf->ed_hdscn = s;
109 	do {
110 		*s = _elf_snode_init.sb_scn;
111 		s->s_elf = elf;
112 		s->s_next = s + 1;
113 		s->s_index = s - elf->ed_hdscn;
114 		s->s_shdr = (Shdr*)s->s_elf->ed_shdr + s->s_index;
115 		ELFMUTEXINIT(&s->s_mutex);
116 
117 		/*
118 		 * Section has not yet been cooked!
119 		 *
120 		 * We don't cook a section until it's data is actually
121 		 * referenced.
122 		 */
123 		s->s_myflags = 0;
124 	} while (++s < end);
125 
126 	elf->ed_tlscn = --s;
127 	s->s_next = 0;
128 
129 	/*
130 	 * Section index SHN_UNDEF (0) does not and cannot
131 	 * have a data buffer.  Fix it here.  Also mark the
132 	 * initial section as being allocated for the block
133 	 */
134 
135 	s = elf->ed_hdscn;
136 	s->s_myflags = SF_ALLOC;
137 	s->s_hdnode = 0;
138 	s->s_tlnode = 0;
139 	NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*s))
140 	return (OK_YES);
141 }
142 
143 
144 Okay
145 _elf_cookscn(Elf_Scn * s)
146 {
147 	NOTE(ASSUMING_PROTECTED(*s, *(s->s_elf)))
148 	Elf *			elf;
149 	Shdr *			sh;
150 	register Dnode *	d = &s->s_dnode;
151 	size_t			fsz, msz;
152 	unsigned		work;
153 
154 	NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*d))
155 	s->s_hdnode = s->s_tlnode = d;
156 	s->s_err = 0;
157 	s->s_shflags = 0;
158 	s->s_uflags = 0;
159 
160 
161 	/*
162 	 * Prepare d_data for inspection, but don't actually
163 	 * translate data until needed.  Leave the READY
164 	 * flag off.  NOBITS sections see zero size.
165 	 */
166 	elf = s->s_elf;
167 	sh = s->s_shdr;
168 
169 	d->db_scn = s;
170 	d->db_off = sh->sh_offset;
171 	d->db_data.d_align = sh->sh_addralign;
172 	d->db_data.d_version = elf->ed_version;
173 	ELFACCESSDATA(work, _elf_work)
174 	d->db_data.d_type = _elf_mtype(elf, sh->sh_type, work);
175 	d->db_data.d_buf = 0;
176 	d->db_data.d_off = 0;
177 	fsz = elf_fsize(d->db_data.d_type, 1, elf->ed_version);
178 	msz = _elf_msize(d->db_data.d_type, elf->ed_version);
179 	d->db_data.d_size = MAX(sh->sh_size, (sh->sh_size / fsz) * msz);
180 	d->db_shsz = sh->sh_size;
181 	d->db_raw = 0;
182 	d->db_buf = 0;
183 	d->db_uflags = 0;
184 	d->db_myflags = 0;
185 	d->db_next = 0;
186 
187 	if (sh->sh_type != SHT_NOBITS)
188 		d->db_fsz = sh->sh_size;
189 	else
190 		d->db_fsz = 0;
191 
192 	s->s_myflags |= SF_READY;
193 
194 	NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*d))
195 	return (OK_YES);
196 }
197 
198 
199 
200 Snode *
201 _elf_snode()
202 {
203 	register Snode	*s;
204 
205 	if ((s = malloc(sizeof (Snode))) == 0) {
206 		_elf_seterr(EMEM_SNODE, errno);
207 		return (0);
208 	}
209 	*s = _elf_snode_init;
210 	ELFMUTEXINIT(&s->sb_scn.s_mutex);
211 	s->sb_scn.s_myflags = SF_ALLOC | SF_READY;
212 	s->sb_scn.s_shdr = &s->sb_shdr;
213 	return (s);
214 }
215 
216 
217 
218 int
219 _elf_ehdr(Elf * elf, int inplace)
220 {
221 	NOTE(ASSUMING_PROTECTED(*elf))
222 	register size_t	fsz;		/* field size */
223 	Elf_Data	dst, src;
224 
225 	fsz = elf_fsize(ELF_T_EHDR, 1, elf->ed_version);
226 	if (fsz > elf->ed_fsz) {
227 		_elf_seterr(EFMT_EHDRSZ, 0);
228 		return (-1);
229 	}
230 	if (inplace && (fsz >= sizeof (Ehdr))) {
231 		/*
232 		 * The translated Ehdr will fit over the original Ehdr.
233 		 */
234 		/* LINTED */
235 		elf->ed_ehdr = (Ehdr *)elf->ed_ident;
236 		elf->ed_status = ES_COOKED;
237 	} else {
238 		elf->ed_ehdr = malloc(sizeof (Ehdr));
239 		if (elf->ed_ehdr == 0) {
240 			_elf_seterr(EMEM_EHDR, errno);
241 			return (-1);
242 		}
243 		elf->ed_myflags |= EDF_EHALLOC;
244 	}
245 
246 	/*
247 	 * Memory size >= fsz, because otherwise the memory version
248 	 * loses information and cannot accurately implement the
249 	 * file.
250 	 */
251 
252 	src.d_buf = (Elf_Void *)elf->ed_ident;
253 	src.d_type = ELF_T_EHDR;
254 	src.d_size = fsz;
255 	src.d_version = elf->ed_version;
256 	dst.d_buf = (Elf_Void *)elf->ed_ehdr;
257 	dst.d_size = sizeof (Ehdr);
258 	dst.d_version = EV_CURRENT;
259 
260 	if ((_elf_vm(elf, (size_t)0, fsz) != OK_YES) ||
261 	    (elf_xlatetom(&dst, &src, elf->ed_encode) == 0)) {
262 		if (elf->ed_myflags & EDF_EHALLOC) {
263 			elf->ed_myflags &= ~EDF_EHALLOC;
264 			free(elf->ed_ehdr);
265 		}
266 		elf->ed_ehdr = 0;
267 		return (-1);
268 	}
269 
270 	if (((Ehdr*)elf->ed_ehdr)->e_ident[EI_CLASS] != ELFCLASS) {
271 		_elf_seterr(EREQ_CLASS, 0);
272 		if (elf->ed_myflags & EDF_EHALLOC) {
273 			elf->ed_myflags &= ~EDF_EHALLOC;
274 			free(elf->ed_ehdr);
275 		}
276 		elf->ed_ehdr = 0;
277 		return (-1);
278 	}
279 
280 	if (((Ehdr*)elf->ed_ehdr)->e_version != elf->ed_version) {
281 		_elf_seterr(EFMT_VER2, 0);
282 		if (elf->ed_myflags & EDF_EHALLOC) {
283 			elf->ed_myflags &= ~EDF_EHALLOC;
284 			free(elf->ed_ehdr);
285 		}
286 		elf->ed_ehdr = 0;
287 		return (-1);
288 	}
289 
290 	return (0);
291 }
292 
293 
294 
295 int
296 _elf_phdr(Elf * elf, int inplace)
297 {
298 	NOTE(ASSUMING_PROTECTED(*elf))
299 	register size_t		fsz, msz;
300 	Elf_Data		dst, src;
301 	Ehdr *			eh = elf->ed_ehdr;	/* must be present */
302 	unsigned		work;
303 
304 	if (eh->e_phnum == 0)
305 		return (0);
306 
307 	fsz = elf_fsize(ELF_T_PHDR, 1, elf->ed_version);
308 	if (eh->e_phentsize != fsz) {
309 		_elf_seterr(EFMT_PHDRSZ, 0);
310 		return (-1);
311 	}
312 
313 	fsz *= eh->e_phnum;
314 	ELFACCESSDATA(work, _elf_work)
315 	msz = _elf_msize(ELF_T_PHDR, work) * eh->e_phnum;
316 	if ((eh->e_phoff == 0) ||
317 	    (elf->ed_fsz <= eh->e_phoff) ||
318 	    (elf->ed_fsz - eh->e_phoff < fsz)) {
319 		_elf_seterr(EFMT_PHTAB, 0);
320 		return (-1);
321 	}
322 
323 	if (inplace && fsz >= msz && eh->e_phoff % sizeof (ElfField) == 0) {
324 		elf->ed_phdr = (Elf_Void *)(elf->ed_ident + eh->e_phoff);
325 		elf->ed_status = ES_COOKED;
326 	} else {
327 		if ((elf->ed_phdr = malloc(msz)) == 0) {
328 			_elf_seterr(EMEM_PHDR, errno);
329 			return (-1);
330 		}
331 		elf->ed_myflags |= EDF_PHALLOC;
332 	}
333 	src.d_buf = (Elf_Void *)(elf->ed_ident + eh->e_phoff);
334 	src.d_type = ELF_T_PHDR;
335 	src.d_size = fsz;
336 	src.d_version = elf->ed_version;
337 	dst.d_buf = elf->ed_phdr;
338 	dst.d_size = msz;
339 	dst.d_version = work;
340 	if ((_elf_vm(elf, (size_t)eh->e_phoff, fsz) != OK_YES) ||
341 	    (elf_xlatetom(&dst, &src, elf->ed_encode) == 0)) {
342 		if (elf->ed_myflags & EDF_PHALLOC) {
343 			elf->ed_myflags &= ~EDF_PHALLOC;
344 			free(elf->ed_phdr);
345 		}
346 		elf->ed_phdr = 0;
347 		return (-1);
348 	}
349 	elf->ed_phdrsz = msz;
350 	return (0);
351 }
352 
353 
354 
355 int
356 _elf_shdr(Elf * elf, int inplace)
357 {
358 	NOTE(ASSUMING_PROTECTED(*elf))
359 	register size_t		fsz, msz;
360 	size_t			scncnt;
361 	Elf_Data		dst, src;
362 	register Ehdr		*eh = elf->ed_ehdr;	/* must be present */
363 
364 	if ((eh->e_shnum == 0) && (eh->e_shoff == 0))
365 		return (0);
366 
367 	fsz = elf_fsize(ELF_T_SHDR, 1, elf->ed_version);
368 	if (eh->e_shentsize != fsz) {
369 		_elf_seterr(EFMT_SHDRSZ, 0);
370 		return (-1);
371 	}
372 	/*
373 	 * If we are dealing with a file with 'extended section
374 	 * indexes' - then we need to load the first section
375 	 * header.  The actual section count is stored in
376 	 * Shdr[0].sh_size.
377 	 */
378 	if ((scncnt = eh->e_shnum) == 0) {
379 		Shdr	sh;
380 		if ((eh->e_shoff == 0) ||
381 		    (elf->ed_fsz <= eh->e_shoff) ||
382 		    (elf->ed_fsz - eh->e_shoff < fsz)) {
383 			_elf_seterr(EFMT_SHTAB, 0);
384 			return (-1);
385 		}
386 		src.d_buf = (Elf_Void *)(elf->ed_ident + eh->e_shoff);
387 		src.d_type = ELF_T_SHDR;
388 		src.d_size = fsz;
389 		src.d_version = elf->ed_version;
390 		dst.d_buf = (Elf_Void *)&sh;
391 		dst.d_size = sizeof (Shdr);
392 		dst.d_version = EV_CURRENT;
393 		if ((_elf_vm(elf, (size_t)eh->e_shoff, fsz) != OK_YES) ||
394 		    (elf_xlatetom(&dst, &src, elf->ed_encode) == 0)) {
395 			return (-1);
396 		}
397 		scncnt = sh.sh_size;
398 	}
399 
400 	fsz *= scncnt;
401 	msz = scncnt * sizeof (Shdr);
402 	if ((eh->e_shoff == 0) ||
403 	    (elf->ed_fsz <= eh->e_shoff) ||
404 	    (elf->ed_fsz - eh->e_shoff < fsz)) {
405 		_elf_seterr(EFMT_SHTAB, 0);
406 		return (-1);
407 	}
408 
409 	if (inplace && (fsz >= msz) &&
410 	    ((eh->e_shoff % sizeof (ElfField)) == 0)) {
411 		/* LINTED */
412 		elf->ed_shdr = (Shdr *)(elf->ed_ident + eh->e_shoff);
413 		elf->ed_status = ES_COOKED;
414 	} else {
415 		if ((elf->ed_shdr = malloc(msz)) == 0) {
416 			_elf_seterr(EMEM_SHDR, errno);
417 			return (-1);
418 		}
419 		elf->ed_myflags |= EDF_SHALLOC;
420 	}
421 	src.d_buf = (Elf_Void *)(elf->ed_ident + eh->e_shoff);
422 	src.d_type = ELF_T_SHDR;
423 	src.d_size = fsz;
424 	src.d_version = elf->ed_version;
425 	dst.d_buf = (Elf_Void *)elf->ed_shdr;
426 	dst.d_size = msz;
427 	dst.d_version = EV_CURRENT;
428 	if ((_elf_vm(elf, (size_t)eh->e_shoff, fsz) != OK_YES) ||
429 	    (elf_xlatetom(&dst, &src, elf->ed_encode) == 0) ||
430 	    (_elf_prepscn(elf, scncnt) != OK_YES)) {
431 		if (elf->ed_myflags & EDF_SHALLOC) {
432 			elf->ed_myflags &= ~EDF_SHALLOC;
433 			free(elf->ed_shdr);
434 		}
435 		elf->ed_shdr = 0;
436 		return (-1);
437 	}
438 	return (0);
439 }
440