use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use std::sync::OnceLock;
pub(crate) fn tag() -> u64 {
static TAG: OnceLock<u64> = OnceLock::new();
*TAG.get_or_init(compute)
}
fn compute() -> u64 {
match self_path().and_then(|p| std::fs::read(p).ok()) {
Some(bytes) => {
let mut h = DefaultHasher::new();
h.write(&bytes);
h.finish()
}
None => fallback(),
}
}
#[cfg(unix)]
fn self_path() -> Option<std::path::PathBuf> {
use std::ffi::{CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
let mut info: libc::Dl_info = unsafe { std::mem::zeroed() };
let addr = compute as *const std::ffi::c_void;
if unsafe { libc::dladdr(addr, &mut info) } == 0 || info.dli_fname.is_null() {
return None;
}
let bytes = unsafe { CStr::from_ptr(info.dli_fname) }.to_bytes();
Some(std::path::PathBuf::from(OsStr::from_bytes(bytes)))
}
#[cfg(not(unix))]
fn self_path() -> Option<std::path::PathBuf> {
None
}
fn fallback() -> u64 {
static SENTINEL: u8 = 0;
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
let aslr = &SENTINEL as *const u8 as u64;
let mut h = DefaultHasher::new();
h.write_u64(nanos);
h.write_u64(aslr);
h.finish()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tag_is_cached() {
assert_eq!(tag(), tag());
}
#[test]
fn computes_from_the_real_binary() {
assert_eq!(compute(), compute());
}
}