Merge pull request #458 from RalfJung/tls
Move TLS data to machine data
This commit is contained in:
commit
c95923d602
4 changed files with 94 additions and 116 deletions
|
|
@ -8,10 +8,6 @@ use std::mem;
|
|||
|
||||
use super::*;
|
||||
|
||||
use tls::MemoryExt;
|
||||
|
||||
use super::memory::MemoryKind;
|
||||
|
||||
pub trait EvalContextExt<'tcx, 'mir> {
|
||||
/// Emulate calling a foreign item, fail if the item is not supported.
|
||||
/// This function will handle `goto_block` if needed.
|
||||
|
|
@ -129,7 +125,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
self.write_null(dest)?;
|
||||
} else {
|
||||
let align = self.tcx.data_layout.pointer_align;
|
||||
let ptr = self.memory.allocate(Size::from_bytes(size), align, MemoryKind::C.into())?;
|
||||
let ptr = self.memory.allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into())?;
|
||||
self.write_scalar(Scalar::Ptr(ptr), dest)?;
|
||||
}
|
||||
}
|
||||
|
|
@ -140,7 +136,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
self.memory.deallocate(
|
||||
ptr.to_ptr()?,
|
||||
None,
|
||||
MemoryKind::C.into(),
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +152,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
}
|
||||
let ptr = self.memory.allocate(Size::from_bytes(size),
|
||||
Align::from_bytes(align, align).unwrap(),
|
||||
MemoryKind::Rust.into())?;
|
||||
MiriMemoryKind::Rust.into())?;
|
||||
self.write_scalar(Scalar::Ptr(ptr), dest)?;
|
||||
}
|
||||
"__rust_alloc_zeroed" => {
|
||||
|
|
@ -170,7 +166,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
}
|
||||
let ptr = self.memory.allocate(Size::from_bytes(size),
|
||||
Align::from_bytes(align, align).unwrap(),
|
||||
MemoryKind::Rust.into())?;
|
||||
MiriMemoryKind::Rust.into())?;
|
||||
self.memory.write_repeat(ptr.into(), 0, Size::from_bytes(size))?;
|
||||
self.write_scalar(Scalar::Ptr(ptr), dest)?;
|
||||
}
|
||||
|
|
@ -187,7 +183,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
self.memory.deallocate(
|
||||
ptr,
|
||||
Some((Size::from_bytes(old_size), Align::from_bytes(align, align).unwrap())),
|
||||
MemoryKind::Rust.into(),
|
||||
MiriMemoryKind::Rust.into(),
|
||||
)?;
|
||||
}
|
||||
"__rust_realloc" => {
|
||||
|
|
@ -207,7 +203,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
Align::from_bytes(align, align).unwrap(),
|
||||
Size::from_bytes(new_size),
|
||||
Align::from_bytes(align, align).unwrap(),
|
||||
MemoryKind::Rust.into(),
|
||||
MiriMemoryKind::Rust.into(),
|
||||
)?;
|
||||
self.write_scalar(Scalar::Ptr(new_ptr), dest)?;
|
||||
}
|
||||
|
|
@ -365,7 +361,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
}
|
||||
if let Some(old) = success {
|
||||
if let Some(var) = old {
|
||||
self.memory.deallocate(var, None, MemoryKind::Env.into())?;
|
||||
self.memory.deallocate(var, None, MiriMemoryKind::Env.into())?;
|
||||
}
|
||||
self.write_null(dest)?;
|
||||
} else {
|
||||
|
|
@ -391,7 +387,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
let value_copy = self.memory.allocate(
|
||||
Size::from_bytes((value.len() + 1) as u64),
|
||||
Align::from_bytes(1, 1).unwrap(),
|
||||
MemoryKind::Env.into(),
|
||||
MiriMemoryKind::Env.into(),
|
||||
)?;
|
||||
self.memory.write_bytes(value_copy.into(), &value)?;
|
||||
let trailing_zero_ptr = value_copy.offset(Size::from_bytes(value.len() as u64), &self)?.into();
|
||||
|
|
@ -401,7 +397,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
value_copy,
|
||||
)
|
||||
{
|
||||
self.memory.deallocate(var, None, MemoryKind::Env.into())?;
|
||||
self.memory.deallocate(var, None, MiriMemoryKind::Env.into())?;
|
||||
}
|
||||
self.write_null(dest)?;
|
||||
} else {
|
||||
|
|
@ -504,7 +500,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
let key_layout = self.layout_of(key_type)?;
|
||||
|
||||
// Create key and write it into the memory where key_ptr wants it
|
||||
let key = self.memory.create_tls_key(dtor) as u128;
|
||||
let key = self.machine.tls.create_tls_key(dtor, *self.tcx) as u128;
|
||||
if key_layout.size.bits() < 128 && key >= (1u128 << key_layout.size.bits() as u128) {
|
||||
return err!(OutOfTls);
|
||||
}
|
||||
|
|
@ -520,19 +516,19 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
}
|
||||
"pthread_key_delete" => {
|
||||
let key = self.read_scalar(args[0])?.to_bytes()?;
|
||||
self.memory.delete_tls_key(key)?;
|
||||
self.machine.tls.delete_tls_key(key)?;
|
||||
// Return success (0)
|
||||
self.write_null(dest)?;
|
||||
}
|
||||
"pthread_getspecific" => {
|
||||
let key = self.read_scalar(args[0])?.to_bytes()?;
|
||||
let ptr = self.memory.load_tls(key)?;
|
||||
let ptr = self.machine.tls.load_tls(key)?;
|
||||
self.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"pthread_setspecific" => {
|
||||
let key = self.read_scalar(args[0])?.to_bytes()?;
|
||||
let new_ptr = self.read_scalar(args[1])?.not_undef()?;
|
||||
self.memory.store_tls(key, new_ptr)?;
|
||||
self.machine.tls.store_tls(key, new_ptr)?;
|
||||
|
||||
// Return success (0)
|
||||
self.write_null(dest)?;
|
||||
|
|
@ -607,7 +603,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
// This just creates a key; Windows does not natively support TLS dtors.
|
||||
|
||||
// Create key and return it
|
||||
let key = self.memory.create_tls_key(None) as u128;
|
||||
let key = self.machine.tls.create_tls_key(None, *self.tcx) as u128;
|
||||
|
||||
// Figure out how large a TLS key actually is. This is c::DWORD.
|
||||
if dest.layout.size.bits() < 128 && key >= (1u128 << dest.layout.size.bits() as u128) {
|
||||
|
|
@ -617,13 +613,13 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
|
|||
}
|
||||
"TlsGetValue" => {
|
||||
let key = self.read_scalar(args[0])?.to_bytes()?;
|
||||
let ptr = self.memory.load_tls(key)?;
|
||||
let ptr = self.machine.tls.load_tls(key)?;
|
||||
self.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"TlsSetValue" => {
|
||||
let key = self.read_scalar(args[0])?.to_bytes()?;
|
||||
let new_ptr = self.read_scalar(args[1])?.not_undef()?;
|
||||
self.memory.store_tls(key, new_ptr)?;
|
||||
self.machine.tls.store_tls(key, new_ptr)?;
|
||||
|
||||
// Return success (1)
|
||||
self.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
|
||||
|
|
|
|||
39
src/lib.rs
39
src/lib.rs
|
|
@ -21,17 +21,16 @@ use rustc::mir;
|
|||
use syntax::ast::Mutability;
|
||||
use syntax::attr;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub use rustc::mir::interpret::*;
|
||||
pub use rustc_mir::interpret::*;
|
||||
pub use rustc_mir::interpret;
|
||||
|
||||
mod fn_call;
|
||||
mod operator;
|
||||
mod intrinsic;
|
||||
mod helpers;
|
||||
mod memory;
|
||||
mod tls;
|
||||
mod locks;
|
||||
mod range_map;
|
||||
|
|
@ -39,9 +38,7 @@ mod range_map;
|
|||
use fn_call::EvalContextExt as MissingFnsEvalContextExt;
|
||||
use operator::EvalContextExt as OperatorEvalContextExt;
|
||||
use intrinsic::EvalContextExt as IntrinsicEvalContextExt;
|
||||
use tls::EvalContextExt as TlsEvalContextExt;
|
||||
use memory::{MemoryKind as MiriMemoryKind, TlsKey, TlsEntry, MemoryData};
|
||||
use locks::LockInfo;
|
||||
use tls::{EvalContextExt as TlsEvalContextExt, TlsData};
|
||||
use range_map::RangeMap;
|
||||
use helpers::FalibleScalarExt;
|
||||
|
||||
|
|
@ -54,7 +51,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
|
|||
tcx.at(syntax::source_map::DUMMY_SP),
|
||||
ty::ParamEnv::reveal_all(),
|
||||
Default::default(),
|
||||
MemoryData::new()
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
|
||||
|
|
@ -201,21 +198,41 @@ pub fn eval_main<'a, 'tcx: 'a>(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum MiriMemoryKind {
|
||||
/// `__rust_alloc` memory
|
||||
Rust,
|
||||
/// `malloc` memory
|
||||
C,
|
||||
/// Part of env var emulation
|
||||
Env,
|
||||
/// mutable statics
|
||||
MutStatic,
|
||||
}
|
||||
|
||||
impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
|
||||
fn into(self) -> MemoryKind<MiriMemoryKind> {
|
||||
MemoryKind::Machine(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Default, PartialEq, Eq)]
|
||||
pub struct Evaluator<'tcx> {
|
||||
/// Environment variables set by `setenv`
|
||||
/// Miri does not expose env vars from the host to the emulated program
|
||||
pub(crate) env_vars: HashMap<Vec<u8>, Pointer>,
|
||||
|
||||
/// Use the lifetime
|
||||
_dummy : PhantomData<&'tcx ()>,
|
||||
/// TLS state
|
||||
pub(crate) tls: TlsData<'tcx>,
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
|
||||
type MemoryData = memory::MemoryData<'tcx>;
|
||||
type MemoryKinds = memory::MemoryKind;
|
||||
type MemoryData = ();
|
||||
type MemoryKinds = MiriMemoryKind;
|
||||
|
||||
const MUT_STATIC_KIND: Option<memory::MemoryKind> = Some(memory::MemoryKind::MutStatic);
|
||||
const MUT_STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::MutStatic);
|
||||
const DETECT_LOOPS: bool = false;
|
||||
|
||||
/// Returns Ok() when the function was handled, fail otherwise
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
use std::collections::{HashMap, BTreeMap};
|
||||
|
||||
use rustc::ty;
|
||||
|
||||
use super::{AllocId, Scalar, LockInfo, RangeMap};
|
||||
|
||||
pub type TlsKey = u128;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct TlsEntry<'tcx> {
|
||||
pub(crate) data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread.
|
||||
pub(crate) dtor: Option<ty::Instance<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MemoryData<'tcx> {
|
||||
/// The Key to use for the next thread-local allocation.
|
||||
pub(crate) next_thread_local: TlsKey,
|
||||
|
||||
/// pthreads-style thread-local storage.
|
||||
pub(crate) thread_local: BTreeMap<TlsKey, TlsEntry<'tcx>>,
|
||||
|
||||
/// Memory regions that are locked by some function
|
||||
///
|
||||
/// Only mutable (static mut, heap, stack) allocations have an entry in this map.
|
||||
/// The entry is created when allocating the memory and deleted after deallocation.
|
||||
pub(crate) locks: HashMap<AllocId, RangeMap<LockInfo<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'tcx> MemoryData<'tcx> {
|
||||
pub(crate) fn new() -> Self {
|
||||
MemoryData {
|
||||
next_thread_local: 1, // start with 1 as we must not use 0 on Windows
|
||||
thread_local: BTreeMap::new(),
|
||||
locks: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum MemoryKind {
|
||||
/// `__rust_alloc` memory
|
||||
Rust,
|
||||
/// `malloc` memory
|
||||
C,
|
||||
/// Part of env var emulation
|
||||
Env,
|
||||
/// mutable statics
|
||||
MutStatic,
|
||||
}
|
||||
|
||||
impl Into<::rustc_mir::interpret::MemoryKind<MemoryKind>> for MemoryKind {
|
||||
fn into(self) -> ::rustc_mir::interpret::MemoryKind<MemoryKind> {
|
||||
::rustc_mir::interpret::MemoryKind::Machine(self)
|
||||
}
|
||||
}
|
||||
78
src/tls.rs
78
src/tls.rs
|
|
@ -1,31 +1,52 @@
|
|||
use rustc::{ty, mir};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::{TlsKey, TlsEntry, EvalResult, EvalErrorKind, Scalar, Memory, Evaluator,
|
||||
use rustc::{ty, ty::layout::HasDataLayout, mir};
|
||||
|
||||
use super::{EvalResult, EvalErrorKind, Scalar, Evaluator,
|
||||
Place, StackPopCleanup, EvalContext};
|
||||
|
||||
pub trait MemoryExt<'tcx> {
|
||||
fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey;
|
||||
fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx>;
|
||||
fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar>;
|
||||
fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx>;
|
||||
fn fetch_tls_dtor(
|
||||
&mut self,
|
||||
key: Option<TlsKey>,
|
||||
) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)>;
|
||||
pub type TlsKey = u128;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct TlsEntry<'tcx> {
|
||||
pub(crate) data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread.
|
||||
pub(crate) dtor: Option<ty::Instance<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TlsData<'tcx> {
|
||||
/// The Key to use for the next thread-local allocation.
|
||||
pub(crate) next_key: TlsKey,
|
||||
|
||||
/// pthreads-style thread-local storage.
|
||||
pub(crate) keys: BTreeMap<TlsKey, TlsEntry<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> Default for TlsData<'tcx> {
|
||||
fn default() -> Self {
|
||||
TlsData {
|
||||
next_key: 1, // start with 1 as we must not use 0 on Windows
|
||||
keys: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EvalContextExt<'tcx> {
|
||||
fn run_tls_dtors(&mut self) -> EvalResult<'tcx>;
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evaluator<'tcx>> {
|
||||
fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey {
|
||||
let new_key = self.data.next_thread_local;
|
||||
self.data.next_thread_local += 1;
|
||||
self.data.thread_local.insert(
|
||||
impl<'tcx> TlsData<'tcx> {
|
||||
pub fn create_tls_key(
|
||||
&mut self,
|
||||
dtor: Option<ty::Instance<'tcx>>,
|
||||
cx: impl HasDataLayout,
|
||||
) -> TlsKey {
|
||||
let new_key = self.next_key;
|
||||
self.next_key += 1;
|
||||
self.keys.insert(
|
||||
new_key,
|
||||
TlsEntry {
|
||||
data: Scalar::ptr_null(*self.tcx).into(),
|
||||
data: Scalar::ptr_null(cx).into(),
|
||||
dtor,
|
||||
},
|
||||
);
|
||||
|
|
@ -33,8 +54,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
|||
new_key
|
||||
}
|
||||
|
||||
fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> {
|
||||
match self.data.thread_local.remove(&key) {
|
||||
pub fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> {
|
||||
match self.keys.remove(&key) {
|
||||
Some(_) => {
|
||||
trace!("TLS key {} removed", key);
|
||||
Ok(())
|
||||
|
|
@ -43,8 +64,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
|||
}
|
||||
}
|
||||
|
||||
fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar> {
|
||||
match self.data.thread_local.get(&key) {
|
||||
pub fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar> {
|
||||
match self.keys.get(&key) {
|
||||
Some(&TlsEntry { data, .. }) => {
|
||||
trace!("TLS key {} loaded: {:?}", key, data);
|
||||
Ok(data)
|
||||
|
|
@ -53,8 +74,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
|||
}
|
||||
}
|
||||
|
||||
fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx> {
|
||||
match self.data.thread_local.get_mut(&key) {
|
||||
pub fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx> {
|
||||
match self.keys.get_mut(&key) {
|
||||
Some(&mut TlsEntry { ref mut data, .. }) => {
|
||||
trace!("TLS key {} stored: {:?}", key, new_data);
|
||||
*data = new_data;
|
||||
|
|
@ -85,10 +106,11 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
|||
fn fetch_tls_dtor(
|
||||
&mut self,
|
||||
key: Option<TlsKey>,
|
||||
cx: impl HasDataLayout,
|
||||
) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> {
|
||||
use std::collections::Bound::*;
|
||||
|
||||
let thread_local = &mut self.data.thread_local;
|
||||
let thread_local = &mut self.keys;
|
||||
let start = match key {
|
||||
Some(key) => Excluded(key),
|
||||
None => Unbounded,
|
||||
|
|
@ -99,7 +121,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
|||
if !data.is_null() {
|
||||
if let Some(dtor) = dtor {
|
||||
let ret = Some((dtor, *data, key));
|
||||
*data = Scalar::ptr_null(*self.tcx);
|
||||
*data = Scalar::ptr_null(cx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -110,7 +132,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
|
|||
|
||||
impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>> {
|
||||
fn run_tls_dtors(&mut self) -> EvalResult<'tcx> {
|
||||
let mut dtor = self.memory.fetch_tls_dtor(None);
|
||||
let mut dtor = self.machine.tls.fetch_tls_dtor(None, *self.tcx);
|
||||
// FIXME: replace loop by some structure that works with stepping
|
||||
while let Some((instance, ptr, key)) = dtor {
|
||||
trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
|
||||
|
|
@ -134,9 +156,9 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx> for EvalContext<'a, 'mir, '
|
|||
// step until out of stackframes
|
||||
self.run()?;
|
||||
|
||||
dtor = match self.memory.fetch_tls_dtor(Some(key)) {
|
||||
dtor = match self.machine.tls.fetch_tls_dtor(Some(key), *self.tcx) {
|
||||
dtor @ Some(_) => dtor,
|
||||
None => self.memory.fetch_tls_dtor(None),
|
||||
None => self.machine.tls.fetch_tls_dtor(None, *self.tcx),
|
||||
};
|
||||
}
|
||||
// FIXME: On a windows target, call `unsafe extern "system" fn on_tls_callback`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue