Skip to content

Commit be161a1

Browse files
authored
Avoid transmutes with Context (#429)
This commit re-consolidates transmutes around `Context` to just one location to ensure that this is in as few places as possible. This reorganizes the location of the `map_sup` variable and places it inside of `Stash` instead which is a sort of auxiliary bag for extra information that can be referenced anyway. Afterwards with some refactoring an a new `Mapping::mk` function it's back to one transmute, yay!
1 parent ed3689c commit be161a1

File tree

3 files changed

+74
-57
lines changed

3 files changed

+74
-57
lines changed

src/symbolize/gimli.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,45 @@ struct Mapping {
5858
// 'static lifetime is a lie to hack around lack of support for self-referential structs.
5959
cx: Context<'static>,
6060
_map: Mmap,
61-
_map_sup: Option<Mmap>,
6261
_stash: Stash,
6362
}
6463

65-
impl Mapping {
64+
enum Either<A, B> {
6665
#[allow(dead_code)]
66+
A(A),
67+
B(B),
68+
}
69+
70+
impl Mapping {
71+
/// Creates a `Mapping` by ensuring that the `data` specified is used to
72+
/// create a `Context` and it can only borrow from that or the `Stash` of
73+
/// decompressed sections or auxiliary data.
6774
fn mk<F>(data: Mmap, mk: F) -> Option<Mapping>
6875
where
69-
F: for<'a> Fn(&'a [u8], &'a Stash) -> Option<Context<'a>>,
76+
F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Context<'a>>,
77+
{
78+
Mapping::mk_or_other(data, move |data, stash| {
79+
let cx = mk(data, stash)?;
80+
Some(Either::B(cx))
81+
})
82+
}
83+
84+
/// Creates a `Mapping` from `data`, or if the closure decides to, returns a
85+
/// different mapping.
86+
fn mk_or_other<F>(data: Mmap, mk: F) -> Option<Mapping>
87+
where
88+
F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Either<Mapping, Context<'a>>>,
7089
{
7190
let stash = Stash::new();
72-
let cx = mk(&data, &stash)?;
91+
let cx = match mk(&data, &stash)? {
92+
Either::A(mapping) => return Some(mapping),
93+
Either::B(cx) => cx,
94+
};
7395
Some(Mapping {
7496
// Convert to 'static lifetimes since the symbols should
7597
// only borrow `map` and `stash` and we're preserving them below.
7698
cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) },
7799
_map: data,
78-
_map_sup: None,
79100
_stash: stash,
80101
})
81102
}

src/symbolize/gimli/elf.rs

Lines changed: 28 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::mystd::ffi::{OsStr, OsString};
22
use super::mystd::fs;
33
use super::mystd::os::unix::ffi::{OsStrExt, OsStringExt};
44
use super::mystd::path::{Path, PathBuf};
5+
use super::Either;
56
use super::{Context, Mapping, Stash, Vec};
67
use core::convert::{TryFrom, TryInto};
78
use core::str;
@@ -18,75 +19,50 @@ type Elf = object::elf::FileHeader64<NativeEndian>;
1819
impl Mapping {
1920
pub fn new(path: &Path) -> Option<Mapping> {
2021
let map = super::mmap(path)?;
21-
let object = Object::parse(&map)?;
22+
Mapping::mk_or_other(map, |map, stash| {
23+
let object = Object::parse(&map)?;
2224

23-
// Try to locate an external debug file using the build ID.
24-
if let Some(path_debug) = object.build_id().and_then(locate_build_id) {
25-
if let Some(mapping) = Mapping::new_debug(path_debug, None) {
26-
return Some(mapping);
25+
// Try to locate an external debug file using the build ID.
26+
if let Some(path_debug) = object.build_id().and_then(locate_build_id) {
27+
if let Some(mapping) = Mapping::new_debug(path_debug, None) {
28+
return Some(Either::A(mapping));
29+
}
2730
}
28-
}
2931

30-
// Try to locate an external debug file using the GNU debug link section.
31-
if let Some((path_debug, crc)) = object.gnu_debuglink_path(path) {
32-
if let Some(mapping) = Mapping::new_debug(path_debug, Some(crc)) {
33-
return Some(mapping);
32+
// Try to locate an external debug file using the GNU debug link section.
33+
if let Some((path_debug, crc)) = object.gnu_debuglink_path(path) {
34+
if let Some(mapping) = Mapping::new_debug(path_debug, Some(crc)) {
35+
return Some(Either::A(mapping));
36+
}
3437
}
35-
}
3638

37-
let stash = Stash::new();
38-
let cx = Context::new(&stash, object, None)?;
39-
Some(Mapping {
40-
// Convert to 'static lifetimes since the symbols should
41-
// only borrow `map` and `stash` and we're preserving them below.
42-
cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) },
43-
_map: map,
44-
_map_sup: None,
45-
_stash: stash,
39+
Context::new(stash, object, None).map(Either::B)
4640
})
4741
}
4842

4943
/// Load debuginfo from an external debug file.
5044
fn new_debug(path: PathBuf, crc: Option<u32>) -> Option<Mapping> {
5145
let map = super::mmap(&path)?;
52-
let object = Object::parse(&map)?;
46+
Mapping::mk(map, |map, stash| {
47+
let object = Object::parse(&map)?;
5348

54-
if let Some(_crc) = crc {
55-
// TODO: check crc
56-
}
49+
if let Some(_crc) = crc {
50+
// TODO: check crc
51+
}
5752

58-
// Try to locate a supplementary object file.
59-
if let Some((path_sup, build_id_sup)) = object.gnu_debugaltlink_path(&path) {
60-
if let Some(map_sup) = super::mmap(&path_sup) {
61-
if let Some(sup) = Object::parse(&map_sup) {
62-
if sup.build_id() == Some(build_id_sup) {
63-
let stash = Stash::new();
64-
let cx = Context::new(&stash, object, Some(sup))?;
65-
return Some(Mapping {
66-
// Convert to 'static lifetimes since the symbols should
67-
// only borrow `map`, `map_sup`, and `stash` and we're
68-
// preserving them below.
69-
cx: unsafe {
70-
core::mem::transmute::<Context<'_>, Context<'static>>(cx)
71-
},
72-
_map: map,
73-
_map_sup: Some(map_sup),
74-
_stash: stash,
75-
});
53+
// Try to locate a supplementary object file.
54+
if let Some((path_sup, build_id_sup)) = object.gnu_debugaltlink_path(&path) {
55+
if let Some(map_sup) = super::mmap(&path_sup) {
56+
let map_sup = stash.set_mmap_aux(map_sup);
57+
if let Some(sup) = Object::parse(map_sup) {
58+
if sup.build_id() == Some(build_id_sup) {
59+
return Context::new(stash, object, Some(sup));
60+
}
7661
}
7762
}
7863
}
79-
}
8064

81-
let stash = Stash::new();
82-
let cx = Context::new(&stash, object, None)?;
83-
Some(Mapping {
84-
// Convert to 'static lifetimes since the symbols should
85-
// only borrow `map` and `stash` and we're preserving them below.
86-
cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) },
87-
_map: map,
88-
_map_sup: None,
89-
_stash: stash,
65+
Context::new(stash, object, None)
9066
})
9167
}
9268
}

src/symbolize/gimli/stash.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
// only used on Linux right now, so allow dead code elsewhere
22
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
33

4+
use super::Mmap;
45
use alloc::vec;
56
use alloc::vec::Vec;
67
use core::cell::UnsafeCell;
78

89
/// A simple arena allocator for byte buffers.
910
pub struct Stash {
1011
buffers: UnsafeCell<Vec<Vec<u8>>>,
12+
mmap_aux: UnsafeCell<Option<Mmap>>,
1113
}
1214

1315
impl Stash {
1416
pub fn new() -> Stash {
1517
Stash {
1618
buffers: UnsafeCell::new(Vec::new()),
19+
mmap_aux: UnsafeCell::new(None),
1720
}
1821
}
1922

@@ -29,4 +32,21 @@ impl Stash {
2932
// to the data inside any buffer will live as long as `self` does.
3033
&mut buffers[i]
3134
}
35+
36+
/// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer
37+
/// which is scoped to just this lifetime.
38+
pub fn set_mmap_aux(&self, map: Mmap) -> &[u8] {
39+
// SAFETY: this is the only location for a mutable pointer to
40+
// `mmap_aux`, and this structure isn't threadsafe to shared across
41+
// threads either. This also is careful to store at most one `mmap_aux`
42+
// since overwriting a previous one would invalidate the previous
43+
// pointer. Given that though we can safely return a pointer to our
44+
// interior-owned contents.
45+
unsafe {
46+
let mmap_aux = &mut *self.mmap_aux.get();
47+
assert!(mmap_aux.is_none());
48+
*mmap_aux = Some(map);
49+
mmap_aux.as_ref().unwrap()
50+
}
51+
}
3252
}

0 commit comments

Comments
 (0)