summaryrefslogtreecommitdiff
path: root/usr/src/cmd/sgs/librtld/common/dynamic.c
blob: 580b61732fde30fef310395a5c95c9dced22d4c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Update any dynamic entry offsets.  One issue with dynamic entries is that
 * you only know whether they refer to a value or an offset if you know each
 * type.  Thus we check for all types we know about, it a type is found that
 * we don't know about then return and error as we have no idea what to do.
 */

#include	<libelf.h>
#include	<link.h>
#include	"libld.h"
#include	"msg.h"
#include	"rtld.h"
#include	"_librtld.h"

int
update_dynamic(Cache *cache, Cache *_cache, Rt_map *lmp, int flags,
    Addr addr, Off off, const char *file, Xword null, Xword data, Xword func,
    Xword entsize, Xword checksum)
{
	Dyn		*dyn = (Dyn *)_cache->c_data->d_buf, *posdyn = 0;
	const char	*strs;
	Cache		*__cache;

	/*
	 * If we're dealing with an object that might have bound to an external
	 * dependency establish our string table for possible NEEDED processing.
	 */
	if (flags & RTLD_REL_DEPENDS) {
		__cache = &cache[_cache->c_shdr->sh_link];
		strs = (const char *)__cache->c_data->d_buf;
	}

	/*
	 * Loop through the dynamic table updating all offsets.
	 */
	while (dyn->d_tag != DT_NULL) {
		Rt_map	*dlmp;

		switch ((Xword)dyn->d_tag) {
		case DT_NEEDED:
			if (posdyn == 0)
				break;

			/*
			 * Determine whether this dependency has been loaded
			 * (this is the most generic way to check any alias
			 * names), and if it has been bound to, undo any
			 * lazy-loading or deferred position flag.
			 */
			if (dlmp = is_so_loaded(LIST(lmp),
			    (strs + dyn->d_un.d_val), NULL)) {
				Bnd_desc	*bdp;
				Aliste		idx;

				for (APLIST_TRAVERSE(DEPENDS(lmp), idx, bdp)) {
					if (dlmp != bdp->b_depend)
						continue;

					posdyn->d_un.d_val &=
					    ~(DF_P1_LAZYLOAD | DF_P1_DEFERRED);
					break;
				}
			}
			break;

		case DT_RELAENT:
		case DT_STRSZ:
		case DT_SYMENT:
		case DT_SONAME:
		case DT_RPATH:
		case DT_SYMBOLIC:
		case DT_RELENT:
		case DT_PLTREL:
		case DT_TEXTREL:
		case DT_VERDEFNUM:
		case DT_VERNEEDNUM:
		case DT_AUXILIARY:
		case DT_USED:
		case DT_FILTER:
		case DT_DEPRECATED_SPARC_REGISTER:
		case M_DT_REGISTER:
		case DT_BIND_NOW:
		case DT_INIT_ARRAYSZ:
		case DT_FINI_ARRAYSZ:
		case DT_RUNPATH:
		case DT_FLAGS:
		case DT_CONFIG:
		case DT_DEPAUDIT:
		case DT_AUDIT:
		case DT_SUNW_SYMSZ:
			break;
		case DT_PLTGOT:
		case DT_HASH:
		case DT_STRTAB:
		case DT_SYMTAB:
		case DT_SUNW_SYMTAB:
		case DT_INIT:
		case DT_FINI:
		case DT_VERSYM:
		case DT_VERDEF:
		case DT_VERNEED:
		case DT_INIT_ARRAY:
		case DT_FINI_ARRAY:
			dyn->d_un.d_ptr += addr;
			break;

		/*
		 * If the memory image is being used, this element would have
		 * been initialized to the runtime linkers internal link-map
		 * list.  Clear it.
		 */
		case DT_DEBUG:
			dyn->d_un.d_val = 0;
			break;

		/*
		 * The number of relocations may have been reduced if
		 * relocations have been saved in the new image.  Thus we
		 * compute the new relocation size and start.
		 */
		case DT_RELASZ:
		case DT_RELSZ:
			dyn->d_un.d_val = ((data + func) * entsize);
			break;

		case DT_RELA:
		case DT_REL:
			dyn->d_un.d_ptr = (addr + off + (null * entsize));
			break;

		/*
		 * If relative relocations have been processed clear the count.
		 */
		case DT_RELACOUNT:
		case DT_RELCOUNT:
			if (flags & RTLD_REL_RELATIVE)
				dyn->d_un.d_val = 0;
			break;

		case DT_PLTRELSZ:
			dyn->d_un.d_val = (func * entsize);
			break;

		case DT_JMPREL:
			dyn->d_un.d_ptr = (addr + off +
			    ((null + data) * entsize));
			break;

		/*
		 * Recompute the images elf checksum.
		 */
		case DT_CHECKSUM:
			dyn->d_un.d_val = checksum;
			break;

		/*
		 * If a flag entry is available, indicate if this image has
		 * been generated via the configuration process (crle(1)).
		 * Because we only started depositing DT_FLAGS_1 entries in all
		 * objects starting with Solaris 8, set a feature flag if it
		 * is present (these got added in Solaris 7).
		 * The runtime linker may use this flag to search for a local
		 * configuration file - this is only meaningful in executables
		 * but the flag has value for identifying images regardless.
		 *
		 * If this file is acting as a filter, and dependency
		 * relocations have been processed (a filter is thought of as a
		 * dependency in terms of symbol binding), we may have bound to
		 * the filtee, and hence carried out the relocation.  Indicate
		 * that the filtee must be preloaded, as the .plt won't get
		 * exercised to cause its normal loading.
		 */
		case DT_FLAGS_1:
			if (flags & RTLD_CONFSET)
				dyn->d_un.d_val |= DF_1_CONFALT;
			if ((flags & RTLD_REL_DEPENDS) &&
			    (FLAGS1(lmp)) & MSK_RT_FILTER)
				dyn->d_un.d_val |= DF_1_LOADFLTR;
			break;

		case DT_FEATURE_1:
			if (flags & RTLD_CONFSET)
				dyn->d_un.d_val |= DTF_1_CONFEXP;
			break;

		/*
		 * If a position flag is available save it for possible update
		 * when processing the next NEEDED tag.
		 */
		case DT_POSFLAG_1:
			if (flags & RTLD_REL_DEPENDS) {
				posdyn = dyn++;
				continue;
			}
			break;

		/*
		 * Collect the defaults.
		 */
		default:
			/*
			 * If d_val is used, don't touch.
			 */
			if ((dyn->d_tag >= DT_VALRNGLO) &&
			    (dyn->d_tag <= DT_VALRNGHI))
				break;

			/*
			 * If d_ptr is used, adjust.  Note, some entries that
			 * fell into this range are offsets into the dynamic
			 * string table.  Although these would need modifying
			 * if the section itself were resized, there is no
			 * resizing with dldump().  Entries that correspond to
			 * offsets are picked off in the initial DT_ loop
			 * above.
			 */
			if ((dyn->d_tag >= DT_ADDRRNGLO) &&
			    (dyn->d_tag <= DT_ADDRRNGHI)) {
				dyn->d_un.d_ptr += addr;
				break;
			}

			/*
			 * Check to see if this DT_ entry conforms
			 * to the DT_ENCODING rules.
			 */
			if ((dyn->d_tag >= DT_ENCODING) &&
			    (dyn->d_tag <= DT_HIOS)) {
				/*
				 * Even tag values are ADDRESS encodings
				 */
				if ((dyn->d_tag % 2) == 0) {
					dyn->d_un.d_ptr += addr;
				}
				break;
			}
			eprintf(LIST(lmp), ERR_WARNING,
			    MSG_INTL(MSG_DT_UNKNOWN), file,
			    EC_XWORD(dyn->d_tag));
			return (1);
		}
		posdyn = 0;
		dyn++;
	}
	return (0);
}