xref: /illumos-gate/usr/src/cmd/sgs/libld/common/sunwmove.c (revision 6e375c8351497b82ffa4f33cbf61d712999b4605)
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	<string.h>
28 #include	<debug.h>
29 #include	"msg.h"
30 #include	"_libld.h"
31 
32 /*
33  *
34  */
35 static uintptr_t
36 make_mvsections(Ofl_desc *ofl)
37 {
38 	Listnode *	lnp1;
39 	Psym_info *	psym;
40 	Word 		mv_nums = 0;
41 	Xword		align_parexpn = 0;	/* for -z nopartial .data sec */
42 	size_t		size_parexpn = 0;	/* Size of parexpn section */
43 
44 	/*
45 	 * Compute the size of the output move section
46 	 */
47 	for (LIST_TRAVERSE(&ofl->ofl_parsym, lnp1, psym)) {
48 		Sym_desc *	symd = psym->psym_symd;
49 		Sym *		sym;
50 		Xword		align_val;
51 
52 		sym = symd->sd_sym;
53 		if (sym->st_shndx == SHN_COMMON)
54 			align_val = sym->st_value;
55 		else
56 			align_val = 8;
57 		if (symd->sd_flags & FLG_SY_PAREXPN) {
58 			/*
59 			 * This global symbol goes to the special
60 			 * partial initialization .data section.
61 			 */
62 			size_parexpn = (size_t)S_ROUND(size_parexpn,
63 			    sym->st_value) + sym->st_size;
64 			if (align_val > align_parexpn)
65 				align_parexpn = align_val;
66 
67 		} else {
68 			mv_nums += psym->psym_num;
69 		}
70 	}
71 
72 	if (mv_nums != 0) {
73 		if (ld_make_sunwmove(ofl, mv_nums) == S_ERROR)
74 			return (S_ERROR);
75 	}
76 
77 	/*
78 	 * Add empty area for partially initialized symbols.
79 	 *
80 	 * A special .data section is created when the '-z nopartial'
81 	 * option is in effect in order to receive the expanded data.
82 	 */
83 	if (size_parexpn) {
84 		/* LINTED */
85 		if (ld_make_parexpn_data(ofl, size_parexpn,
86 		    align_parexpn) == S_ERROR)
87 			return (S_ERROR);
88 	}
89 	return (1);
90 }
91 
92 /*
93  * This function insert the Move_itm into the move list held by
94  * psymp.
95  */
96 static uintptr_t
97 insert_mvitm(Ofl_desc *ofl, Psym_info *psymp, Mv_itm *itm)
98 {
99 	Listnode *	lnpc, *lnpp, *new;
100 	Mv_itm *	mvp;
101 
102 	/*
103 	 * If there is error on this symbol already,
104 	 * don't go any further.
105 	 */
106 	if ((psymp->psym_flag & FLG_PSYM_OVERLAP) != 0)
107 		return (1);
108 
109 	if ((new = libld_calloc(sizeof (Listnode), 1)) == 0)
110 		return (S_ERROR);
111 	new->data = (void *) itm;
112 	lnpp = lnpc = psymp->psym_mvs.head;
113 
114 	/*
115 	 * If this is the first, just update the
116 	 * head and tail.
117 	 */
118 	if (lnpc == (Listnode *) NULL) {
119 		psymp->psym_mvs.tail = psymp->psym_mvs.head = new;
120 		return (1);
121 	}
122 
123 	for (LIST_TRAVERSE(&psymp->psym_mvs, lnpc, mvp)) {
124 		Mv_itm *	small, *large;
125 
126 		/*
127 		 * Check overlapping
128 		 * If there is no overlapping so far,
129 		 * check overlapping.
130 		 */
131 		if (itm->mv_start > mvp->mv_start) {
132 			small = mvp;
133 			large = itm;
134 		} else {
135 			small = itm;
136 			large = mvp;
137 		}
138 
139 		if ((itm->mv_start == mvp->mv_start) ||
140 		    (small->mv_start + small->mv_length > large->mv_start)) {
141 			eprintf(ofl->ofl_lml, ERR_FATAL,
142 			    MSG_INTL(MSG_PSYM_OVERLAP),
143 			    psymp->psym_symd->sd_file->ifl_name,
144 			    itm->mv_isp->is_name,
145 			    demangle(psymp->psym_symd->sd_name));
146 			psymp->psym_flag |= FLG_PSYM_OVERLAP;
147 			return (1);
148 		}
149 
150 		/*
151 		 * If passed, insert
152 		 */
153 		if (mvp->mv_start > itm->mv_start) {
154 			new->next = lnpc;
155 			if (lnpc == psymp->psym_mvs.head) {
156 				psymp->psym_mvs.head = new;
157 			} else
158 				lnpp->next = new;
159 			return (1);
160 		}
161 
162 		/*
163 		 * If lnpc is the end, add
164 		 */
165 		if (lnpc->next == NULL) {
166 			new->next = lnpc->next;
167 			lnpc->next = new;
168 			psymp->psym_mvs.tail = new;
169 			return (1);
170 		}
171 
172 		/*
173 		 * Go next
174 		 */
175 		lnpp = lnpc;
176 	}
177 	return (1);
178 }
179 
180 /*
181  * Install the mv entry into the Psym_info
182  *
183  * Count coverage size
184  *	If the coverage size meets the symbol size,
185  *	mark that the symbol should be expanded.
186  *	psymp->psym_symd->sd_flags |= FLG_SY_PAREXPN;
187  *
188  * Check overlapping
189  *	If overlapping occurs, mark it at psymp->psym_flags
190  */
191 static uintptr_t
192 install_mv(Ofl_desc *ofl, Psym_info *psymp, Move *mv, Is_desc *isp)
193 {
194 	Mv_itm *	mvitmp;
195 	int 		cnt = mv->m_repeat;
196 	int 		i;
197 
198 	if ((mvitmp = libld_calloc(sizeof (Mv_itm), cnt)) == 0)
199 		return (S_ERROR);
200 
201 	mvitmp->mv_flag |= FLG_MV_OUTSECT;
202 	psymp->psym_num += 1;
203 	for (i = 0; i < cnt; i++) {
204 		/* LINTED */
205 		mvitmp->mv_length = ELF_M_SIZE(mv->m_info);
206 		mvitmp->mv_start = mv->m_poffset + i *
207 		    ((mv->m_stride + 1) * mvitmp->mv_length);
208 		mvitmp->mv_ientry = mv;
209 		mvitmp->mv_isp = isp;		/* Mark input section */
210 
211 		/*
212 		 * Insert the item
213 		 */
214 		if (insert_mvitm(ofl, psymp, mvitmp) == S_ERROR)
215 			return (S_ERROR);
216 		mvitmp++;
217 	}
218 	return (1);
219 }
220 
221 /*
222  * Insert the given psym_info
223  */
224 static uintptr_t
225 insert_psym(Ofl_desc *ofl, Psym_info *p1)
226 {
227 	Listnode *	lnpc, *lnpp, *new;
228 	Psym_info *	p2;
229 	int		g1 = 0;
230 
231 	if ((new = libld_calloc(sizeof (Listnode), 1)) == 0)
232 		return (S_ERROR);
233 	new->data = (void *) p1;
234 	lnpp = lnpc = ofl->ofl_parsym.head;
235 	if (ELF_ST_BIND(p1->psym_symd->sd_sym->st_info) != STB_LOCAL)
236 		g1 = 1;
237 
238 	/*
239 	 * If this is the first, just update the
240 	 * head and tail.
241 	 */
242 	if (lnpc == (Listnode *) NULL) {
243 		ofl->ofl_parsym.tail = ofl->ofl_parsym.head = new;
244 		return (1);
245 	}
246 
247 	for (LIST_TRAVERSE(&ofl->ofl_parsym, lnpc, p2)) {
248 		int cmp1, g2, cmp;
249 
250 		if (ELF_ST_BIND(p2->psym_symd->sd_sym->st_info) != STB_LOCAL)
251 			g2 = 1;
252 		else
253 			g2 = 0;
254 
255 		cmp1 = strcmp(p1->psym_symd->sd_name, p2->psym_symd->sd_name);
256 
257 		/*
258 		 * Compute position
259 		 */
260 		if (g1 == g2)
261 			cmp = cmp1;
262 		else if (g1 == 0) {
263 			/*
264 			 * p1 is a local symbol.
265 			 * p2 is a global, so p1 passed.
266 			 */
267 			cmp = -1;
268 		} else {
269 			/*
270 			 * p1 is global
271 			 * p2 is still local.
272 			 * so try the next one.
273 			 *
274 			 * If lnpc is the end, add
275 			 */
276 			if (lnpc->next == NULL) {
277 				new->next = lnpc->next;
278 				lnpc->next = new;
279 				ofl->ofl_parsym.tail = new;
280 				break;
281 			}
282 			lnpp = lnpc;
283 			continue;
284 		}
285 
286 		/*
287 		 * If same, just add after
288 		 */
289 		if (cmp == 0) {
290 			new->next = lnpc->next;
291 			if (lnpc == ofl->ofl_parsym.tail)
292 				ofl->ofl_parsym.tail = new;
293 			lnpc->next = new;
294 			break;
295 		}
296 
297 		/*
298 		 * If passed, insert
299 		 */
300 		if (cmp < 0) {
301 			new->next = lnpc;
302 			if (lnpc == ofl->ofl_parsym.head) {
303 				ofl->ofl_parsym.head = new;
304 			} else
305 				lnpp->next = new;
306 			break;
307 		}
308 
309 		/*
310 		 * If lnpc is the end, add
311 		 */
312 		if (lnpc->next == NULL) {
313 			new->next = lnpc->next;
314 			lnpc->next = new;
315 			ofl->ofl_parsym.tail = new;
316 			break;
317 		}
318 
319 		/*
320 		 * Go next
321 		 */
322 		lnpp = lnpc;
323 	}
324 	return (1);
325 }
326 
327 /*
328  * Mark the symbols
329  *
330  * Check only the symbols which came from the relocatable
331  * files.If partially initialized symbols come from
332  * shared objects, they can be ignored here because
333  * they are already processed when the shared object is
334  * created.
335  *
336  */
337 uintptr_t
338 ld_sunwmove_preprocess(Ofl_desc *ofl)
339 {
340 	Listnode *	lnp;
341 	Is_desc *	isp;
342 	Sym_desc *	sdp;
343 	Move *		mv;
344 	Psym_info *	psym;
345 	int 		errcnt = 0;
346 
347 	for (LIST_TRAVERSE(&ofl->ofl_ismove, lnp, isp)) {
348 		Ifl_desc *	ifile = isp->is_file;
349 		Xword		i, num;
350 
351 		DBG_CALL(Dbg_move_input(ofl->ofl_lml, ifile->ifl_name));
352 		mv = (Move *) isp->is_indata->d_buf;
353 
354 		if (isp->is_shdr->sh_entsize == 0) {
355 			eprintf(ofl->ofl_lml, ERR_FATAL,
356 			    MSG_INTL(MSG_FIL_INVSHENTSIZE),
357 			    isp->is_file->ifl_name, isp->is_name, EC_XWORD(0));
358 			return (S_ERROR);
359 		}
360 		num = isp->is_shdr->sh_size/isp->is_shdr->sh_entsize;
361 		for (i = 0; i < num; i++) {
362 			Xword 	ndx = ELF_M_SYM(mv->m_info);
363 
364 			if ((ndx >= (Xword) isp->is_file->ifl_symscnt) ||
365 			    (ndx == 0)) {
366 				eprintf(ofl->ofl_lml, ERR_FATAL,
367 				    MSG_INTL(MSG_PSYM_INVMINFO1),
368 				    isp->is_file->ifl_name, isp->is_name, i,
369 				    EC_XWORD(mv->m_info));
370 				return (S_ERROR);
371 			}
372 			if (mv->m_repeat == 0) {
373 				eprintf(ofl->ofl_lml, ERR_FATAL,
374 				    MSG_INTL(MSG_PSYM_INVMREPEAT),
375 				    isp->is_file->ifl_name, isp->is_name, i,
376 				    EC_XWORD(mv->m_repeat));
377 				return (S_ERROR);
378 			}
379 
380 			sdp = isp->is_file->ifl_oldndx[ndx];
381 			DBG_CALL(Dbg_move_entry1(ofl->ofl_lml, 0, mv, sdp));
382 
383 			/*
384 			 * Check if this entry has a valid size of not
385 			 */
386 			/* LINTED */
387 			switch (ELF_M_SIZE(mv->m_info)) {
388 			case 1: case 2: case 4: case 8:
389 				break;
390 			default:
391 				eprintf(ofl->ofl_lml, ERR_FATAL,
392 				    MSG_INTL(MSG_PSYM_INVMINFO2),
393 				    isp->is_file->ifl_name, isp->is_name, i,
394 				    EC_XWORD(mv->m_info));
395 				return (S_ERROR);
396 			}
397 
398 			/*
399 			 * If this is a global symbol, adjust the visibility.
400 			 */
401 			if (sdp->sd_aux &&
402 			    ((sdp->sd_flags & FLG_SY_VISIBLE) == 0))
403 				ld_sym_adjust_vis(sdp, ofl);
404 
405 			if (sdp->sd_psyminfo == (Psym_info *)NULL) {
406 				/*
407 				 * Mark the symbol as partial, and install the
408 				 * symbol in the partial symbol list.
409 				 */
410 				if ((psym =
411 				    libld_calloc(sizeof (Psym_info), 1)) == 0)
412 					return (S_ERROR);
413 				psym->psym_symd = sdp;
414 				sdp->sd_psyminfo = psym;
415 
416 				/*
417 				 * Even if the -zredlocsym is in effect, the
418 				 * local symbol used for partial initialization
419 				 * is kept.
420 				 */
421 				if ((ofl->ofl_flags & FLG_OF_REDLSYM) &&
422 				    (ELF_ST_BIND(sdp->sd_sym->st_info) ==
423 				    STB_LOCAL) &&
424 				    (ELF_ST_TYPE(sdp->sd_sym->st_info) ==
425 				    STT_OBJECT)) {
426 					ofl->ofl_locscnt++;
427 					if (st_insert(ofl->ofl_strtab,
428 					    sdp->sd_name) == -1)
429 						return (S_ERROR);
430 				}
431 				if (insert_psym(ofl, psym) == 0)
432 					return (S_ERROR);
433 
434 				/*
435 				 * Mark the input section which the partially
436 				 * initialized * symbol is defined.
437 				 * This is needed when the symbol
438 				 * the relocation entry uses symbol information
439 				 * not from the symbol entry.
440 				 *
441 				 * For executable, the following is
442 				 * needed only for expanded symbol. However,
443 				 * for shared object * any partially non
444 				 * expanded symbols are moved * from
445 				 * .bss/COMMON to .sunwbss. So the following are
446 				 * needed.
447 				 */
448 				if ((sdp->sd_sym->st_shndx != SHN_UNDEF) &&
449 				    (sdp->sd_sym->st_shndx < SHN_LOPROC)) {
450 					Is_desc * isym = ifile->ifl_isdesc[
451 					    sdp->sd_sym->st_shndx];
452 					isym->is_flags |= FLG_IS_RELUPD;
453 					if (sdp->sd_osym == (Sym *) 0) {
454 						if ((sdp->sd_osym =
455 						    libld_calloc(sizeof (Sym),
456 						    1)) == 0)
457 							return (S_ERROR);
458 						*(sdp->sd_osym) =
459 						    *(sdp->sd_sym);
460 					}
461 				}
462 			} else
463 				psym = sdp->sd_psyminfo;
464 
465 			if (install_mv(ofl, psym, mv, isp) == S_ERROR)
466 				return (S_ERROR);
467 			if ((psym->psym_flag & FLG_PSYM_OVERLAP) != 0)
468 				errcnt++;
469 
470 			/*
471 			 * If this symbol is marked to be
472 			 * expanded, go to the next moveentry.
473 			 */
474 			if (sdp->sd_flags & FLG_SY_PAREXPN) {
475 				mv++;
476 				continue;
477 			}
478 
479 			/*
480 			 * Decide whether this partial symbol is to be expanded
481 			 * or not.
482 			 *
483 			 * The symbol will be expanded if:
484 			 *	a) '-z nopartial' is specified
485 			 *	b) move entries covered entire symbol
486 			 *
487 			 * To expand an move entry, size of the symbol to be
488 			 * expanded need to be known to generate a file space.
489 			 * (see make_movesections().)
490 			 *
491 			 * Therefore the move entry can not be expanded
492 			 * if the partial symbol is a section symbol.
493 			 * (The size of the symbol may be unknown.)
494 			 * This may happen, for example, when a local symbol is
495 			 * reduced by the -zredlocsym.
496 			 *
497 			 * The following two if statements checks the
498 			 * if the move entry can be expanded or not.
499 			 */
500 			if (((ofl->ofl_flags & FLG_OF_STATIC) != 0) &&
501 			    ((ofl->ofl_flags & FLG_OF_EXEC) != 0)) {
502 				if (ELF_ST_TYPE(sdp->sd_sym->st_info) ==
503 				    STT_SECTION) {
504 					errcnt++;
505 					eprintf(ofl->ofl_lml, ERR_FATAL,
506 					    MSG_INTL(MSG_PSYM_CANNOTEXPND),
507 					    psym->psym_symd->sd_file->ifl_name,
508 					    isp->is_name, i,
509 					    MSG_INTL(MSG_PSYM_NOSTATIC));
510 				} else {
511 					sdp->sd_flags |= FLG_SY_PAREXPN;
512 				}
513 			} else if ((ofl->ofl_flags1 & FLG_OF1_NOPARTI) != 0) {
514 				if (ELF_ST_TYPE(sdp->sd_sym->st_info) ==
515 				    STT_SECTION) {
516 					eprintf(ofl->ofl_lml, ERR_WARNING,
517 					    MSG_INTL(MSG_PSYM_CANNOTEXPND),
518 					    psym->psym_symd->sd_file->ifl_name,
519 					    isp->is_name, i,
520 					    MSG_ORIG(MSG_STR_EMPTY));
521 				} else {
522 					sdp->sd_flags |= FLG_SY_PAREXPN;
523 				}
524 			} else if (
525 			    ((Xword)((sizeof (Move)) * psym->psym_num) >
526 			    psym->psym_symd->sd_sym->st_size) &&
527 			    (ELF_ST_TYPE(sdp->sd_sym->st_info) == STT_OBJECT)) {
528 				sdp->sd_flags |= FLG_SY_PAREXPN;
529 			}
530 
531 			/*
532 			 * If a move section exists that references .bss, make
533 			 * sure a section symbol for .bss is introduced into
534 			 * the .dynsym.
535 			 */
536 			if (((sdp->sd_flags & FLG_SY_PAREXPN) == 0) &&
537 			    ((ELF_ST_BIND(sdp->sd_sym->st_info) == STB_LOCAL) ||
538 			    ((sdp->sd_flags1 & FLG_SY1_HIDDEN) &&
539 			    (ofl->ofl_flags & FLG_OF_PROCRED)))) {
540 				ofl->ofl_flags1 |= FLG_OF1_BSSOREL;
541 			}
542 			mv++;
543 		}
544 	}
545 
546 	if (errcnt != 0)
547 		return (S_ERROR);
548 	if (make_mvsections(ofl) == S_ERROR)
549 		return (S_ERROR);
550 
551 	return (1);
552 }
553