Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 5 additions & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ jobs:
toolchain: ${{ matrix.toolchain }}
profile: minimal
override: true
targets: "wasm32-unknown-unknown"
targets: "wasm32-unknown-unknown,i686-pc-windows-msvc"
- name: "Test on the native x86_64-pc-windows-mscv"
run: cargo test --all-features --workspace
- name: "Test when cross-compiling to x86_64-pc-windows-gnu"
env:
AUDITABLE_TEST_TARGET: "x86_64-pc-windows-gnu"
run: cargo test --all-features --workspace
- name: "Test when cross-compiling to i686-pc-windows-msvc"
env:
AUDITABLE_TEST_TARGET: "i686-pc-windows-msvc"
run: cargo test --all-features --workspace
113 changes: 113 additions & 0 deletions cargo-auditable/src/object_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,38 @@ fn create_object_file(
abi_version,
e_flags,
};

// Add the COFF `@feat.00` symbol used to communicate linker feature flags.
//
// For i386, bit 0 (`IMAGE_FILE_SAFE_EXCEPTION_HANDLER`) marks the object as SAFESEH-compatible.
// Without this, linkers can reject the object with `/safeseh`.
//
// - <https://github.com/rust-lang/rust/issues/96498>
// - <https://learn.microsoft.com/en-us/windows/win32/debug/pe-format>
if binary_format == BinaryFormat::Coff {
// Disable mangling so the "@feat.00" symbol name is written verbatim.
// CoffI386 mangling adds a `_` prefix which would break this special symbol.
let original_mangling = file.mangling();
file.set_mangling(write::Mangling::None);

let mut feature: u64 = 0;
if architecture == Architecture::I386 {
feature |= 1; // IMAGE_FILE_SAFE_EXCEPTION_HANDLER
}
file.add_symbol(Symbol {
name: b"@feat.00".to_vec(),
value: feature,
size: 0,
kind: SymbolKind::Data,
scope: SymbolScope::Compilation,
weak: false,
section: SymbolSection::Absolute,
flags: SymbolFlags::None,
});

file.set_mangling(original_mangling);
}

Some(file)
}

Expand Down Expand Up @@ -322,6 +354,87 @@ windows
assert_eq!(result.architecture(), Architecture::X86_64);
}

#[test]
fn test_create_object_file_windows_msvc_i686() {
let rustc_output = br#"debug_assertions
target_arch="x86"
target_endian="little"
target_env="msvc"
target_family="windows"
target_feature="fxsr"
target_feature="sse"
target_feature="sse2"
target_os="windows"
target_pointer_width="32"
target_vendor="pc"
windows
"#;
let target_triple = "i686-pc-windows-msvc";
let target_info = parse_rustc_target_info(rustc_output);
let result = create_object_file(&target_info, target_triple).unwrap();
assert_eq!(result.format(), BinaryFormat::Coff);
assert_eq!(result.architecture(), Architecture::I386);
}

/// Verify that i686 COFF metadata objects contain an absolute `@feat.00` symbol with
/// `IMAGE_FILE_SAFE_EXCEPTION_HANDLER` (bit 0) set.
///
/// See <https://github.com/rust-lang/rust/issues/96498>
#[test]
fn test_create_metadata_file_windows_msvc_i686_has_feat00() {
let rustc_output = br#"debug_assertions
target_arch="x86"
target_endian="little"
target_env="msvc"
target_family="windows"
target_feature="fxsr"
target_feature="sse"
target_feature="sse2"
target_os="windows"
target_pointer_width="32"
target_vendor="pc"
windows
"#;
let target_triple = "i686-pc-windows-msvc";
let target_info = parse_rustc_target_info(rustc_output);
let contents = b"test audit data";
let result = create_metadata_file(
&target_info,
target_triple,
contents,
"AUDITABLE_VERSION_INFO",
)
.expect("should produce an object file for i686-pc-windows-msvc");

// Parse the COFF symbol table and verify `@feat.00` has value bit0=1 and absolute section.
let symtab_ptr = u32::from_le_bytes(result[8..12].try_into().unwrap()) as usize;
let sym_count = u32::from_le_bytes(result[12..16].try_into().unwrap()) as usize;
let symbol_size = 18;

let feat = (0..sym_count).find_map(|i| {
let start = symtab_ptr + i * symbol_size;
let end = start + symbol_size;
let entry = result.get(start..end)?;
if &entry[0..8] != b"@feat.00" {
return None;
}
let value = u32::from_le_bytes(entry[8..12].try_into().unwrap());
let section_number = i16::from_le_bytes(entry[12..14].try_into().unwrap());
Some((value, section_number))
});

let (value, section_number) = feat.expect("COFF object for i686 must contain @feat.00");
assert_eq!(
value & 1,
1,
"@feat.00 must set IMAGE_FILE_SAFE_EXCEPTION_HANDLER on i686"
);
assert_eq!(
section_number, -1,
"@feat.00 must be an absolute COFF symbol (section number -1)"
);
}

#[test]
fn test_create_object_file_windows_gnu() {
let rustc_output = br#"debug_assertions
Expand Down
4 changes: 4 additions & 0 deletions cargo-auditable/src/platform_detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ pub fn is_32bit(target_info: &RustcTargetInfo) -> bool {
key_equals(target_info, "target_pointer_width", "32")
}

pub fn is_x86(target_info: &RustcTargetInfo) -> bool {
key_equals(target_info, "target_arch", "x86")
}

fn key_equals(target_info: &RustcTargetInfo, key: &str, value: &str) -> bool {
target_info.get(key).map(|s| s.as_str()) == Some(value)
}
11 changes: 9 additions & 2 deletions cargo-auditable/src/rustc_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{

use crate::{
binary_file, collect_audit_data,
platform_detection::{is_apple, is_msvc, is_wasm},
platform_detection::{is_apple, is_msvc, is_wasm, is_x86},
rustc_arguments::{self, should_embed_audit_data},
target_info,
};
Expand Down Expand Up @@ -134,7 +134,14 @@ fn rustc_command_with_audit_data(rustc_path: &OsStr) -> Option<Command> {
command.arg("-Clink-arg=-Wl,-u,_AUDITABLE_VERSION_INFO");
}
} else if is_msvc(&target_info) {
command.arg("-Clink-arg=/INCLUDE:AUDITABLE_VERSION_INFO");
// On x86 MSVC, the `object` crate's CoffI386 mangling adds a `_`
// prefix to global symbols, so the linker must reference the
// decorated name.
if is_x86(&target_info) {
command.arg("-Clink-arg=/INCLUDE:_AUDITABLE_VERSION_INFO");
} else {
command.arg("-Clink-arg=/INCLUDE:AUDITABLE_VERSION_INFO");
}
} else if is_wasm(&target_info) {
// We don't emit the symbol name in WASM, so nothing to do
} else {
Expand Down
6 changes: 6 additions & 0 deletions cargo-auditable/tests/fixtures/bare_linker/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ rustflags = ["-C", "target-feature=+crt-static"]
[target.x86_64-unknown-linux-musl]
linker = "rust-lld"
rustflags = ["-C", "target-feature=+crt-static"]

# Not included in the original Rayhunter example, this tests for
# https://github.com/rust-lang/rust/issues/96498

[target.i686-pc-windows-msvc]
linker = "rust-lld"
Loading