Skip to content

Commit 0555ada

Browse files
authored
Merge pull request #4397 from tgross35/gettid-shims
Add shims for `gettid`-esque functions
2 parents 5705610 + b31dd9c commit 0555ada

File tree

14 files changed

+312
-12
lines changed

14 files changed

+312
-12
lines changed

src/helpers.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,6 @@ where
13371337

13381338
/// Check that the number of varargs is at least the minimum what we expect.
13391339
/// Fixed args should not be included.
1340-
/// Use `check_vararg_fixed_arg_count` to extract the varargs slice from full function arguments.
13411340
pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
13421341
name: &'a str,
13431342
args: &'a [OpTy<'tcx>],

src/shims/env.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
110110
}
111111
}
112112

113+
/// Get the process identifier.
113114
fn get_pid(&self) -> u32 {
114115
let this = self.eval_context_ref();
115116
if this.machine.communicate() { std::process::id() } else { 1000 }
116117
}
118+
119+
/// Get an "OS" thread ID for the current thread.
120+
fn get_current_tid(&self) -> u32 {
121+
let this = self.eval_context_ref();
122+
self.get_tid(this.machine.threads.active_thread())
123+
}
124+
125+
/// Get an "OS" thread ID for any thread.
126+
fn get_tid(&self, thread: ThreadId) -> u32 {
127+
let this = self.eval_context_ref();
128+
let index = thread.to_u32();
129+
let target_os = &this.tcx.sess.target.os;
130+
if target_os == "linux" || target_os == "netbsd" {
131+
// On Linux, the main thread has PID == TID so we uphold this. NetBSD also appears
132+
// to exhibit the same behavior, though I can't find a citation.
133+
this.get_pid().strict_add(index)
134+
} else {
135+
// Other platforms do not display any relationship between PID and TID.
136+
index
137+
}
138+
}
117139
}

src/shims/extern_static.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl<'tcx> MiriMachine<'tcx> {
6666
ecx,
6767
&["__cxa_thread_atexit_impl", "__clock_gettime64"],
6868
)?;
69-
Self::weak_symbol_extern_statics(ecx, &["getrandom", "statx"])?;
69+
Self::weak_symbol_extern_statics(ecx, &["getrandom", "gettid", "statx"])?;
7070
}
7171
"freebsd" => {
7272
Self::null_ptr_extern_statics(ecx, &["__cxa_thread_atexit_impl"])?;

src/shims/unix/env.rs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,52 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
274274
interp_ok(Scalar::from_u32(this.get_pid()))
275275
}
276276

277-
fn linux_gettid(&mut self) -> InterpResult<'tcx, Scalar> {
277+
/// The `gettid`-like function for Unix platforms that take no parameters and return a 32-bit
278+
/// integer. It is not always named "gettid".
279+
fn unix_gettid(&mut self, link_name: &str) -> InterpResult<'tcx, Scalar> {
278280
let this = self.eval_context_ref();
279-
this.assert_target_os("linux", "gettid");
281+
this.assert_target_os_is_unix(link_name);
280282

281-
let index = this.machine.threads.active_thread().to_u32();
283+
// For most platforms the return type is an `i32`, but some are unsigned. The TID
284+
// will always be positive so we don't need to differentiate.
285+
interp_ok(Scalar::from_u32(this.get_current_tid()))
286+
}
287+
288+
/// The Apple-specific `int pthread_threadid_np(pthread_t thread, uint64_t *thread_id)`, which
289+
/// allows querying the ID for arbitrary threads, identified by their pthread_t.
290+
///
291+
/// API documentation: <https://www.manpagez.com/man/3/pthread_threadid_np/>.
292+
fn apple_pthread_threadip_np(
293+
&mut self,
294+
thread_op: &OpTy<'tcx>,
295+
tid_op: &OpTy<'tcx>,
296+
) -> InterpResult<'tcx, Scalar> {
297+
let this = self.eval_context_mut();
298+
this.assert_target_os("macos", "pthread_threadip_np");
299+
300+
let tid_dest = this.read_pointer(tid_op)?;
301+
if this.ptr_is_null(tid_dest)? {
302+
// If NULL is passed, an error is immediately returned
303+
return interp_ok(this.eval_libc("EINVAL"));
304+
}
305+
306+
let thread = this.read_scalar(thread_op)?.to_int(this.libc_ty_layout("pthread_t").size)?;
307+
let thread = if thread == 0 {
308+
// Null thread ID indicates that we are querying the active thread.
309+
this.machine.threads.active_thread()
310+
} else {
311+
// Our pthread_t is just the raw ThreadId.
312+
let Ok(thread) = this.thread_id_try_from(thread) else {
313+
return interp_ok(this.eval_libc("ESRCH"));
314+
};
315+
thread
316+
};
282317

283-
// Compute a TID for this thread, ensuring that the main thread has PID == TID.
284-
let tid = this.get_pid().strict_add(index);
318+
let tid = this.get_tid(thread);
319+
let tid_dest = this.deref_pointer_as(tid_op, this.machine.layouts.u64)?;
320+
this.write_int(tid, &tid_dest)?;
285321

286-
interp_ok(Scalar::from_u32(tid))
322+
// Possible errors have been handled, return success.
323+
interp_ok(Scalar::from_u32(0))
287324
}
288325
}

src/shims/unix/freebsd/foreign_items.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5656
};
5757
this.write_scalar(res, dest)?;
5858
}
59+
"pthread_getthreadid_np" => {
60+
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
61+
let result = this.unix_gettid(link_name.as_str())?;
62+
this.write_scalar(result, dest)?;
63+
}
5964

6065
"cpuset_getaffinity" => {
6166
// The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically.

src/shims/unix/linux/foreign_items.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::*;
1818
const TASK_COMM_LEN: u64 = 16;
1919

2020
pub fn is_dyn_sym(name: &str) -> bool {
21-
matches!(name, "statx")
21+
matches!(name, "gettid" | "statx")
2222
}
2323

2424
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -117,7 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
117117
}
118118
"gettid" => {
119119
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
120-
let result = this.linux_gettid()?;
120+
let result = this.unix_gettid(link_name.as_str())?;
121121
this.write_scalar(result, dest)?;
122122
}
123123

src/shims/unix/linux_like/syscall.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_span::Symbol;
44
use rustc_target::callconv::FnAbi;
55

66
use crate::helpers::check_min_vararg_count;
7+
use crate::shims::unix::env::EvalContextExt;
78
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
89
use crate::shims::unix::linux_like::sync::futex;
910
use crate::*;
@@ -24,6 +25,7 @@ pub fn syscall<'tcx>(
2425
let sys_getrandom = ecx.eval_libc("SYS_getrandom").to_target_usize(ecx)?;
2526
let sys_futex = ecx.eval_libc("SYS_futex").to_target_usize(ecx)?;
2627
let sys_eventfd2 = ecx.eval_libc("SYS_eventfd2").to_target_usize(ecx)?;
28+
let sys_gettid = ecx.eval_libc("SYS_gettid").to_target_usize(ecx)?;
2729

2830
match ecx.read_target_usize(op)? {
2931
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
@@ -53,6 +55,10 @@ pub fn syscall<'tcx>(
5355
let result = ecx.eventfd(initval, flags)?;
5456
ecx.write_int(result.to_i32()?, dest)?;
5557
}
58+
num if num == sys_gettid => {
59+
let result = ecx.unix_gettid("SYS_gettid")?;
60+
ecx.write_int(result.to_u32()?, dest)?;
61+
}
5662
num => {
5763
throw_unsup_format!("syscall: unsupported syscall number {num}");
5864
}

src/shims/unix/macos/foreign_items.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
222222
};
223223
this.write_scalar(res, dest)?;
224224
}
225+
"pthread_threadid_np" => {
226+
let [thread, tid_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
227+
let res = this.apple_pthread_threadip_np(thread, tid_ptr)?;
228+
this.write_scalar(res, dest)?;
229+
}
225230

226231
// Synchronization primitives
227232
"os_sync_wait_on_address" => {

src/shims/windows/foreign_items.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
629629
this.write_scalar(name, &name_ptr)?;
630630
this.write_scalar(res, dest)?;
631631
}
632+
"GetThreadId" => {
633+
let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;
634+
let handle = this.read_handle(handle, "GetThreadId")?;
635+
let thread = match handle {
636+
Handle::Thread(thread) => thread,
637+
Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
638+
_ => this.invalid_handle("GetThreadDescription")?,
639+
};
640+
let tid = this.get_tid(thread);
641+
this.write_scalar(Scalar::from_u32(tid), dest)?;
642+
}
643+
"GetCurrentThreadId" => {
644+
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
645+
let thread = this.active_thread();
646+
let tid = this.get_tid(thread);
647+
this.write_scalar(Scalar::from_u32(tid), dest)?;
648+
}
632649

633650
// Miscellaneous
634651
"ExitProcess" => {

src/shims/windows/handle.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ impl Handle {
166166
/// Structurally invalid handles return [`HandleError::InvalidHandle`].
167167
/// If the handle is structurally valid but semantically invalid, e.g. a for non-existent thread
168168
/// ID, returns [`HandleError::ThreadNotFound`].
169+
///
170+
/// This function is deliberately private; shims should always use `read_handle`.
171+
/// That enforces handle validity even when Windows does not: for now, we argue invalid
172+
/// handles are always a bug and programmers likely want to know about them.
169173
fn try_from_scalar<'tcx>(
170174
handle: Scalar,
171175
cx: &MiriInterpCx<'tcx>,

0 commit comments

Comments
 (0)