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
//! Obtain a [Uuid] uniquely representing the build of the current binary. //! //! This is intended to be used to check that different processes are indeed //! invocations of identically laid out binaries. //! //! As such: //! * It is guaranteed to be identical within multiple invocations of the same //! binary. //! * It is guaranteed to be different across binaries with different code or //! data segments or layout. //! * Equality is unspecified if the binaries have identical code and data //! segments and layout but differ immaterially (e.g. if a timestamp is included //! in the binary at compile time). //! //! # Examples //! //! ``` //! # let remote_build_id = build_id::get(); //! let local_build_id = build_id::get(); //! if local_build_id == remote_build_id { //! println!("We're running the same binary as remote!"); //! } else { //! println!("We're running a different binary to remote"); //! } //! ``` //! //! # Note //! //! This looks first for linker-inserted build ID / binary UUIDs (i.e. //! `.note.gnu.build-id` on Linux; `LC_UUID` in Mach-O; etc), falling back to //! hashing the whole binary. #![doc(html_root_url = "https://docs.rs/build_id/0.1.0")] #![deny(missing_docs, warnings, deprecated)] #![allow(intra_doc_link_resolution_failure)] extern crate byteorder; extern crate twox_hash; extern crate uuid; use std::{env, fs, hash::Hasher, io, sync}; use uuid::Uuid; static mut BUILD_ID: Uuid = Uuid::nil(); static INIT: sync::Once = sync::ONCE_INIT; /// Returns a [Uuid] uniquely representing the build of the current binary. /// /// This is intended to be used to check that different processes are indeed /// invocations of identically laid out binaries. /// /// As such: /// * It is guaranteed to be identical within multiple invocations of the same /// binary. /// * It is guaranteed to be different across binaries with different code or /// data segments or layout. /// * Equality is unspecified if the binaries have identical code and data /// segments and layout but differ immaterially (e.g. if a timestamp is included /// in the binary at compile time). /// /// # Examples /// /// ``` /// # let remote_build_id = build_id::get(); /// let local_build_id = build_id::get(); /// if local_build_id == remote_build_id { /// println!("We're running the same binary as remote!"); /// } else { /// println!("We're running a different binary to remote"); /// } /// ``` /// /// # Note /// /// This looks first for linker-inserted build ID / binary UUIDs (i.e. /// `.note.gnu.build-id` on Linux; `LC_UUID` in Mach-O; etc), falling back to /// hashing the whole binary. #[inline] pub fn get() -> Uuid { unsafe { INIT.call_once(|| { BUILD_ID = calculate(); }); BUILD_ID } } fn calculate() -> Uuid { // LC_UUID https://opensource.apple.com/source/libsecurity_codesigning/libsecurity_codesigning-55037.6/lib/machorep.cpp https://stackoverflow.com/questions/10119700/how-to-get-mach-o-uuid-of-a-running-process // .note.gnu.build-id https://github.com/golang/go/issues/21564 https://github.com/golang/go/blob/178307c3a72a9da3d731fecf354630761d6b246c/src/cmd/go/internal/buildid/buildid.go let file = fs::File::open(env::current_exe().unwrap()).unwrap(); let mut hasher = twox_hash::XxHash::with_seed(0); io::copy(&mut &file, &mut HashWriter(&mut hasher)).unwrap(); let mut bytes = [0; 16]; <byteorder::LittleEndian as byteorder::ByteOrder>::write_u64(&mut bytes, hasher.finish()); Uuid::from_random_bytes(bytes) } struct HashWriter<T: Hasher>(T); impl<T: Hasher> io::Write for HashWriter<T> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf); Ok(buf.len()) } fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.write(buf).map(|_| ()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } #[cfg(test)] mod test { #[test] fn brute() { let x = super::calculate(); for _ in 0..1000 { assert_eq!(x, super::calculate()); } } #[test] fn get() { let x = super::calculate(); assert_eq!(x, super::get()); assert_eq!(x, super::get()); assert_eq!(x, super::get()); } }