Skip to content

Decouple instructions into a separate feature flag. #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 23, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ array-init = { version = "0.1.1", optional = true }
cc = { version = "1.0.37", optional = true }

[features]
default = [ "nightly" ]
default = [ "nightly", "instructions" ]
instructions = []
deny-warnings = []
stable = [ "cc", "array-init" ]
external_asm = [ "cc", "array-init" ]
nightly = [ "inline_asm", "const_fn", "abi_x86_interrupt" ]
inline_asm = []
abi_x86_interrupt = []
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Support for x86_64 specific instructions (e.g. TLB flush), registers (e.g. contr
## Crate Feature Flags

* `nightly`: This is the default.
* `stable`: Use this to build with non-nightly rust. Needs `default-features = false`.
* `instructions`: Enabled by default, turns on x86\_64 specific instructions, and dependent features. Only available for x86\_64 targets.
* `external_asm`: Use this to build with non-nightly rust. Needs `default-features = false, features = ["instructions"]`. Is unsupported on Windows.

## Building with stable rust

Expand Down
78 changes: 45 additions & 33 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,52 @@
#[cfg(feature = "inline_asm")]
fn main() {}

#[cfg(all(not(feature = "inline_asm"), not(feature = "stable")))]
fn main() {
compile_error!("Neither feature \"stable\" nor \"nightly\" was set!");
}
println!("cargo:rerun-if-changed=build.rs");

#[cfg(all(not(feature = "inline_asm"), feature = "stable"))]
fn main() {
use std::ffi::OsString;
use std::fs;
#[cfg(all(feature = "external_asm", windows))]
compile_error!("\"external_asm\" feature is not available on windows!");

println!("cargo:rerun-if-changed=build.rs");
#[cfg(all(feature = "instructions", not(target_arch = "x86_64")))]
compile_error!("\"instructions\" feature is only available for x86_64 targets!");

let entries = fs::read_dir("src/asm")
.unwrap()
.filter_map(|f| {
f.ok().and_then(|e| {
let path = e.path();
match path.extension() {
Some(ext) if ext.eq(&OsString::from("s")) => Some(path),
_ => None,
}
#[cfg(all(
feature = "instructions",
not(feature = "inline_asm"),
not(feature = "external_asm")
))]
compile_error!("\"instructions\" feature is enabled, but neither feature \"external_asm\" nor \"inline_asm\" was set!");

#[cfg(all(feature = "inline_asm", feature = "external_asm"))]
compile_error!(
"\"inline_asm\" and \"external_asm\" features can not be enabled at the same time!"
);

#[cfg(all(feature = "instructions", feature = "external_asm"))]
{
use std::ffi::OsString;
use std::fs;

let entries = fs::read_dir("src/asm")
.unwrap()
.filter_map(|f| {
f.ok().and_then(|e| {
let path = e.path();
match path.extension() {
Some(ext) if ext.eq(&OsString::from("s")) => Some(path),
_ => None,
}
})
})
})
.collect::<Vec<_>>();

cc::Build::new()
.no_default_flags(true)
.files(&entries)
.pic(true)
.static_flag(true)
.shared_flag(false)
.compile("x86_64_asm");

for e in entries {
println!("cargo:rerun-if-changed={}", e.to_str().unwrap());
.collect::<Vec<_>>();

cc::Build::new()
.no_default_flags(true)
.files(&entries)
.pic(true)
.static_flag(true)
.shared_flag(false)
.compile("x86_64_asm");

for e in entries {
println!("cargo:rerun-if-changed={}", e.to_str().unwrap());
}
}
}
116 changes: 115 additions & 1 deletion src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg(target_arch = "x86_64")]
#![cfg(feature = "instructions")]

//! Special x86_64 instructions.

Expand Down Expand Up @@ -51,3 +51,117 @@ pub fn bochs_breakpoint() {
llvm_asm!("xchgw %bx, %bx" :::: "volatile");
}
}

/// Gets the current instruction pointer. Note that this is only approximate as it requires a few
/// instructions to execute.
#[cfg(feature = "inline_asm")]
#[inline(always)]
pub fn read_rip() -> u64 {
let rip: u64;
unsafe {
llvm_asm!(
"lea (%rip), $0"
: "=r"(rip) ::: "volatile"
);
}
rip
}

/// Writes the FS segment base address
///
/// ## Safety
///
/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`.
///
/// The caller must ensure that this write operation has no unsafe side
/// effects, as the FS segment base address is often used for thread
/// local storage.
#[inline]
pub unsafe fn wrfsbase(val: u64) {
#[cfg(feature = "inline_asm")]
#[inline(always)]
unsafe fn inner(val: u64) {
llvm_asm!("wrfsbase $0" :: "r"(val) :: "volatile")
}

#[cfg(not(feature = "inline_asm"))]
#[inline(always)]
unsafe fn inner(val: u64) {
crate::asm::x86_64_asm_wrfsbase(val)
}

inner(val)
}

/// Reads the FS segment base address
///
/// ## Safety
///
/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`.
#[inline]
pub unsafe fn rdfsbase() -> u64 {
#[cfg(feature = "inline_asm")]
#[inline(always)]
unsafe fn inner() -> u64 {
let val: u64;
llvm_asm!("rdfsbase $0" : "=r" (val) ::: "volatile");
val
}

#[cfg(not(feature = "inline_asm"))]
#[inline(always)]
unsafe fn inner() -> u64 {
crate::asm::x86_64_asm_rdfsbase()
}

inner()
}

/// Writes the GS segment base address
///
/// ## Safety
///
/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`.
///
/// The caller must ensure that this write operation has no unsafe side
/// effects, as the GS segment base address might be in use.
#[inline]
pub unsafe fn wrgsbase(val: u64) {
#[cfg(feature = "inline_asm")]
#[inline(always)]
unsafe fn inner(val: u64) {
llvm_asm!("wrgsbase $0" :: "r"(val) :: "volatile")
}

#[cfg(not(feature = "inline_asm"))]
#[inline(always)]
unsafe fn inner(val: u64) {
crate::asm::x86_64_asm_wrgsbase(val)
}

inner(val)
}

/// Reads the GS segment base address
///
/// ## Safety
///
/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`.
#[inline]
pub unsafe fn rdgsbase() -> u64 {
#[cfg(feature = "inline_asm")]
#[inline(always)]
unsafe fn inner() -> u64 {
let val: u64;
llvm_asm!("rdgsbase $0" : "=r" (val) ::: "volatile");
val
}

#[cfg(not(feature = "inline_asm"))]
#[inline(always)]
unsafe fn inner() -> u64 {
crate::asm::x86_64_asm_rdgsbase()
}

inner()
}
3 changes: 1 addition & 2 deletions src/instructions/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
/// Used to obtain random numbers using x86_64's RDRAND opcode
pub struct RdRand(());

#[cfg(target_arch = "x86_64")]
impl RdRand {
/// Creates Some(RdRand) if RDRAND is supported, None otherwise
#[inline]
Expand Down Expand Up @@ -66,7 +65,7 @@ impl RdRand {
}
}

#[cfg(all(test, target_arch = "x86_64"))]
#[cfg(all(test))]
mod tests {
use super::*;

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ macro_rules! const_fn {
}
}

#[cfg(not(feature = "inline_asm"))]
#[cfg(all(feature = "instructions", not(feature = "inline_asm")))]
pub(crate) mod asm;

pub mod addr;
Expand Down
2 changes: 1 addition & 1 deletion src/registers/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ bitflags! {
}
}

#[cfg(target_arch = "x86_64")]
#[cfg(feature = "instructions")]
mod x86_64 {
use super::*;
use crate::structures::paging::PhysFrame;
Expand Down
116 changes: 4 additions & 112 deletions src/registers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,116 +4,8 @@ pub mod control;
pub mod model_specific;
pub mod rflags;

/// Gets the current instruction pointer. Note that this is only approximate as it requires a few
/// instructions to execute.
#[cfg(feature = "inline_asm")]
#[inline(always)]
pub fn read_rip() -> u64 {
let rip: u64;
unsafe {
llvm_asm!(
"lea (%rip), $0"
: "=r"(rip) ::: "volatile"
);
}
rip
}
#[cfg(feature = "instructions")]
pub use crate::instructions::{rdfsbase, rdgsbase, wrfsbase, wrgsbase};

/// Writes the FS segment base address
///
/// ## Safety
///
/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`.
///
/// The caller must ensure that this write operation has no unsafe side
/// effects, as the FS segment base address is often used for thread
/// local storage.
#[inline]
pub unsafe fn wrfsbase(val: u64) {
#[cfg(feature = "inline_asm")]
#[inline(always)]
unsafe fn inner(val: u64) {
llvm_asm!("wrfsbase $0" :: "r"(val) :: "volatile")
}

#[cfg(not(feature = "inline_asm"))]
#[inline(always)]
unsafe fn inner(val: u64) {
crate::asm::x86_64_asm_wrfsbase(val)
}

inner(val)
}

/// Reads the FS segment base address
///
/// ## Safety
///
/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`.
#[inline]
pub unsafe fn rdfsbase() -> u64 {
#[cfg(feature = "inline_asm")]
#[inline(always)]
unsafe fn inner() -> u64 {
let val: u64;
llvm_asm!("rdfsbase $0" : "=r" (val) ::: "volatile");
val
}

#[cfg(not(feature = "inline_asm"))]
#[inline(always)]
unsafe fn inner() -> u64 {
crate::asm::x86_64_asm_rdfsbase()
}

inner()
}

/// Writes the GS segment base address
///
/// ## Safety
///
/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`.
///
/// The caller must ensure that this write operation has no unsafe side
/// effects, as the GS segment base address might be in use.
#[inline]
pub unsafe fn wrgsbase(val: u64) {
#[cfg(feature = "inline_asm")]
#[inline(always)]
unsafe fn inner(val: u64) {
llvm_asm!("wrgsbase $0" :: "r"(val) :: "volatile")
}

#[cfg(not(feature = "inline_asm"))]
#[inline(always)]
unsafe fn inner(val: u64) {
crate::asm::x86_64_asm_wrgsbase(val)
}

inner(val)
}

/// Reads the GS segment base address
///
/// ## Safety
///
/// If `CR4.FSGSBASE` is not set, this instruction will throw an `#UD`.
#[inline]
pub unsafe fn rdgsbase() -> u64 {
#[cfg(feature = "inline_asm")]
#[inline(always)]
unsafe fn inner() -> u64 {
let val: u64;
llvm_asm!("rdgsbase $0" : "=r" (val) ::: "volatile");
val
}

#[cfg(not(feature = "inline_asm"))]
#[inline(always)]
unsafe fn inner() -> u64 {
crate::asm::x86_64_asm_rdgsbase()
}

inner()
}
#[cfg(all(feature = "instructions", feature = "inline_asm"))]
pub use crate::instructions::read_rip;
Loading