Skip to content
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

enable enc2 #65

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
8 changes: 8 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.toolchain == 'nightly' }}
strategy:
fail-fast: false
matrix:
toolchain: [stable, beta, nightly]
os:
Expand All @@ -30,4 +31,11 @@ jobs:
- uses: Swatinem/rust-cache@v2

- run: cargo test
- run: cargo test --features enc2-m32-a32
- run: cargo test --features enc2-m64-a64
- run: cargo test --features dylib

- run: cargo test --all-features
# Enabling enc2 features and dylib currently results in link errors
# on windows.
if: matrix.os != 'windows-latest'
19 changes: 19 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,31 @@ categories = ["encoding", "external-ffi-bindings", "hardware-support", "parsing"
rust-version = "1.64"

[features]
# Enable the enc2 module of XED.
#
# Each feature enables a version of enc2 for a different module and operand
# size. Note that attempting to enable multiple enc2 feature variants is not
# supported and will result in a compile error (instead of the link error it
# would normally emit).
enc2-m32-a32 = ["bindgen", "_internal-enc2"]
enc2-m64-a64 = ["bindgen", "_internal-enc2"]

# Enable the checked variants for the enc2 module of XED.
enc2-chk = ["bindgen", "_internal-enc2"]

# Generate bindings with bindgen at build-time instead of using the
# pregenerated bindings.
#
# This will be slower but is required for certain feature combinations.
bindgen = ["dep:bindgen"]

dylib = []

# Build xed with the enc2.
#
# This is an internal feature and you do not need to enable it manually.
_internal-enc2 = []

[dependencies]

[build-dependencies]
Expand Down
115 changes: 96 additions & 19 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{
str::FromStr,
};

use target_lexicon::Triple;
use target_lexicon::{OperatingSystem, Triple};

fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
fs::create_dir(path).or_else(|e| match e.kind() {
Expand Down Expand Up @@ -47,7 +47,7 @@ fn find_python() -> Command {
panic!("Unable to find a working python installation. Tried `python3` and `python`.");
}

fn build_xed() {
fn build_xed(_cc: &cc::Build) {
println!("cargo:rerun-if-changed=xed/VERSION");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed-env=PROFILE");
Expand All @@ -56,6 +56,12 @@ fn build_xed() {
let cwd = env::current_dir().expect("Failed to get CWD");
let target = env::var("TARGET").expect("Failed to read TARGET");
let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_owned());
let debug = match env::var("DEBUG") {
Ok(var) => var != "false",
_ => false,
};

let triple = Triple::from_str(&target).expect("TARGET was not a valid target triple");

let install_dir = out_dir.join("install");
let build_dir = out_dir.join("build");
Expand All @@ -82,24 +88,31 @@ fn build_xed() {
.arg(&mfile_path)
.arg("install")
.arg(format!("--jobs={}", num_jobs))
.arg("--silent")
.arg("--static-stripped")
.arg("--extra-ccflags=-fPIC")
.arg("--no-werror")
.arg(format!(
"--host-cpu={}",
Triple::from_str(&target)
.expect("TARGET was not a valid target triple")
.architecture
))
.arg(format!("--host-cpu={}", triple.architecture))
.arg(format!("--install-dir={}", install_dir.display()));

if cfg!(feature = "dylib") {
cmd.arg("--shared");
} else {
cmd.arg("--static-stripped");
}

if debug {
cmd.arg("--debug");
}

if profile == "release" {
cmd.arg("--opt=3");
} else {
cmd.arg("--opt=0");
}

if cfg!(feature = "_internal-enc2") {
cmd.arg("--enc2");
}

eprintln!("XED build command: {:?}", cmd);

let status = cmd.status().expect("Failed to start xed build");
Expand All @@ -109,22 +122,71 @@ fn build_xed() {
}

let lib_dir = install_dir.join("lib");
let bin_dir = install_dir.join("bin");
let linkty = match cfg!(feature = "dylib") {
true => "dylib",
false => "static",
};

println!("cargo:rustc-link-search=native={}", lib_dir.display());
println!("cargo:rustc-link-lib=static=xed");
}
println!("cargo:rustc-link-lib={linkty}=xed");

fn build_inline_shim() {
if triple.operating_system == OperatingSystem::Windows {
println!("cargo:rustc-link-search=native={}", bin_dir.display());
}

macro_rules! cfg_link_enc2 {
($variant:literal) => {
if cfg!(feature = $variant) {
println!("cargo:rustc-link-lib={linkty}=xed-{}", $variant);

if cfg!(feature = "enc2-chk") {
println!("cargo:rustc-link-lib={linkty}=xed-chk-{}", $variant);
}
}
};
}

cfg_link_enc2!("enc2-m32-a32");
cfg_link_enc2!("enc2-m64-a64");

#[cfg(not(feature = "dylib"))]
#[cfg(all(feature = "enc2-m32-a32", feature = "enc2-m64-a64"))]
compile_error!(concat!(
"Cannot enable both `enc2-m32-a32` and `enc2-m64-a64` features when linking statically.",
"You can avoid this by enabling the `dylib` feature to build these as dylibs."
));
}
fn build_inline_shim(mut cc: cc::Build) {
let cwd = std::env::current_dir().unwrap();
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("Failed to read OUT_DIR"));

let mut cc = cc::Build::new();
cc.include(out_dir.join("install/include"))
.include(&cwd)
// xed-static.c contains an instance of this. It's not an error and we
// don't want to be modifying generated files so just silence the warning.
.flag_if_supported("-Wno-duplicate-decl-specifier");

macro_rules! cfg_define_enc2 {
($variant:literal) => {
if cfg!(feature = $variant) {
let def = $variant.replace('-', "_").to_ascii_uppercase();
cc.define(&format!("XED_SYS_{def}"), None);
}
};
}

cfg_define_enc2!("enc2-m32-a32");
cfg_define_enc2!("enc2-m64-a64");

if cfg!(feature = "enc2-chk") {
cc.define("XED_SYS_ENC2_CHK", None);
}

if cfg!(feature = "dylib") {
cc.define("XED_DLL", None);
}

if cfg!(feature = "bindgen") {
cc.file(out_dir.join("xed-static.c"));
} else {
Expand All @@ -140,7 +202,7 @@ fn build_bindgen() {

let dot_rs = out_dir.join("xed.rs");

let builder = bindgen::builder()
let mut builder = bindgen::builder()
.clang_arg("-I")
.clang_arg(out_dir.join("install/include").display().to_string())
.allowlist_type("xed3?_.*")
Expand All @@ -154,6 +216,22 @@ fn build_bindgen() {
.wrap_static_fns_path(out_dir.join("xed-static.c"))
.wrap_static_fns_suffix("_xed_sys_inline");

if cfg!(feater = "dylib") {
builder = builder.clang_arg("-DXED_DLL");
}

if cfg!(feature = "enc2-m32-a32") {
builder = builder.clang_arg("-DXED_SYS_ENC2_M32_A32");
}

if cfg!(feature = "enc2-m64-a64") {
builder = builder.clang_arg("-DXED_SYS_ENC2_M64_A64");
}

if cfg!(feature = "enc2-chk") {
builder = builder.clang_arg("-DXED_SYS_ENC2_CHK");
}

let bindings = builder
.header("xed.h")
.generate()
Expand All @@ -168,10 +246,9 @@ fn build_bindgen() {
}

fn main() {
build_xed();

let cc = cc::Build::new();
build_xed(&cc);
#[cfg(feature = "bindgen")]
build_bindgen();

build_inline_shim();
build_inline_shim(cc);
}
22 changes: 21 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,30 @@
//! Note that [`xed_tables_init()`][0] must be called before using the library.
//!
//! # Features
//!
//! - `bindgen` - Don't use the bundled bindings files and instead regenerate
//! rust bindings from scratch at compile time. You should never need to
//! enable this manually but it will be enabled by other features.
//! - `dylib` - Link XED (and `enc2` libraries if enabled) dynamically. This
//! allows you to work around the link errors that prevent you from linking
//! both enc2 libraries statically.
//!
//! ## `enc2`
//! XED has the option to enable its [`enc2`][1] encoder. This contains a
//! function to generate an x86 instruction for _every single instruction
//! variant_. As such, it is slow to compile and running bindgen on the
//! resulting headers generates a mountain (20+MB) of rust code.
//!
//! The generated static libraries also unfortunately have some duplicate
//! symbols so attempting to link both will result in linker errors. To make
//! the error less confusing this library will fail to compile if both
//! `enc2-m64-a64` and `enc2-m32-a32` are enabled and the `dylib` feature is
//! not enabled.
//!
//! - `enc2-m64-a64` - Enable enc2 for 64-bit mode with 64-bit addresses.
//! - `enc2-m32-a32` - Enable enc2 for 32-bit mode with 32-bit addresses.
//! - `enc2-chk` - Enable checked variants of the of the enc2 functions. Note
//! that this feature does nothing unless you have enabled one of the other
//! enc2 features.
//!
//! [0]: crate::xed_tables_init
//! [1]: https://github.com/intelxed/xed/wiki/The-fast-encoder---enc2
Expand Down
17 changes: 12 additions & 5 deletions xed.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@
#include "xed/xed-interface.h"
#include "xed/xed-isa-set.h"

#ifdef XED_ENC2_ENCODER
#include "xed/xed-enc2-m32-a32.h"
#include "xed/xed-chk-enc2-m32-a32.h"
#include "xed/xed-enc2-m64-a64.h"
#include "xed/xed-chk-enc2-m64-a64.h"
#ifdef XED_SYS_ENC2_M32_A32
# include "xed/xed-enc2-m32-a32.h"
# ifdef XED_SYS_ENC2_CHK
# include "xed/xed-chk-enc2-m32-a32.h"
# endif
#endif

#ifdef XED_SYS_ENC2_M64_A64
# include "xed/xed-enc2-m64-a64.h"
# ifdef XED_SYS_ENC2_CHK
# include "xed/xed-chk-enc2-m64-a64.h"
# endif
#endif