summaryrefslogtreecommitdiff
path: root/usr/src/cmd/sgs/rtld/common/_inline_reloc.h
blob: 23ee41e288b9cda17b2ca0b86c493d9adf3f69f1 (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
/*
 * 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) 2010, Oracle and/or its affiliates. All rights reserved.
 */
#ifndef	_INLINE_RELOC_H
#define	_INLINE_RELOC_H

#include	<sys/types.h>
#include	<rtld.h>
#include	<debug.h>

/*
 * Generic relative relocation function.
 */
inline static ulong_t
/* LINTED */
/* ARGSUSED4 */
_elf_reloc_relative(ulong_t rbgn, ulong_t base, Rt_map *lmp, APlist **textrel,
    int add)
{
	mmapobj_result_t	*mpp;
	ulong_t			roffset;

	roffset = ((M_RELOC *)rbgn)->r_offset;
	roffset += base;

	/*
	 * If this relocation is against an address that is not associated with
	 * a mapped segment, fall back to the generic relocation loop to
	 * collect the associated error.
	 */
	if ((mpp = find_segment((caddr_t)roffset, lmp)) == NULL)
		return (0);

	/*
	 * If this relocation is against a segment that does not provide write
	 * access, set the write permission for all non-writable mappings.
	 */
	if (((mpp->mr_prot & PROT_WRITE) == 0) && textrel &&
	    ((set_prot(lmp, mpp, 1) == 0) ||
	    (aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL)))
		return (0);

	/*
	 * Perform a base address update.  This simple operation is required
	 * for updating .plt relocations in preparation for lazy binding.
	 */
#if	defined(__x86)
	if (add) {
		*((ulong_t *)roffset) += base;
		return (1);
	}
#endif
	/*
	 * Perform the actual relocation.  Note, for backward compatibility,
	 * SPARC relocations are added to the offset contents (there was a time
	 * when the offset was used to contain the addend, rather than using
	 * the addend itself).
	 */
#if	defined(__sparc)
	*((ulong_t *)roffset) += base + ((M_RELOC *)rbgn)->r_addend;
#elif	defined(__amd64)
	*((ulong_t *)roffset) = base + ((M_RELOC *)rbgn)->r_addend;
#else
	*((ulong_t *)roffset) += base;
#endif
	return (1);
}

/*
 * When a generic relocation loop realizes that it's dealing with relative
 * relocations, but no DT_RELCOUNT .dynamic tag is present, this tighter loop
 * is entered as an optimization.
 */
inline static ulong_t
/* LINTED */
elf_reloc_relative(ulong_t rbgn, ulong_t rend, ulong_t rsize, ulong_t base,
    Rt_map *lmp, APlist **textrel, int add)
{
	uchar_t	rtype;

	do {
		if (_elf_reloc_relative(rbgn, base, lmp, textrel, add) == 0)
			break;

		rbgn += rsize;
		if (rbgn >= rend)
			break;

		/*
		 * Make sure the next type is a relative relocation.
		 */
		rtype = ELF_R_TYPE(((M_RELOC *)rbgn)->r_info, M_MACH);

	} while (rtype == M_R_RELATIVE);

	return (rbgn);
}

/*
 * This is the tightest loop for RELATIVE relocations for those objects built
 * with the DT_RELACOUNT .dynamic entry.
 */
inline static ulong_t
/* LINTED */
elf_reloc_relative_count(ulong_t rbgn, ulong_t rcount, ulong_t rsize,
    ulong_t base, Rt_map *lmp, APlist **textrel, int add)
{
	for (; rcount; rcount--) {
		if (_elf_reloc_relative(rbgn, base, lmp, textrel, add) == 0)
			break;

		rbgn += rsize;
	}
	return (rbgn);
}

/*
 * Determine, from a symbols Syminfo information, whether a symbol reference
 * is deferred.  This routine is called from elf_reloc() as part of processing
 * an objects relocations.
 */
inline static int
/* LINTED */
is_sym_deferred(ulong_t rbgn, ulong_t base, Rt_map *lmp, APlist **textrel,
    Syminfo *sip, ulong_t sndx)
{
	Syminfo	*sipe;

	/*
	 * ldd(1) by default, sets LD_DEFERRED to force deferred dependency
	 * processing.  ldd -D disables LD_DEFERRED, which allows ld.so.1's
	 * default action of skipping deferred dependencies.
	 */
	if (rtld_flags & RT_FL_DEFERRED)
		return (0);

	/* LINTED */
	sipe = (Syminfo *)((char *)sip + (sndx * SYMINENT(lmp)));
	if (sipe->si_flags & SYMINFO_FLG_DEFERRED) {
		/*
		 * This .plt relocation should be skipped at this time, as
		 * deferred references are only processed when the associated
		 * function is explicitly called.
		 *
		 * On i386 and amd64 platforms the relocation offset needs
		 * adjusting to add this objects base address.  If the object
		 * has already been relocated without RTLD_NOW, then this
		 * update will have already been carried out.  However, if this
		 * is an initial RTLD_NOW relocation pass, this relocation
		 * offset needs updating now.
		 */
#if	defined(__x86)
		if ((FLAGS(lmp) & FLG_RT_RELOCED) == 0)
			(void) _elf_reloc_relative(rbgn, base, lmp, textrel, 1);
#endif
		return (1);
	}
	return (0);
}

#endif	/* _INLINE_RELOC_H */