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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
//! Relocation targets: from a symbol index in a file to the definition it
//! refers to.
//!
//! A relocation names a symbol of its own file. A local symbol is defined
//! right there; a global one goes through the file's symbol IDs to the
//! definition resolution chose, which may be in another file, a common
//! symbol, a linker-defined symbol, or nothing.
#![deny(clippy::arithmetic_side_effects)]
use crate::elf::read::consts::{STB_WEAK, STT_GNU_IFUNC, STT_TLS};
use crate::elf::read::{RawSymbol, SectionIndex};
use crate::ids::{FileId, SectionId, SymbolId};
use crate::symbols::{DefinitionKind, Resolution, SymbolTable};
use super::inputs::ElfInput;
use super::sections::Sections;
/// The file ID used in [`crate::symbols::Definition`]s of linker-defined
/// symbols.
pub const LINKER_FILE: FileId = FileId::from_u32(u32::MAX - 1);
/// Where a relocation's symbol is defined.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Def {
/// In section `section` of file `file`, at `value` (raw `st_value`).
Section {
/// Defining file.
file: usize,
/// Section index in that file.
section: u32,
/// The symbol's `st_value`.
value: u64,
},
/// An absolute value.
Absolute(u64),
/// A common symbol (global), allocated by the linker.
Common(SymbolId),
/// A linker-defined symbol (global).
Linker(SymbolId),
/// Defined by a shared object (global): resolved at run time.
Shared(SymbolId),
/// Undefined; `weak` tells whether the reference is weak.
Undefined {
/// The reference (in the referring file) is weak.
weak: bool,
},
}
/// A resolved relocation target.
#[derive(Clone, Copy, Debug)]
pub struct Target {
/// The global symbol, for global references.
pub global: Option<SymbolId>,
/// The definition.
pub def: Def,
/// The defining symbol's raw entry, when there is one in an object;
/// for an undefined thread-local variable, the reference's.
pub raw: Option<RawSymbol>,
}
impl Target {
/// Whether the symbol is an IFUNC.
#[must_use]
pub fn is_ifunc(&self) -> bool {
self.raw.is_some_and(|raw| raw.kind() == STT_GNU_IFUNC)
&& matches!(self.def, Def::Section { .. })
}
/// Whether the symbol is thread-local.
#[must_use]
pub fn is_tls(&self) -> bool {
self.raw.is_some_and(|raw| raw.kind() == STT_TLS)
}
/// Whether it is a section symbol.
#[must_use]
pub fn is_section_symbol(&self) -> bool {
self.raw
.is_some_and(|raw| raw.kind() == crate::elf::read::consts::STT_SECTION)
}
}
/// Read-only access to everything needed to resolve relocation targets.
#[derive(Clone, Copy)]
pub struct Refs<'r, 'a, F: crate::elf::read::ElfFormat = crate::elf::read::Elf64Le> {
/// All inputs.
pub files: &'r [ElfInput<'a, F>],
/// The global symbol table.
pub symbols: &'r SymbolTable<'a>,
/// The resolution result.
pub resolution: &'r Resolution<'a>,
/// Section numbering and liveness.
pub sections: &'r Sections,
}
impl<'r, 'a, F: crate::elf::read::ElfFormat> Refs<'r, 'a, F> {
/// The global symbol ID of symbol `index` of `file`, if it is global.
#[inline]
#[must_use]
pub fn global_id(&self, file: usize, index: usize) -> Option<SymbolId> {
let object = self.files.get(file)?.object.as_ref()?;
let local = index.checked_sub(object.first_global)?;
self.resolution
.symbol_ids(FileId::new(file))
.get(local)
.copied()
}
/// Resolves symbol `index` of `file`.
///
/// A loop over one file's relocations should take
/// [`for_file`](Self::for_file) once instead: this looks the file, its
/// symbol table and its symbol IDs up again for every call.
#[inline(always)]
#[must_use]
pub fn target(&self, file: usize, index: usize) -> Option<Target> {
self.for_file(file)?.target(index)
}
/// A view of one file's relocation targets, with the lookups that do
/// not depend on the symbol done once. `None` if `file` is not an
/// object.
#[inline]
#[must_use]
pub fn for_file(&self, file: usize) -> Option<FileTargets<'r, 'a, F>> {
let object = self.files.get(file)?.object.as_ref()?;
Some(FileTargets {
refs: *self,
file,
symbols: object.elf.symbols(),
first_global: object.first_global,
ids: self.resolution.symbol_ids(FileId::new(file)),
})
}
/// Resolves global symbol `id`; `weak` is the binding of the reference.
#[inline(always)]
#[must_use]
pub fn global_target(&self, id: SymbolId, weak: bool) -> Target {
let def = self.symbols.definition(id);
let undefined = Target {
global: Some(id),
def: Def::Undefined { weak },
raw: None,
};
match def.kind {
DefinitionKind::Undefined | DefinitionKind::Lazy => undefined,
DefinitionKind::Shared => {
let raw = self
.files
.get(def.file.index())
.and_then(|f| f.shared.as_ref())
.and_then(|s| {
let index = *s.symbols.get(def.index as usize)?;
s.elf.symbols().get_raw(index as usize)
});
Target {
global: Some(id),
def: Def::Shared(id),
raw,
}
}
DefinitionKind::Common => Target {
global: Some(id),
def: Def::Common(id),
raw: None,
},
DefinitionKind::Regular | DefinitionKind::Weak => {
if def.file == LINKER_FILE {
return Target {
global: Some(id),
def: Def::Linker(id),
raw: None,
};
}
let file = def.file.index();
let Some(object) = self.files.get(file).and_then(|f| f.object.as_ref()) else {
// The internal file: `--defsym`.
return Target {
global: Some(id),
def: Def::Linker(id),
raw: None,
};
};
let symbols = object.elf.symbols();
let Some(index) = (def.index as usize).checked_add(object.first_global) else {
return undefined;
};
let Some(raw) = symbols.get_raw(index) else {
return undefined;
};
match local_def(file, index, &raw, symbols) {
Some(d) => Target {
global: Some(id),
def: d,
raw: Some(raw),
},
None => undefined,
}
}
}
}
/// The name of symbol `symbol` of `file`, for diagnostics.
#[must_use]
pub fn symbol_name(&self, file: usize, symbol: u32) -> Option<String> {
let object = self.files.get(file)?.object.as_ref()?;
let symbol = object.elf.symbols().get(symbol as usize).ok()?;
Some(String::from_utf8_lossy(symbol.name).into_owned())
}
/// The section a target is defined in.
#[must_use]
pub fn target_section(&self, target: &Target) -> Option<SectionId> {
match target.def {
Def::Section { file, section, .. } => self.sections.id(file, section),
_ => None,
}
}
}
/// One file's relocation targets: [`Refs::for_file`].
///
/// Resolving a relocation's symbol starts with three lookups that are the
/// same for every relocation of the file — the input, its symbol table and
/// the symbol IDs resolution gave its globals. The scan and the writer walk
/// millions of relocations file by file, so they take this view once and
/// ask it for each symbol.
#[derive(Clone, Copy)]
pub struct FileTargets<'r, 'a, F: crate::elf::read::ElfFormat = crate::elf::read::Elf64Le> {
refs: Refs<'r, 'a, F>,
file: usize,
symbols: &'r crate::elf::read::SymbolTable<'a, F>,
first_global: usize,
ids: &'r [SymbolId],
}
impl<'r, 'a, F: crate::elf::read::ElfFormat> FileTargets<'r, 'a, F> {
/// The whole-link view this one came from.
#[inline]
#[must_use]
pub fn refs(&self) -> Refs<'r, 'a, F> {
self.refs
}
/// The global symbol ID of symbol `index`, if it is global.
#[inline]
#[must_use]
pub fn global_id(&self, index: usize) -> Option<SymbolId> {
let local = index.checked_sub(self.first_global)?;
self.ids.get(local).copied()
}
/// Resolves symbol `index` of the file.
#[inline(always)]
#[must_use]
pub fn target(&self, index: usize) -> Option<Target> {
let raw = self.symbols.get_raw(index)?;
if index < self.first_global {
let def = local_def(self.file, index, &raw, self.symbols)?;
return Some(Target {
global: None,
def,
raw: Some(raw),
});
}
let id = self.global_id(index)?;
let mut target = self.refs.global_target(id, raw.binding() == STB_WEAK);
// An undefined thread-local variable has no defining entry, and
// some relocations mean something else against an ordinary symbol
// (LoongArch's extreme code model): keep the reference's entry, so
// that `STT_TLS` is still visible.
if target.raw.is_none() && raw.kind() == STT_TLS {
target.raw = Some(raw);
}
Some(target)
}
}
#[inline(always)]
fn local_def<F: crate::elf::read::ElfFormat>(
file: usize,
index: usize,
raw: &RawSymbol,
symbols: &crate::elf::read::SymbolTable<'_, F>,
) -> Option<Def> {
Some(match symbols.section_of(index, raw)? {
SectionIndex::Section(section) => Def::Section {
file,
section,
value: raw.st_value,
},
SectionIndex::Absolute => Def::Absolute(raw.st_value),
SectionIndex::Undefined => Def::Undefined {
weak: raw.binding() == STB_WEAK,
},
SectionIndex::Common | SectionIndex::Reserved(_) => Def::Absolute(0),
})
}