Skip to content

Commit

Permalink
added new '--no-include-main-msvc' command-line build option, which a…
Browse files Browse the repository at this point in the history
…llows for Windows DLLs to be built *without* the '/include:main' linker argument. This, plus a few other tricks, allows for Windows DLLs to be built for fuzzing.
  • Loading branch information
cwshugg committed Oct 15, 2024
1 parent e2b684b commit 30bac16
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 20 deletions.
14 changes: 14 additions & 0 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,20 @@ pub struct BuildOptions {
/// and the fuzzer can store an input to the corpus at each condition that it passes;
/// giving it a better chance of producing an input that reaches `res = 2;`.
pub disable_branch_folding: Option<bool>,

#[arg(long)]
/// Disable the inclusion of the `/include:main` MSVC linker argument
///
/// The purpose of `/include:main` is to force the MSVC linker to include an
/// external reference to the symbol `main`, such that fuzzing targets built
/// on Windows are able to find LibFuzzer's `main` function.
///
/// In certain corner cases, users may prefer to *not* build with this
/// argument. One such example: if a user is intending to build and fuzz a
/// Windows DLL, they would likely choose to enable this flag, to prevent
/// the DLL from having an extern `main` reference added to it. (DLLs/shared
/// libraries should not have any reference to `main`.)
pub no_include_main_msvc: bool,
}

impl stdfmt::Display for BuildOptions {
Expand Down
36 changes: 16 additions & 20 deletions src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,28 +241,24 @@ impl FuzzProject {
if !build.release || build.debug_assertions || build.careful_mode {
rustflags.push_str(" -Cdebug-assertions");
}
if build.triple.contains("-msvc") {
// The entrypoint is in the bundled libfuzzer rlib, this gets the
// linker to find it.
rustflags.push_str(" -Clink-arg=/include:main");

// NOTE: On Windows, if the user's fuzzing targets have a dependency
// on a local Rust DLL (with `crate-type` containing `["cdylib"]),
// the MSVC Linker may be unable to resolve the `main` symbol when
// linking the DLL. It may fail with this error:
//
// LINK : error LNK2001: unresolved external symbol main
// C:\....\depedency.dll : fatal error LNK1120: 1 unresolved externals
if build.triple.contains("-msvc") && !build.no_include_main_msvc {
// This forces the MSVC linker (which runs on Windows systems) to
// find the entry point (i.e. the `main` function) within the
// LibFuzzer `.rlib` file produced during the build.
//
// We cannot remove the above argument from the rustc args *only*
// for the cdylib dependencies, so this fix will have to be on the
// user's side. To fix this, the user should add the following MSVC
// linker argument to their DLL's build script (`build.rs`):
// The `--no-include-main-msvc` argument disables the addition of
// this linker argument. In certain situations, a user may not want
// this argument included as part of the MSVC invocation.
//
// println!("cargo:rustc-link-arg=/force:unresolved");
//
// See here for information on the `/force` MSVC linker argument:
// https://learn.microsoft.com/en-us/cpp/build/reference/force-force-file-output
// For example, if the user is attempting to build and fuzz a
// Windows DLL (shared library), adding `/include:main` will force
// the DLL to compile with an external reference to `main`.
// DLLs/shared libraries are designed to be built as a separate
// object file, intentionally left *without* knowledge of the entry
// point. So, by forcing a DLL to include `main` will cause linking
// to fail. Using `--no-include-main-msvc` will allow the DLL to be
// built without issue.
rustflags.push_str(" -Clink-arg=/include:main");
}

// If release mode is enabled then we force 1 CGU to be used in rustc.
Expand Down

0 comments on commit 30bac16

Please sign in to comment.