xref: /illumos-gate/usr/src/cmd/sgs/libelf/common/getdata.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
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 #include <stdlib.h>
33 #include <assert.h>
34 #include <errno.h>
35 #include <libelf.h>
36 #include "decl.h"
37 #include "msg.h"
38 
39 
40 /*
41  * Convert data from file format to memory format.
42  */
43 
44 
45 static const size_t	align32[ELF_T_NUM] =
46 {
47 	1,			/* ELF_T_BYTE */
48 	sizeof (Elf32),		/* ELF_T_ADDR */
49 	sizeof (Elf32),		/* ELF_T_DYN */
50 	sizeof (Elf32),		/* ELF_T_EHDR */
51 	sizeof (Elf32_Half),	/* ELF_T_HALF */
52 	sizeof (Elf32),		/* ELF_T_OFF */
53 	sizeof (Elf32),		/* ELF_T_PHDR */
54 	sizeof (Elf32),		/* ELF_T_RELA */
55 	sizeof (Elf32),		/* ELF_T_REL */
56 	sizeof (Elf32),		/* ELF_T_SHDR */
57 	sizeof (Elf32),		/* ELF_T_SWORD */
58 	sizeof (Elf32),		/* ELF_T_SYM */
59 	sizeof (Elf32),		/* ELF_T_WORD */
60 	sizeof (Elf32),		/* ELF_T_VERDEF */
61 	sizeof (Elf32),		/* ELF_T_VERNEED */
62 	sizeof (Elf64_Sxword),	/* ELF_T_SXWORD */
63 	sizeof (Elf64),		/* ELF_T_XWORD */
64 	sizeof (Elf32_Half), 	/* ELF_T_SYMINFO */
65 	sizeof (Elf32),		/* ELF_T_NOTE */
66 	sizeof (Elf32_Lword),	/* ELF_T_MOVE */
67 	sizeof (Elf32_Lword),	/* ELF_T_MOVEP */
68 	sizeof (Elf32_Word)	/* ELF_T_CAP */
69 
70 };
71 
72 #define	Nalign32	(sizeof (align32)/sizeof (align32[0]))
73 
74 static const size_t	align64[ELF_T_NUM] =
75 {
76 	1,			/* ELF_T_BYTE */
77 	sizeof (Elf64),		/* ELF_T_ADDR */
78 	sizeof (Elf64),		/* ELF_T_DYN */
79 	sizeof (Elf64),		/* ELF_T_EHDR */
80 	sizeof (Elf64_Half),	/* ELF_T_HALF */
81 	sizeof (Elf64),		/* ELF_T_OFF */
82 	sizeof (Elf64),		/* ELF_T_PHDR */
83 	sizeof (Elf64),		/* ELF_T_RELA */
84 	sizeof (Elf64),		/* ELF_T_REL */
85 	sizeof (Elf64),		/* ELF_T_SHDR */
86 	sizeof (Elf64_Word),	/* ELF_T_SWORD */
87 	sizeof (Elf64),		/* ELF_T_SYM */
88 	sizeof (Elf64_Word),	/* ELF_T_WORD */
89 	sizeof (Elf64),		/* ELF_T_VDEF */
90 	sizeof (Elf64),		/* ELF_T_VNEED */
91 	sizeof (Elf64),		/* ELF_T_SXWORD */
92 	sizeof (Elf64),		/* ELF_T_XWORD */
93 	sizeof (Elf32_Half), 	/* ELF_T_SYMINFO */
94 	sizeof (Elf32),		/* ELF_T_NOTE */
95 	sizeof (Elf64),		/* ELF_T_MOVE */
96 	sizeof (Elf64),		/* ELF_T_MOVEP */
97 	sizeof (Elf64_Word)	/* ELF_T_CAP */
98 };
99 
100 #define	Nalign64	(sizeof (align64)/sizeof (align64[0]))
101 
102 
103 /*
104  * Could use an array indexed by ELFCLASS*, but I'd rather
105  * avoid .data over something this infrequently used.  The
106  * next choice would be to add extra conditionals.
107  */
108 #define	NALIGN(elf)	((elf->ed_class == ELFCLASS32) ? Nalign32 : Nalign64)
109 #define	ALIGN(elf)	((elf->ed_class == ELFCLASS32) ? align32 : align64)
110 
111 
112 Elf_Data *
113 _elf_locked_getdata(Elf_Scn * scn, Elf_Data * data)
114 {
115 	Dnode *		d = (Dnode *)data;
116 	Elf *		elf;
117 	Elf_Data	src;
118 	unsigned	work;
119 
120 	assert(!elf_threaded || RW_LOCK_HELD(&(scn->s_elf->ed_rwlock)));
121 	assert(!elf_threaded || MUTEX_HELD(&(scn->s_mutex)));
122 	elf = scn->s_elf;
123 
124 	if ((scn->s_myflags & SF_READY) == 0) {
125 		UPGRADELOCKS(elf, scn)
126 		/*
127 		 * make sure someone else didn't come along and cook
128 		 * this stuff.
129 		 */
130 		if ((scn->s_myflags & SF_READY) == 0)
131 			(void) _elf_cookscn(scn);
132 		DOWNGRADELOCKS(elf, scn)
133 	}
134 
135 	if (d == 0)
136 		d = scn->s_hdnode;
137 	else
138 		d = d->db_next;
139 
140 	if (scn->s_err != 0) {
141 		/*LINTED*/
142 		_elf_seterr((Msg)scn->s_err, 0);
143 		return (0);
144 	}
145 
146 	if (d == 0) {
147 		return (0);
148 	}
149 
150 	if (d->db_scn != scn) {
151 		_elf_seterr(EREQ_DATA, 0);
152 		return (0);
153 	}
154 
155 	if (d->db_myflags & DBF_READY) {
156 		return (&d->db_data);
157 	}
158 	elf = scn->s_elf;
159 
160 	/*
161 	 * Prepare return buffer.  The data comes from the memory
162 	 * image of the file.  "Empty" regions get an empty buffer.
163 	 *
164 	 * Only sections of an ELF_C_READ file can be not READY here.
165 	 * Furthermore, the input file must have been cooked or
166 	 * frozen by now.  Translate cooked files in place if possible.
167 	 */
168 
169 	ELFACCESSDATA(work, _elf_work)
170 	d->db_data.d_version = work;
171 	if ((d->db_off == 0) || (d->db_fsz == 0)) {
172 		d->db_myflags |= DBF_READY;
173 		return (&d->db_data);
174 	}
175 
176 	if (elf->ed_class == ELFCLASS32) {
177 		Elf32_Shdr	*sh = scn->s_shdr;
178 		size_t		sz = sh->sh_entsize;
179 		Elf_Type	t = d->db_data.d_type;
180 
181 		if ((t != ELF_T_BYTE) &&
182 		    (sz > 1) && (sz != elf32_fsize(t, 1, elf->ed_version))) {
183 			_elf_seterr(EFMT_ENTSZ, 0);
184 			return (0);
185 		}
186 	} else if (elf->ed_class == ELFCLASS64) {
187 		Elf64_Shdr	*sh = scn->s_shdr;
188 		Elf64_Xword	sz = sh->sh_entsize;
189 		Elf_Type	t = d->db_data.d_type;
190 
191 		if (t != ELF_T_BYTE && sz > 1 &&
192 		    sz != elf64_fsize(t, 1, elf->ed_version)) {
193 			_elf_seterr(EFMT_ENTSZ, 0);
194 			return (0);
195 		}
196 	} else {
197 		_elf_seterr(EREQ_CLASS, 0);
198 		return (0);
199 	}
200 
201 
202 	/*
203 	 * validate the region
204 	 */
205 
206 	if ((d->db_off < 0) || (d->db_off >= elf->ed_fsz) ||
207 	    (elf->ed_fsz - d->db_off < d->db_fsz)) {
208 		_elf_seterr(EFMT_DATA, 0);
209 		return (0);
210 	}
211 
212 	/*
213 	 * set up translation buffers and validate
214 	 */
215 
216 	src.d_buf = (Elf_Void *)(elf->ed_ident + d->db_off);
217 	src.d_size = d->db_fsz;
218 	src.d_type = d->db_data.d_type;
219 	src.d_version = elf->ed_version;
220 	if (elf->ed_vm) {
221 		UPGRADELOCKS(elf, scn)
222 		if (_elf_vm(elf, (size_t)d->db_off, d->db_fsz) != OK_YES) {
223 			DOWNGRADELOCKS(elf, scn)
224 			return (0);
225 		}
226 		DOWNGRADELOCKS(elf, scn)
227 	}
228 
229 	/*
230 	 * decide where to put destination
231 	 */
232 
233 	switch (elf->ed_status) {
234 	case ES_COOKED:
235 		if ((size_t)d->db_data.d_type >= NALIGN(elf)) {
236 			_elf_seterr(EBUG_COOKTYPE, 0);
237 			return (0);
238 		}
239 
240 		/*
241 		 * If the destination size (memory) is at least as
242 		 * big as the source size (file), and has the necessary
243 		 * alignment, reuse the space.
244 		 *
245 		 * Note that it is not sufficient to check the alignment
246 		 * of the offset within the object. Rather, we must check
247 		 * the alignment of the actual data buffer. The offset is
248 		 * sufficient if the file is a plain object file, which
249 		 * will always be mapped on a page boundary. In an archive
250 		 * however, the only guarantee is that the object will start
251 		 * on an even boundary within the archive file. The
252 		 * Solaris ar(1) adds padding in most (but not all cases)
253 		 * which minimizes this issue, but it is still important
254 		 * for the remaining cases that do not get padded. It also
255 		 * matters with archives produced by other versions of
256 		 * ar(1), such as the GNU version, or one from another
257 		 * ELF based operating system.
258 		 */
259 
260 		if (d->db_data.d_size <= src.d_size) {
261 			d->db_data.d_buf = (Elf_Void *)(elf->ed_ident +
262 			    d->db_off);
263 			if (((uintptr_t)d->db_data.d_buf
264 			    % ALIGN(elf)[d->db_data.d_type]) == 0) {
265 				break;
266 			} else {   /* Failure: Restore NULL buffer pointer */
267 				d->db_data.d_buf = 0;
268 			}
269 		}
270 
271 		/*FALLTHRU*/
272 	case ES_FROZEN:
273 		if ((d->db_buf = malloc(d->db_data.d_size)) == 0) {
274 			_elf_seterr(EMEM_DATA, errno);
275 			return (0);
276 		}
277 		d->db_data.d_buf = d->db_buf;
278 		break;
279 
280 	default:
281 		_elf_seterr(EBUG_COOKSTAT, 0);
282 		return (0);
283 	}
284 
285 	if (elf->ed_class == ELFCLASS32) {
286 		if (elf32_xlatetom(&d->db_data, &src, elf->ed_encode) == 0)
287 			return (0);
288 	} else {	/* ELFCLASS64 */
289 		if (elf64_xlatetom(&d->db_data, &src, elf->ed_encode) == 0)
290 			return (0);
291 	}
292 	d->db_myflags |= DBF_READY;
293 
294 	return (&d->db_data);
295 }
296 
297 Elf_Data *
298 elf_getdata(Elf_Scn * scn, Elf_Data * data)
299 {
300 	Elf_Data *	rc;
301 	Elf *	elf;
302 
303 	/*
304 	 * trap null args, end of list, previous buffer.
305 	 * SHT_NULL sections have no buffer list, so they
306 	 * fall out here too.
307 	 */
308 	if (scn == 0)
309 		return (0);
310 
311 	elf = scn->s_elf;
312 	READLOCKS(elf, scn);
313 	rc = _elf_locked_getdata(scn, data);
314 	READUNLOCKS(elf, scn);
315 	return (rc);
316 }
317