2
votes

I am trying to build a Rust library which can use the Opus codec. When compiling for x86_64-pc-windows-msvc everything works as expected. However when I compile for i686-pc-windows-msvc I get errors about unresolved externals. e.g.

error LNK2001: unresolved external symbol _opus_encoder_create

Obviously this is failing because it's looking for the wrong name! There shouldn't be a leading underscore there. My Rust import looks like:

extern "C" {
    pub fn opus_encoder_create(fs:i32, chan:i32, app:i32, err:*mut i32) -> *mut OpusEncoder;
}

It looks like Rust is automatically inserting the underscore at the start. Running dumpbin on both the 32 bit and 64 bit lib files (built in Visual Studio) gets me:

32 bit:

> 7202A opus_encoder_create

64 bit:

> 7202A opus_encoder_create

No underscores in sight!

What am I doing wrong? How do I properly import and call these functions from Rust?

1
Microsoft's convention for 32-bit x86 is to prefix external C names with an underscore (_) so Rust is correct, your 32-bit libraries aren't. Given the symbols have the same offset in both versions of your libraries it appears that they're both the same and your 32-bit library is actually your 64-bit library. - Ross Ridge
The build configuration is: i.imgur.com/DGFL1T3.png which presumably means it's building 32 bit libs? I also just rebuilt the opus project and copied the resulting libs into the right place, just to make sure I wasn't using the wrong ones! - Martin

1 Answers

0
votes

The Rust build script which imports the dependencies will need some logic to do different work for different platforms. e.g.

extern crate target_build_utils;

use target_build_utils::TargetInfo;

fn main() {
    let arch = TargetInfo::new().expect("could not get target info").target_arch();

    match platform {
        "x86" => { /* import 32 bit dependencies */
        "x86_64" => { /* import 64 bit dependencies */
    }
}

My original script took the following approach (caution, this does not work):

fn main() {
    add_deps();
}

#[cfg(target_arch = "x86")]
fn add_deps() {
    /* import 32 bit dependencies */
}

#[cfg(target_arch = "x86_64")]
fn add_deps() {
    /* import 64 bit dependencies */
}

However these cfg attributes are for the platform the build script is building for, not the platform the actual code itself is building for (this is actually fairly obvious in retrospect)!