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

rust_eh_personality already defined in dynamic library #126633

Open
BenCheung0422 opened this issue Jun 18, 2024 · 5 comments
Open

rust_eh_personality already defined in dynamic library #126633

BenCheung0422 opened this issue Jun 18, 2024 · 5 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@BenCheung0422
Copy link

Code

Follow the steps to build https://github.com/ldesgoui/discord_game_sdk
This can also happen when directly linking Discord GameSDK libraries by bindgen separately.

Current output

error: linking with `link.exe` failed: exit code: 1169
  |
  = note: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.40.33807\\bin\\HostX64\\x64\\link.exe" "/NOLOGO" .. (a lot of libraries)
  = note: libstd-af7a289140bfd09b.rlib(std-af7a289140bfd09b.std.9b8e320a6364d5d1-cgu.0.rcgu.o) : error LNK2005: rust_eh_personality already defined in discord_game_sdk.lib(discord_game_sdk.dll)
          D:\..\discord_game_sdk\target\debug\deps\discord_game_sdk_sys-7c33338da05ee15f.exe : fatal error LNK1169: one or more multiply defined symbols found

Desired output

Compile and build successfully

Rationale and extra context

No response

Other cases

No response

Rust Version

rustc 1.79.0 (129f3b996 2024-06-10)
binary: rustc
commit-hash: 129f3b9964af4d4a709d1383930ade12dfe7c081
commit-date: 2024-06-10
host: x86_64-pc-windows-msvc
release: 1.79.0
LLVM version: 18.1.7

Anything else?

No response

@BenCheung0422 BenCheung0422 added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 18, 2024
@BenCheung0422
Copy link
Author

BenCheung0422 commented Jun 18, 2024

I may head to use discord-sdk crate instead, although the official SDK should still be usable. By the way, this is my simplified version of discord_game_sdk_sys/build.rs:

use std::{env, fs, path::*};
use std::fs::{create_dir_all, File};
use std::io::Write;
use zip::ZipArchive;

const SDK_VERSION: &str = "2.5.6";

fn main() {
    // DO NOT RELY ON THIS
    if cfg!(feature = "private-docs-rs") {
        return generate_ffi_bindings(bindgen::builder().header("discord_game_sdk.h"));
    }

    let sdk_path = fetch_discord_game_sdk();
    // println!("cargo:rustc-env=LD_LIBRARY_PATH={}", fs::canonicalize(sdk_path.join("lib/x86_64")).unwrap().display());
    // println!("cargo:rustc-env=DYLD_LIBRARY_PATH={}", fs::canonicalize(sdk_path.join("lib/x86_64")).unwrap().display());
    println!("cargo:rustc-link-search={}", fs::canonicalize(sdk_path.join("lib/x86_64")).unwrap().to_str().unwrap());
    println!("cargo:rerun-if-changed={}", sdk_path.display());

    generate_ffi_bindings(
        bindgen::builder().header(sdk_path.join("c/discord_game_sdk.h").to_str().unwrap()),
    );

    if cfg!(feature = "link") {
        let target = env::var("TARGET").unwrap();

        verify_installation(&target, &sdk_path);
        configure_linkage(&target, &sdk_path);
    }
}

fn fetch_discord_game_sdk() -> PathBuf {
    let mut sdk_version = SDK_VERSION.to_string();
    println!("Target Discord GameSDK: {sdk_version}");
    let main_dir = PathBuf::from("libraries");
    let extract_dir = main_dir.join("discord_game_sdk");
    create_dir_all(&extract_dir).unwrap();
    println!("Checking local cache...");

    // Check cached version
    let version_file_path = extract_dir.join("VERSION");
    if version_file_path.exists() {
        match fs::read_to_string(&version_file_path) {
            Ok(ver) => {
                if ver == sdk_version {
                    println!("Version matched, no upgrade required.");
                    return extract_dir;
                } else {
                    println!("Version not matched, an upgrade is required.");
                }
            }
            Err(err) => eprintln!("Error opening VERSION: {err}"),
        }
    }

    println!("Clearing cache directory...");
    if extract_dir.exists() {
        fs::remove_dir_all(&extract_dir).expect("remove directory");
        fs::create_dir(&extract_dir).expect("create directory");
    }

    println!("Fetching {sdk_version} SDK...");
    let download_path = main_dir.join("fetch.zip");
    reqwest::blocking::get(format!(
        "https://dl-game-sdk.discordapp.net/{sdk_version}/discord_game_sdk.zip"))
        .expect("request Discord Game SDK")
        .copy_to(&mut File::create(&download_path).expect("Discord Game SDK local cache"))
        .expect("download Discord Game SDK");
    ZipArchive::new(File::open(&download_path).expect("read download cache"))
        .expect("valid zip")
        .extract(&extract_dir)
        .expect("extract cached zip");
    fs::remove_file(download_path).expect("remove cached zip");
    File::create(version_file_path).expect("create VERSION")
        .write_all(unsafe { sdk_version.as_bytes_mut() }).expect("write VERSION");
    println!("SDK fetched.");

    // Prepare for library

    fs::rename(extract_dir.join("lib/x86_64/discord_game_sdk.so"),
               extract_dir.join("lib/x86_64/libdiscord_game_sdk.so")).unwrap();
    fs::rename(extract_dir.join("lib/x86_64/discord_game_sdk.dylib"),
               extract_dir.join("lib/x86_64/libdiscord_game_sdk.dylib")).unwrap();
    fs::rename(extract_dir.join("lib/x86_64/discord_game_sdk.dll.lib"),
               extract_dir.join("lib/x86_64/discord_game_sdk.lib")).unwrap();
    fs::rename(extract_dir.join("lib/x86/discord_game_sdk.dll.lib"),
               extract_dir.join("lib/x86/discord_game_sdk.lib")).unwrap();

    extract_dir
}

fn verify_installation(target: &str, sdk_path: &Path) {
    match target {
        "x86_64-unknown-linux-gnu" => {
            assert!(
                sdk_path.join("lib/x86_64/libdiscord_game_sdk.so").exists(),
                "{}",
                MISSING_SETUP
            );
        }

        "x86_64-apple-darwin" => {
            assert!(
                sdk_path
                    .join("lib/x86_64/libdiscord_game_sdk.dylib")
                    .exists(),
                "{}",
                MISSING_SETUP
            );
        }

        "x86_64-pc-windows-gnu" | "x86_64-pc-windows-msvc" => {
            assert!(
                sdk_path.join("lib/x86_64/discord_game_sdk.lib").exists(),
                "{}",
                MISSING_SETUP
            );
        }

        "i686-pc-windows-gnu" | "i686-pc-windows-msvc" => {
            assert!(
                sdk_path.join("lib/x86/discord_game_sdk.lib").exists(),
                "{}",
                MISSING_SETUP
            );
        }

        _ => panic!("{}", INCOMPATIBLE_PLATFORM),
    }
}

fn configure_linkage(target: &str, sdk_path: &Path) {
    match target {
        "x86_64-unknown-linux-gnu"
        | "x86_64-apple-darwin"
        | "x86_64-pc-windows-gnu"
        | "x86_64-pc-windows-msvc" => {
            println!("cargo:rustc-link-lib=discord_game_sdk");
            println!(
                "cargo:rustc-link-search={}",
                sdk_path.join("lib/x86_64").display()
            );
        }

        "i686-pc-windows-gnu" | "i686-pc-windows-msvc" => {
            println!("cargo:rustc-link-lib=discord_game_sdk");
            println!(
                "cargo:rustc-link-search={}",
                sdk_path.join("lib/x86").display()
            );
        }

        _ => {}
    }
}

fn generate_ffi_bindings(builder: bindgen::Builder) {
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());

    builder
        .ctypes_prefix("ctypes")
        .derive_copy(true)
        .derive_debug(true)
        .derive_default(true)
        .derive_eq(true)
        .derive_hash(true)
        .derive_partialeq(true)
        .generate_comments(false)
        .impl_debug(true)
        .impl_partialeq(true)
        .parse_callbacks(Box::new(Callbacks))
        .prepend_enum_name(false)
        .allowlist_function("Discord.+")
        .allowlist_type("[EI]?Discord.+")
        .allowlist_var("DISCORD_.+")
        .generate()
        .expect("discord_game_sdk_sys: bindgen could not generate bindings")
        .write_to_file(out_path.join("bindings.rs"))
        .expect("discord_game_sdk_sys: could not write bindings to file");
}

#[derive(Debug)]
struct Callbacks;

impl bindgen::callbacks::ParseCallbacks for Callbacks {
    fn int_macro(&self, name: &str, _value: i64) -> Option<bindgen::callbacks::IntKind> {
        // Must match sys::DiscordVersion
        if name.ends_with("_VERSION") {
            Some(bindgen::callbacks::IntKind::I32)
        } else {
            None
        }
    }
}

const MISSING_SETUP: &str = r#"

discord_game_sdk_sys: Hello,

You are trying to link to the Discord Game SDK.
Some additional set-up is required, namely some files need to be copied for the linker:

# Linux: prepend with `lib` and add to library search path
$ cp $DISCORD_GAME_SDK_PATH/lib/x86_64/{,lib}discord_game_sdk.so
$ export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}$DISCORD_GAME_SDK_PATH/lib/x86_64

# Mac OS: prepend with `lib` and add to library search path
$ cp $DISCORD_GAME_SDK_PATH/lib/x86_64/{,lib}discord_game_sdk.dylib
$ export DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH:+${DYLD_LIBRARY_PATH}:}$DISCORD_GAME_SDK_PATH/lib/x86_64

# Windows: copy `*.dll.lib` to `*.lib` (won't affect library search)
$ cp $DISCORD_GAME_SDK_PATH/lib/x86_64/discord_game_sdk.{dll.lib,lib}
$ cp $DISCORD_GAME_SDK_PATH/lib/x86/discord_game_sdk.{dll.lib,lib}

After all this, `cargo build` and `cargo run` should function as expected.

Please report any issues you have at:
https://github.com/ldesgoui/discord_game_sdk

Thanks, and apologies for the inconvenience

"#;

const INCOMPATIBLE_PLATFORM: &str = r#"

discord_game_sdk_sys: Hello,

You are trying to link to the Discord Game SDK.
Unfortunately, the platform you are trying to target is not supported.

Please report any issues you have at:
https://github.com/ldesgoui/discord_game_sdk

Thanks, and apologies for the inconvenience

"#;

And discord_game_sdk_sys/Cargo.toml:

[build-dependencies]
bindgen = { version = "0.69.4", default-features = false, features = ["runtime"] }
reqwest = { version = "0.12.5", features = ["blocking"] }
zip = "2.1.3"

@bjorn3
Copy link
Member

bjorn3 commented Jun 19, 2024

The Discord GameSDK uses Rust too internally and exports rust_eh_personality for whatever reason. This rust_eh_personality conflicts with the one from the libstd copy used by your own program. Discord will have to fix their SDK to stop exporting rust_eh_personality. A normal cdylib build doesn't export rust_eh_personality. Without help from someone working at Discord it would be very hard to figure out why rust_eh_personality gets exported by their SDK.

@BenCheung0422
Copy link
Author

Oh I see, so their SDK cannot be used in Rust as library at the moment. Perhaps this issue can be filed to Discord then?

@bjorn3
Copy link
Member

bjorn3 commented Jun 19, 2024

Yeah, that would be your best bet. Until they have fixed it, maybe you could try somehow patching the import library to remove the rust_eh_personality export?

@BenCheung0422
Copy link
Author

It can compile when I used

lib /MACHINE:X86 /def:discord_game_sdk.def /out:discord_game_sdk.lib

with

LIBRARY DISCORD_GAME_SDK
EXPORTS
    DiscordCreate  @1
    DiscordVersion @2

Reference: https://stackoverflow.com/a/280567

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

2 participants