xref: /illumos-gate/usr/src/cmd/sgs/libld/common/groups.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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 #include	<stdio.h>
28 #include	<string.h>
29 #include	<link.h>
30 #include	<debug.h>
31 #include	"msg.h"
32 #include	"_libld.h"
33 
34 /*
35  * Define an AVL node for maintain group names, together with a compare function
36  * for the Grp_node AVL tree.
37  */
38 typedef struct {
39 	const char	*gn_name;	/* group name */
40 	Is_desc		*gn_isc;	/* group input section */
41 	avl_node_t	gn_avl;		/* avl book-keeping (see SGSOFFSETOF) */
42 	uint_t		gn_hash;	/* group name hash value */
43 } Grp_node;
44 
45 static int
46 gnavl_compare(const void *n1, const void *n2)
47 {
48 	uint_t		hash1, hash2;
49 	const char	*st1, *st2;
50 	int		rc;
51 
52 	hash1 = ((Grp_node *)n1)->gn_hash;
53 	hash2 = ((Grp_node *)n2)->gn_hash;
54 
55 	if (hash1 > hash2)
56 		return (1);
57 	if (hash1 < hash2)
58 		return (-1);
59 
60 	st1 = ((Grp_node *)n1)->gn_name;
61 	st2 = ((Grp_node *)n2)->gn_name;
62 
63 	rc = strcmp(st1, st2);
64 	if (rc > 0)
65 		return (1);
66 	if (rc < 0)
67 		return (-1);
68 	return (0);
69 }
70 
71 /*
72  * Determine whether a (COMDAT) group has already been encountered.  If so,
73  * indicate that the group descriptor has an overriding group (gd_oisc).  This
74  * indication triggers the ld_place_section() to discard this group, while the
75  * gd_oisc information provides for complete diagnostics of the override.
76  * Otherwise, this is the first occurrence of this group, therefore the group
77  * descriptor is saved for future comparisons.
78  */
79 static uintptr_t
80 gpavl_loaded(Ofl_desc *ofl, Group_desc *gdp)
81 {
82 	Grp_node	gpn, *gpnp;
83 	avl_tree_t	*avlt;
84 	avl_index_t	where;
85 
86 	/*
87 	 * Create an avl tree if required.
88 	 */
89 	if ((avlt = ofl->ofl_groups) == 0) {
90 		if ((avlt = calloc(sizeof (avl_tree_t), 1)) == NULL)
91 			return (S_ERROR);
92 		avl_create(avlt, gnavl_compare, sizeof (Grp_node),
93 		    SGSOFFSETOF(Grp_node, gn_avl));
94 		ofl->ofl_groups = avlt;
95 	}
96 
97 	gpn.gn_name = gdp->gd_name;
98 	gpn.gn_hash = sgs_str_hash(gdp->gd_name);
99 
100 	if ((gpnp = avl_find(avlt, &gpn, &where)) != NULL) {
101 		gdp->gd_oisc = gpnp->gn_isc;
102 		return (1);
103 	}
104 
105 	/*
106 	 * This is a new group, save it.
107 	 */
108 	if ((gpnp = calloc(sizeof (Grp_node), 1)) == NULL)
109 		return (S_ERROR);
110 
111 	gpnp->gn_name = gpn.gn_name;
112 	gpnp->gn_hash = gpn.gn_hash;
113 	gpnp->gn_isc = gdp->gd_isc;
114 
115 	avl_insert(avlt, gpnp, where);
116 	return (0);
117 }
118 
119 Group_desc *
120 ld_get_group(Ofl_desc *ofl, Is_desc *isp)
121 {
122 	Ifl_desc	*ifl = isp->is_file;
123 	uint_t		scnndx = isp->is_scnndx;
124 	Group_desc	*gdp;
125 	Aliste		idx;
126 
127 	/*
128 	 * Scan the GROUP sections associated with this file to find the
129 	 * matching group section.
130 	 */
131 	for (ALIST_TRAVERSE(ifl->ifl_groups, idx, gdp)) {
132 		size_t	ndx;
133 		Word	*data;
134 
135 		if (isp->is_shdr->sh_type == SHT_GROUP) {
136 			if (isp->is_scnndx == gdp->gd_isc->is_scnndx)
137 				return (gdp);
138 			continue;
139 		}
140 
141 		data = gdp->gd_data;
142 		for (ndx = 1; ndx < gdp->gd_cnt; ndx++) {
143 			if (data[ndx] == scnndx)
144 				return (gdp);
145 		}
146 	}
147 
148 	eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_ELF_NOGROUPSECT),
149 	    ifl->ifl_name, isp->is_name);
150 	ofl->ofl_flags |= FLG_OF_FATAL;
151 	return (NULL);
152 }
153 
154 uintptr_t
155 ld_group_process(Is_desc *gisc, Ofl_desc *ofl)
156 {
157 	Ifl_desc	*gifl = gisc->is_file;
158 	Shdr		*sshdr, *gshdr = gisc->is_shdr;
159 	Is_desc		*isc;
160 	Sym		*sym;
161 	char		*str;
162 	Group_desc	gd;
163 	size_t		ndx;
164 
165 	/*
166 	 * Confirm that the sh_link points to a valid section.
167 	 */
168 	if ((gshdr->sh_link == SHN_UNDEF) ||
169 	    (gshdr->sh_link >= gifl->ifl_shnum) ||
170 	    ((isc = gifl->ifl_isdesc[gshdr->sh_link]) == NULL)) {
171 		eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHLINK),
172 		    gifl->ifl_name, gisc->is_name, EC_XWORD(gshdr->sh_link));
173 		ofl->ofl_flags |= FLG_OF_FATAL;
174 		return (0);
175 	}
176 	if (gshdr->sh_entsize == 0) {
177 		eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHENTSIZE),
178 		    gifl->ifl_name, gisc->is_name, EC_XWORD(gshdr->sh_entsize));
179 		ofl->ofl_flags |= FLG_OF_FATAL;
180 		return (0);
181 	}
182 
183 	/*
184 	 * Get the associated symbol table.  Sanity check the sh_info field
185 	 * (which points to the signature symbol table entry) against the size
186 	 * of the symbol table.
187 	 */
188 	sshdr = isc->is_shdr;
189 	sym = (Sym *)isc->is_indata->d_buf;
190 
191 	if ((sshdr->sh_info == SHN_UNDEF) ||
192 	    (gshdr->sh_info >= (Word)(sshdr->sh_size / sshdr->sh_entsize)) ||
193 	    ((isc = gifl->ifl_isdesc[sshdr->sh_link]) == NULL)) {
194 		eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHINFO),
195 		    gifl->ifl_name, gisc->is_name, EC_XWORD(gshdr->sh_info));
196 		ofl->ofl_flags |= FLG_OF_FATAL;
197 		return (0);
198 	}
199 
200 	sym += gshdr->sh_info;
201 
202 	/*
203 	 * Get the symbol name from the associated string table.
204 	 */
205 	str = (char *)isc->is_indata->d_buf;
206 	str += sym->st_name;
207 
208 	/*
209 	 * Generate a group descriptor.
210 	 */
211 	gd.gd_isc = gisc;
212 	gd.gd_oisc = NULL;
213 	gd.gd_name = str;
214 	gd.gd_data = gisc->is_indata->d_buf;
215 	gd.gd_cnt = gisc->is_indata->d_size / sizeof (Word);
216 
217 	/*
218 	 * If this group is a COMDAT group, validate the signature symbol.
219 	 */
220 	if ((gd.gd_data[0] & GRP_COMDAT) &&
221 	    ((ELF_ST_BIND(sym->st_info) == STB_LOCAL) ||
222 	    (sym->st_shndx == SHN_UNDEF))) {
223 		/* FATAL or ignore? */
224 		eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_GRP_INVALSYM),
225 		    gifl->ifl_name, gisc->is_name, str);
226 		ofl->ofl_flags |= FLG_OF_FATAL;
227 		return (0);
228 	}
229 
230 	/*
231 	 * Validate the section indices within the group.  If this is a COMDAT
232 	 * group, mark each section as COMDAT.
233 	 */
234 	for (ndx = 1; ndx < gd.gd_cnt; ndx++) {
235 		Word	gndx;
236 
237 		if ((gndx = gd.gd_data[ndx]) >= gifl->ifl_shnum) {
238 			eprintf(ofl->ofl_lml, ERR_FATAL,
239 			    MSG_INTL(MSG_GRP_INVALNDX), gifl->ifl_name,
240 			    gisc->is_name, ndx, gndx);
241 			ofl->ofl_flags |= FLG_OF_FATAL;
242 			return (0);
243 		}
244 
245 		if (gd.gd_data[0] & GRP_COMDAT)
246 			gifl->ifl_isdesc[gndx]->is_flags |= FLG_IS_COMDAT;
247 	}
248 
249 	/*
250 	 * If this is a COMDAT group, determine whether this group has already
251 	 * been encountered, or whether this is the first instance of the group.
252 	 */
253 	if ((gd.gd_data[0] & GRP_COMDAT) &&
254 	    (gpavl_loaded(ofl, &gd) == S_ERROR))
255 		return (S_ERROR);
256 
257 	/*
258 	 * Associate the group descriptor with this input file.
259 	 */
260 	if (alist_append(&(gifl->ifl_groups), &gd, sizeof (Group_desc),
261 	    AL_CNT_IFL_GROUPS) == NULL)
262 		return (S_ERROR);
263 
264 	return (1);
265 }
266