xref: /illumos-gate/usr/src/cmd/sgs/libelf/common/clscook.c (revision 92a0208178405fef708b0283ffcaa02fbc3468ff)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 /*
33  * This stuff used to live in cook.c, but was moved out to
34  * facilitate dual (Elf32 and Elf64) compilation.  See block
35  * comment in cook.c for more info.
36  */
37 
38 #include <string.h>
39 #include <ar.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include "decl.h"
43 #include "member.h"
44 #include "msg.h"
45 
46 /*
47  * This module is compiled twice, the second time having
48  * -D_ELF64 defined.  The following set of macros, along
49  * with machelf.h, represent the differences between the
50  * two compilations.  Be careful *not* to add any class-
51  * dependent code (anything that has elf32 or elf64 in the
52  * name) to this code without hiding it behind a switch-
53  * able macro like these.
54  */
55 #if	defined(_ELF64)
56 #define	Snode		Snode64
57 #define	ELFCLASS	ELFCLASS64
58 #define	ElfField	Elf64
59 #define	_elf_snode_init	_elf64_snode_init
60 #define	_elf_prepscan	_elf64_prepscan
61 #define	_elf_cookscn	_elf64_cookscn
62 #define	_elf_mtype	_elf64_mtype
63 #define	_elf_msize	_elf64_msize
64 #define	elf_fsize	elf64_fsize
65 #define	_elf_snode	_elf64_snode
66 #define	_elf_ehdr	_elf64_ehdr
67 #define	elf_xlatetom	elf64_xlatetom
68 #define	_elf_phdr	_elf64_phdr
69 #define	_elf_shdr	_elf64_shdr
70 #define	_elf_prepscn	_elf64_prepscn
71 
72 #else  /* Elf32 */
73 #define	Snode		Snode32
74 #define	ELFCLASS	ELFCLASS32
75 #define	ElfField	Elf32
76 #define	_elf_snode_init	_elf32_snode_init
77 #define	_elf_prepscan	_elf32_prepscan
78 #define	_elf_cookscn	_elf32_cookscn
79 #define	_elf_mtype	_elf32_mtype
80 #define	_elf_msize	_elf32_msize
81 #define	elf_fsize	elf32_fsize
82 #define	_elf_snode	_elf32_snode
83 #define	_elf_ehdr	_elf32_ehdr
84 #define	elf_xlatetom	elf32_xlatetom
85 #define	_elf_phdr	_elf32_phdr
86 #define	_elf_shdr	_elf32_shdr
87 #define	_elf_prepscn	_elf32_prepscn
88 
89 #endif /* _ELF64 */
90 
91 
92 static Okay
93 _elf_prepscn(Elf *elf, size_t cnt)
94 {
95 	NOTE(ASSUMING_PROTECTED(*elf))
96 	Elf_Scn *	s;
97 	Elf_Scn *	end;
98 
99 	if (cnt == 0)
100 		return (OK_YES);
101 
102 	if ((s = malloc(cnt * sizeof (Elf_Scn))) == 0) {
103 		_elf_seterr(EMEM_SCN, errno);
104 		return (OK_NO);
105 	}
106 	NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*s))
107 	elf->ed_scntabsz = cnt;
108 	end = s + cnt;
109 	elf->ed_hdscn = s;
110 	do {
111 		*s = _elf_snode_init.sb_scn;
112 		s->s_elf = elf;
113 		s->s_next = s + 1;
114 		s->s_index = s - elf->ed_hdscn;
115 		s->s_shdr = (Shdr*)s->s_elf->ed_shdr + s->s_index;
116 		ELFMUTEXINIT(&s->s_mutex);
117 
118 		/*
119 		 * Section has not yet been cooked!
120 		 *
121 		 * We don't cook a section until it's data is actually
122 		 * referenced.
123 		 */
124 		s->s_myflags = 0;
125 	} while (++s < end);
126 
127 	elf->ed_tlscn = --s;
128 	s->s_next = 0;
129 
130 	/*
131 	 * Section index SHN_UNDEF (0) does not and cannot
132 	 * have a data buffer.  Fix it here.  Also mark the
133 	 * initial section as being allocated for the block
134 	 */
135 
136 	s = elf->ed_hdscn;
137 	s->s_myflags = SF_ALLOC;
138 	s->s_hdnode = 0;
139 	s->s_tlnode = 0;
140 	NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*s))
141 	return (OK_YES);
142 }
143 
144 
145 Okay
146 _elf_cookscn(Elf_Scn * s)
147 {
148 	NOTE(ASSUMING_PROTECTED(*s, *(s->s_elf)))
149 	Elf *			elf;
150 	Shdr *			sh;
151 	register Dnode *	d = &s->s_dnode;
152 	size_t			fsz, msz;
153 	unsigned		work;
154 
155 	NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*d))
156 	s->s_hdnode = s->s_tlnode = d;
157 	s->s_err = 0;
158 	s->s_shflags = 0;
159 	s->s_uflags = 0;
160 
161 
162 	/*
163 	 * Prepare d_data for inspection, but don't actually
164 	 * translate data until needed.  Leave the READY
165 	 * flag off.  NOBITS sections see zero size.
166 	 */
167 	elf = s->s_elf;
168 	sh = s->s_shdr;
169 
170 	d->db_scn = s;
171 	d->db_off = sh->sh_offset;
172 	d->db_data.d_align = sh->sh_addralign;
173 	d->db_data.d_version = elf->ed_version;
174 	ELFACCESSDATA(work, _elf_work)
175 	d->db_data.d_type = _elf_mtype(elf, sh->sh_type, work);
176 	d->db_data.d_buf = 0;
177 	d->db_data.d_off = 0;
178 	fsz = elf_fsize(d->db_data.d_type, 1, elf->ed_version);
179 	msz = _elf_msize(d->db_data.d_type, elf->ed_version);
180 	d->db_data.d_size = (sh->sh_size / fsz) * msz;
181 	d->db_shsz = sh->sh_size;
182 	d->db_raw = 0;
183 	d->db_buf = 0;
184 	d->db_uflags = 0;
185 	d->db_myflags = 0;
186 	d->db_next = 0;
187 
188 	if (sh->sh_type != SHT_NOBITS)
189 		d->db_fsz = sh->sh_size;
190 	else
191 		d->db_fsz = 0;
192 
193 	s->s_myflags |= SF_READY;
194 
195 	NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*d))
196 	return (OK_YES);
197 }
198 
199 
200 
201 Snode *
202 _elf_snode()
203 {
204 	register Snode	*s;
205 
206 	if ((s = malloc(sizeof (Snode))) == 0) {
207 		_elf_seterr(EMEM_SNODE, errno);
208 		return (0);
209 	}
210 	*s = _elf_snode_init;
211 	ELFMUTEXINIT(&s->sb_scn.s_mutex);
212 	s->sb_scn.s_myflags = SF_ALLOC | SF_READY;
213 	s->sb_scn.s_shdr = &s->sb_shdr;
214 	return (s);
215 }
216 
217 
218 
219 int
220 _elf_ehdr(Elf * elf, int inplace)
221 {
222 	NOTE(ASSUMING_PROTECTED(*elf))
223 	register size_t	fsz;		/* field size */
224 	Elf_Data	dst, src;
225 
226 	fsz = elf_fsize(ELF_T_EHDR, 1, elf->ed_version);
227 	if (fsz > elf->ed_fsz) {
228 		_elf_seterr(EFMT_EHDRSZ, 0);
229 		return (-1);
230 	}
231 	if (inplace && (fsz >= sizeof (Ehdr))) {
232 		/*
233 		 * The translated Ehdr will fit over the original Ehdr.
234 		 */
235 		/* LINTED */
236 		elf->ed_ehdr = (Ehdr *)elf->ed_ident;
237 		elf->ed_status = ES_COOKED;
238 	} else {
239 		elf->ed_ehdr = malloc(sizeof (Ehdr));
240 		if (elf->ed_ehdr == 0) {
241 			_elf_seterr(EMEM_EHDR, errno);
242 			return (-1);
243 		}
244 		elf->ed_myflags |= EDF_EHALLOC;
245 	}
246 
247 	/*
248 	 * Memory size >= fsz, because otherwise the memory version
249 	 * loses information and cannot accurately implement the
250 	 * file.
251 	 */
252 
253 	src.d_buf = (Elf_Void *)elf->ed_ident;
254 	src.d_type = ELF_T_EHDR;
255 	src.d_size = fsz;
256 	src.d_version = elf->ed_version;
257 	dst.d_buf = (Elf_Void *)elf->ed_ehdr;
258 	dst.d_size = sizeof (Ehdr);
259 	dst.d_version = EV_CURRENT;
260 
261 	if ((_elf_vm(elf, (size_t)0, fsz) != OK_YES) ||
262 	    (elf_xlatetom(&dst, &src, elf->ed_encode) == 0)) {
263 		if (elf->ed_myflags & EDF_EHALLOC) {
264 			elf->ed_myflags &= ~EDF_EHALLOC;
265 			free(elf->ed_ehdr);
266 		}
267 		elf->ed_ehdr = 0;
268 		return (-1);
269 	}
270 
271 	if (((Ehdr*)elf->ed_ehdr)->e_ident[EI_CLASS] != ELFCLASS) {
272 		_elf_seterr(EREQ_CLASS, 0);
273 		if (elf->ed_myflags & EDF_EHALLOC) {
274 			elf->ed_myflags &= ~EDF_EHALLOC;
275 			free(elf->ed_ehdr);
276 		}
277 		elf->ed_ehdr = 0;
278 		return (-1);
279 	}
280 
281 	if (((Ehdr*)elf->ed_ehdr)->e_version != elf->ed_version) {
282 		_elf_seterr(EFMT_VER2, 0);
283 		if (elf->ed_myflags & EDF_EHALLOC) {
284 			elf->ed_myflags &= ~EDF_EHALLOC;
285 			free(elf->ed_ehdr);
286 		}
287 		elf->ed_ehdr = 0;
288 		return (-1);
289 	}
290 
291 	return (0);
292 }
293 
294 
295 
296 int
297 _elf_phdr(Elf * elf, int inplace)
298 {
299 	NOTE(ASSUMING_PROTECTED(*elf))
300 	register size_t		fsz, msz;
301 	Elf_Data		dst, src;
302 	Ehdr *			eh = elf->ed_ehdr;	/* must be present */
303 	unsigned		work;
304 
305 	if (eh->e_phnum == 0)
306 		return (0);
307 
308 	fsz = elf_fsize(ELF_T_PHDR, 1, elf->ed_version);
309 	if (eh->e_phentsize != fsz) {
310 		_elf_seterr(EFMT_PHDRSZ, 0);
311 		return (-1);
312 	}
313 
314 	fsz *= eh->e_phnum;
315 	ELFACCESSDATA(work, _elf_work)
316 	msz = _elf_msize(ELF_T_PHDR, work) * eh->e_phnum;
317 	if ((eh->e_phoff == 0) ||
318 	    ((fsz + eh->e_phoff) > elf->ed_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