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
15 changes: 9 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,26 @@ jobs:
- name: "Run cargo build"
run: cargo build

- name: "Run cargo build for stable without instructions"
run: cargo build --no-default-features

- name: "Run cargo build for stable"
run: cargo build --no-default-features --features stable
run: cargo build --no-default-features --features external_asm,instructions
if: runner.os != 'Windows'

- name: "Run cargo build for stable on musl"
run: cargo build --target x86_64-unknown-linux-musl --no-default-features --features stable
run: cargo build --target x86_64-unknown-linux-musl --no-default-features --features external_asm,instructions
if: runner.os == 'Linux'

- name: "Run cargo test"
run: cargo test

- name: "Run cargo test for stable"
run: cargo test --no-default-features --features stable
run: cargo test --no-default-features --features external_asm,instructions
if: runner.os != 'Windows'

- name: "Run cargo test for stable on musl"
run: cargo test --target x86_64-unknown-linux-musl --no-default-features --features stable
run: cargo test --target x86_64-unknown-linux-musl --no-default-features --features external_asm,instructions
if: runner.os == 'Linux'

- name: 'Deny Warnings'
Expand All @@ -90,8 +93,8 @@ jobs:
rustup target add thumbv7em-none-eabihf
- name: 'Build on non x86_64 platforms'
run: |
cargo build --target i686-unknown-linux-gnu
cargo build --target thumbv7em-none-eabihf
cargo build --target i686-unknown-linux-gnu --no-default-features --features nightly
cargo build --target thumbv7em-none-eabihf --no-default-features --features nightly

- name: "Install Rustup Components"
run: rustup component add rust-src llvm-tools-preview
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ edition = "2018"
[dependencies]
bit_field = "0.9.0"
bitflags = "1.0.4"
array-init = { version = "0.1.1", optional = true }

[build-dependencies]
cc = { version = "1.0.37", optional = true }

[features]
default = [ "nightly" ]
default = [ "nightly", "instructions" ]
instructions = []
deny-warnings = []
stable = [ "cc", "array-init" ]
external_asm = [ "cc" ]
nightly = [ "inline_asm", "const_fn", "abi_x86_interrupt" ]
inline_asm = []
abi_x86_interrupt = []
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ 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`.
* `nightly`: Enables features only available on nightly Rust; enabled by default.
* `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
80 changes: 47 additions & 33 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,54 @@
#[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 toolchain!");

println!("cargo:rerun-if-changed=build.rs");
#[cfg(feature = "instructions")]
if std::env::var("CARGO_CFG_TARGET_ARCH").unwrap() != "x86_64" {
panic!("\"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());
}
}
}
17 changes: 16 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,18 @@ 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
}
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
99 changes: 99 additions & 0 deletions src/instructions/segmentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,102 @@ pub fn cs() -> SegmentSelector {
SegmentSelector(segment)
}
}

/// 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()
}
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", feature = "external_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
Loading