Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
libwild/src/test_data/*.a -text
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,27 @@ jobs:
- run: cargo build --profile ci --workspace --no-default-features
- run: WILD_TEST_CROSS=$WILD_TEST_CROSS cargo test --profile ci --workspace

windows-build:
name: Windows build
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
id: rust-toolchain
- uses: actions/cache@v5
with:
path: |
C:\Users\runneradmin\.cargo\registry\index\
C:\Users\runneradmin\.cargo\registry\cache\
C:\Users\runneradmin\.cargo\git\db\
target\
key: ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- run: cargo build --profile ci --workspace --no-default-features
- run: cargo test --profile ci -p libwild --lib --no-default-features

minimal-versions:
runs-on: ubuntu-24.04
timeout-minutes: 10
Expand Down
11 changes: 6 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ object = { version = "0.38.0", default-features = false, features = [
os_info = "3.0.0"
paste = "1.0.15"
perf-event = "0.4.8"
perfetto-recorder = "0.2.0"
perfetto-recorder = "0.3.0"
postcard = { version = "1.1.1", features = ["use-std"] }
rayon = "1.5.1"
rstest = "0.26.1"
Expand Down
22 changes: 17 additions & 5 deletions libwild/src/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
use crate::bail;
use crate::error::Context as _;
use crate::error::Result;
use std::ffi::OsStr;
use std::ops::Range;
use std::os::unix::ffi::OsStrExt as _;
use std::path::Path;
use std::path::PathBuf;
use zerocopy::FromBytes;
use zerocopy::Immutable;
use zerocopy::KnownLayout;
Expand All @@ -20,6 +18,20 @@ pub(crate) enum ArchiveEntry<'data> {
Thin(ThinEntry<'data>),
}

fn path_from_bytes(bytes: &[u8]) -> PathBuf {
#[cfg(unix)]
{
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt as _;
std::path::Path::new(OsStr::from_bytes(bytes)).to_path_buf()
}
#[cfg(not(unix))]
{
let path = std::str::from_utf8(bytes).expect("Invalid UTF-8 in archive path name");
PathBuf::from(path)
}
}

#[derive(Clone, Copy)]
pub(crate) struct ExtendedFilenames<'data> {
data: &'data [u8],
Expand Down Expand Up @@ -324,8 +336,8 @@ impl<'data> Identifier<'data> {
}
}

pub(crate) fn as_path(&self) -> &'data std::path::Path {
Path::new(OsStr::from_bytes(self.as_slice()))
pub(crate) fn as_path(&self) -> PathBuf {
path_from_bytes(self.as_slice())
}
}

Expand Down
11 changes: 10 additions & 1 deletion libwild/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,16 @@ fn arguments_from_string(input: &str) -> Result<Vec<String>> {
}
} else {
if ch == '\\' {
ch = chars.next().context("Invalid escape")?;
let next = chars.next().context("Invalid escape")?;
// Keep backslashes unless escaping \, ", or ' so Windows paths like C:\foo survive.
if matches!(next, '\\' | '"' | '\'') {
ch = next;
} else {
let heap = heap.get_or_insert(String::new());
heap.push('\\');
heap.push(next);
continue;
}
}
heap.get_or_insert(String::new()).push(ch);
}
Expand Down
23 changes: 18 additions & 5 deletions libwild/src/dwarf_address_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,25 @@ use object::SymbolIndex;
use object::read::elf::Crel;
use object::read::elf::RelocationSections;
use std::borrow::Cow;
use std::ffi::OsStr;
use std::fmt::Display;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;

fn pathbuf_from_bytes(bytes: &[u8]) -> PathBuf {
#[cfg(unix)]
{
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;

return Path::new(OsStr::from_bytes(bytes)).to_path_buf();
}

#[cfg(not(unix))]
{
PathBuf::from(String::from_utf8_lossy(bytes).into_owned())
}
}

pub(crate) struct SourceInfo(Option<SourceInfoDetails>);

#[derive(Debug)]
Expand Down Expand Up @@ -64,7 +77,7 @@ pub(crate) fn get_source_info<A: Arch>(
let comp_dir = unit
.comp_dir
.as_ref()
.map(|dir| Path::new(OsStr::from_bytes(dir)).to_owned())
.map(|dir| pathbuf_from_bytes(dir))
.unwrap_or_default();

let mut rows = program.rows();
Expand All @@ -82,7 +95,7 @@ pub(crate) fn get_source_info<A: Arch>(
if let Some(file) = row.file(header) {
path = comp_dir.clone();

path.push(OsStr::from_bytes(
path.push(pathbuf_from_bytes(
&dwarf.attr_string(&unit, file.path_name())?,
));
}
Expand Down
10 changes: 8 additions & 2 deletions libwild/src/file_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,10 @@ impl SizedOutput {
// assuming it eventually calls exec, this at least means that it inherits the file
// descriptor for less time. i.e. this doesn't really fix anything, but makes problems less
// bad.
std::os::unix::fs::OpenOptionsExt::custom_flags(&mut open_options, libc::O_CLOEXEC);
#[cfg(unix)]
{
std::os::unix::fs::OpenOptionsExt::custom_flags(&mut open_options, libc::O_CLOEXEC);
}

match output_config.file_write_mode {
FileWriteMode::UnlinkAndReplace => {
Expand Down Expand Up @@ -316,7 +319,10 @@ impl SizedOutput {

// Making the file executable is best-effort only. For example if we're writing to a pipe or
// something, it isn't going to work and that's OK.
let _ = crate::fs::make_executable(&self.file);
#[cfg(unix)]
{
let _ = crate::fs::make_executable(&self.file);
}
Copy link
Contributor

@bjorn3 bjorn3 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe pull the #[cfg(unix)] into the make_executable body?

Edit: Didn't notice your comment above.


Ok(())
}
Expand Down
3 changes: 3 additions & 0 deletions libwild/src/fs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#[cfg(unix)]
use crate::error::Result;
#[cfg(unix)]
use std::fs::File;

#[cfg(unix)]
pub(crate) fn make_executable(file: &File) -> Result {
use std::os::unix::prelude::PermissionsExt;

Expand Down
2 changes: 1 addition & 1 deletion libwild/src/input_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ fn process_thin_archive<'data>(
ArchiveEntry::Filenames(t) => extended_filenames = Some(t),
ArchiveEntry::Thin(entry) => {
let path = entry.identifier(extended_filenames).as_path();
let entry_path = parent_path.join(path);
let entry_path = parent_path.join(&path);

let file_data =
FileData::new(&entry_path, args.prepopulate_maps).with_context(|| {
Expand Down
4 changes: 2 additions & 2 deletions libwild/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ pub(crate) mod save_dir;
pub(crate) mod sframe;
pub(crate) mod sharding;
pub(crate) mod string_merging;
#[cfg(feature = "fork")]
#[cfg(all(feature = "fork", unix))]
pub(crate) mod subprocess;
#[cfg(not(feature = "fork"))]
#[cfg(not(all(feature = "fork", unix)))]
#[path = "subprocess_unsupported.rs"]
pub(crate) mod subprocess;
pub(crate) mod symbol;
Expand Down
39 changes: 27 additions & 12 deletions libwild/src/linker_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,20 @@ mod tests {
use super::*;
use crate::args::InputSpec;
use itertools::assert_equal;
use std::path::PathBuf;

fn abs_test_path(path: &str) -> PathBuf {
#[cfg(windows)]
{
let path = path.trim_start_matches('/');
let path = path.replace('/', "\\");
PathBuf::from(format!(r"C:\{path}"))
}
#[cfg(not(windows))]
{
PathBuf::from(path)
}
}

fn parse_script(text: &str) -> Result<LinkerScript<'_>> {
LinkerScript::parse(text.as_bytes(), Path::new("test-linker-script.txt"))
Expand Down Expand Up @@ -529,58 +543,59 @@ mod tests {

#[test]
fn test_sysroot_application() {
let sysroot = Path::new("/usr/aarch64-linux-gnu");
let sysroot = abs_test_path("/usr/aarch64-linux-gnu");
let linker_script_outside_sysroot = abs_test_path("/lib/libc.so");
// Linker script is located in the sysroot
assert_equal(
maybe_apply_sysroot(
&sysroot.join("lib/libc.so"),
Path::new("/lib/libc.so.6"),
sysroot,
&sysroot,
),
Some(Box::from(sysroot.join("lib/libc.so.6"))),
);
// Linker script is not located in the sysroot
assert_equal(
maybe_apply_sysroot(
Path::new("/lib/libc.so"),
&linker_script_outside_sysroot,
Path::new("/lib/libc.so.6"),
sysroot,
&sysroot,
),
None,
);
// Sysroot enforced by `=/`
assert_equal(
maybe_apply_sysroot(
Path::new("/lib/libc.so"),
&linker_script_outside_sysroot,
Path::new("=/lib/libc.so.6"),
sysroot,
&sysroot,
),
Some(Box::from(sysroot.join("lib/libc.so.6"))),
);
// Sysroot enforced by `=`
assert_equal(
maybe_apply_sysroot(
Path::new("/lib/libc.so"),
&linker_script_outside_sysroot,
Path::new("=lib/libc.so.6"),
sysroot,
&sysroot,
),
Some(Box::from(sysroot.join("lib/libc.so.6"))),
);
// Sysroot enforced by `$SYSROOT`
assert_equal(
maybe_apply_sysroot(
Path::new("/lib/libc.so"),
&linker_script_outside_sysroot,
Path::new("$SYSROOT/lib/libc.so.6"),
sysroot,
&sysroot,
),
Some(Box::from(sysroot.join("lib/libc.so.6"))),
);
// Sysroot enforced by `$SYSROOT`
assert_equal(
maybe_apply_sysroot(
Path::new("/lib/libc.so"),
&linker_script_outside_sysroot,
Path::new("$SYSROOTlib/libc.so.6"),
sysroot,
&sysroot,
),
Some(Box::from(sysroot.join("lib/libc.so.6"))),
);
Expand Down
Loading
Loading