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

Linux: Missing SONAME in "libwasmer.so" hinders usage in CMake #2429

Closed
MartinKolbAtWork opened this issue Jun 18, 2021 · 21 comments · Fixed by #2430, #2449 or #2457
Closed

Linux: Missing SONAME in "libwasmer.so" hinders usage in CMake #2429

MartinKolbAtWork opened this issue Jun 18, 2021 · 21 comments · Fixed by #2430, #2449 or #2457
Assignees
Labels
bug Something isn't working

Comments

@MartinKolbAtWork
Copy link

The shared library libwasmer.so does not have an SONAME specified. This can be checked using this command:
objdump -p libwasmer.so | grep SONAME

When libwasmer.so is consumed in CMake, the linker produces a wrong output file due to the missing SONAME.
There is a workaround for this in CMake, but according to a reply from the CMake folks, the missing SONAME is a bug that must be fixed by the library provider:
https://gitlab.kitware.com/cmake/cmake/-/issues/22307#note_971562
The libwasmer.so file should have a SONAME. If it doesn't, that's a bug in the wasmer package

The problem is inherent for all Rust builds of cdylibs: rust-lang/cargo#5045
The Rust community did not fix this since 2018 (see issue above), but fortunately it’s easy to fix for library creators. You just need to put the following code into the build.rs of the library:

if cfg!(target_os = "linux") {
    println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libwasmer.so");
}

I tried putting these lines into lib/c-api/build.rs, and then libwasmer.so was built correctly, including a SONAME entry.

Could you please fix this issue?

Thanks
Martin

@MartinKolbAtWork MartinKolbAtWork added the bug Something isn't working label Jun 18, 2021
Hywan added a commit to Hywan/wasmer that referenced this issue Jun 18, 2021
See wasmerio#2429 for more
context. I'm copy-pasting the original comment from @MartinKolbAtWork
here:

> The shared library `libwasmer.so` does not have an `SONAME`
> specified. This can be checked using this command: `objdump -p
> libwasmer.so | grep SONAME`
>
> When `libwasmer.so` is consumed in CMake, the **linker produces a
> wrong output file due to the missing SONAME**.  There is a
> workaround for this in CMake, but according to a reply from the
> CMake folks, the missing SONAME is a bug that must be fixed by the
> library provider:
> https://gitlab.kitware.com/cmake/cmake/-/issues/22307#note_971562
> “_The libwasmer.so file should have a SONAME.  If it doesn't, that's
> a bug in the wasmer package_”
>
>  The problem is **inherent for all Rust builds of cdylibs**:
> rust-lang/cargo#5045 The Rust community
> did not fix this since 2018 (see issue above), but fortunately it’s
> **easy to fix** for library creators. You just need to put the
> following code into the `build.rs` of the library:
>
> ```
> if cfg!(target_os = "linux") {
>     println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libwasmer.so");
> }
> ```
>
> I tried putting these lines into `lib/c-api/build.rs`, and then
> `libwasmer.so` was built correctly, including a SONAME entry.
Hywan added a commit to Hywan/wasmer that referenced this issue Jun 18, 2021
See wasmerio#2429 for more
context. I'm copy-pasting the original comment from @MartinKolbAtWork
here:

> The shared library `libwasmer.so` does not have an `SONAME`
> specified. This can be checked using this command: `objdump -p
> libwasmer.so | grep SONAME`
>
> When `libwasmer.so` is consumed in CMake, the **linker produces a
> wrong output file due to the missing SONAME**.  There is a
> workaround for this in CMake, but according to a reply from the
> CMake folks, the missing SONAME is a bug that must be fixed by the
> library provider:
> https://gitlab.kitware.com/cmake/cmake/-/issues/22307#note_971562
> “_The libwasmer.so file should have a SONAME.  If it doesn't, that's
> a bug in the wasmer package_”
>
>  The problem is **inherent for all Rust builds of cdylibs**:
> rust-lang/cargo#5045 The Rust community
> did not fix this since 2018 (see issue above), but fortunately it’s
> **easy to fix** for library creators. You just need to put the
> following code into the `build.rs` of the library:
>
> ```
> if cfg!(target_os = "linux") {
>     println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libwasmer.so");
> }
> ```
>
> I tried putting these lines into `lib/c-api/build.rs`, and then
> `libwasmer.so` was built correctly, including a SONAME entry.
Hywan added a commit to Hywan/wasmer that referenced this issue Jun 18, 2021
See wasmerio#2429 for more
context. I'm copy-pasting the original comment from @MartinKolbAtWork
here:

> The shared library `libwasmer.so` does not have an `SONAME`
> specified. This can be checked using this command: `objdump -p
> libwasmer.so | grep SONAME`
>
> When `libwasmer.so` is consumed in CMake, the **linker produces a
> wrong output file due to the missing SONAME**.  There is a
> workaround for this in CMake, but according to a reply from the
> CMake folks, the missing SONAME is a bug that must be fixed by the
> library provider:
> https://gitlab.kitware.com/cmake/cmake/-/issues/22307#note_971562
> “_The libwasmer.so file should have a SONAME.  If it doesn't, that's
> a bug in the wasmer package_”
>
>  The problem is **inherent for all Rust builds of cdylibs**:
> rust-lang/cargo#5045 The Rust community
> did not fix this since 2018 (see issue above), but fortunately it’s
> **easy to fix** for library creators. You just need to put the
> following code into the `build.rs` of the library:
>
> ```
> if cfg!(target_os = "linux") {
>     println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libwasmer.so");
> }
> ```
>
> I tried putting these lines into `lib/c-api/build.rs`, and then
> `libwasmer.so` was built correctly, including a SONAME entry.
Hywan added a commit to Hywan/wasmer that referenced this issue Jun 18, 2021
See wasmerio#2429 for more
context. I'm copy-pasting the original comment from @MartinKolbAtWork
here:

> The shared library `libwasmer.so` does not have an `SONAME`
> specified. This can be checked using this command: `objdump -p
> libwasmer.so | grep SONAME`
>
> When `libwasmer.so` is consumed in CMake, the **linker produces a
> wrong output file due to the missing SONAME**.  There is a
> workaround for this in CMake, but according to a reply from the
> CMake folks, the missing SONAME is a bug that must be fixed by the
> library provider:
> https://gitlab.kitware.com/cmake/cmake/-/issues/22307#note_971562
> “_The libwasmer.so file should have a SONAME.  If it doesn't, that's
> a bug in the wasmer package_”
>
>  The problem is **inherent for all Rust builds of cdylibs**:
> rust-lang/cargo#5045 The Rust community
> did not fix this since 2018 (see issue above), but fortunately it’s
> **easy to fix** for library creators. You just need to put the
> following code into the `build.rs` of the library:
>
> ```
> if cfg!(target_os = "linux") {
>     println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libwasmer.so");
> }
> ```
>
> I tried putting these lines into `lib/c-api/build.rs`, and then
> `libwasmer.so` was built correctly, including a SONAME entry.
@Hywan
Copy link
Contributor

Hywan commented Jun 18, 2021

See #2430 for the bug fix. Can you please help us to see if it fixes your issue? Thanks!

@Hywan Hywan self-assigned this Jun 18, 2021
@lu-zero
Copy link

lu-zero commented Jun 18, 2021

@Hywan could you please consider using cargo-c? I can send you a patch if you need help in converting the c-api build system (it is mainly removing the build.rs since cargo-c does already everything for you :)).

@Hywan
Copy link
Contributor

Hywan commented Jun 18, 2021

We need to investigate to see if it's worth it :-). Thank you for the proposal!

@MartinKolbAtWork
Copy link
Author

Hi @Hywan,

Thanks for your fast response.
I tried out your suggested change, but unfortunately it does not work when building wasmer with Makefile in the root directory of the repository.

To adjust the Makefile, change the capi-setup section to these lines:

capi-setup:
ifeq ($(IS_WINDOWS), 1)
  RUSTFLAGS += -C target-feature=+crt-static
else
ifeq ($(IS_LINUX), 1)
  RUSTFLAGS += -C link-arg=-Wl,-soname,libwasmer.so
endif
endif

Maybe you should consider using my original change in the build.rs file, because this would affect all build types and hence eliminate the need to modify two places.

Thanks, Martin

@Hywan
Copy link
Contributor

Hywan commented Jun 18, 2021

Did you try to ˋcargo clean` before rebuilding the library? I'm surprised it doesn't work.

@MartinKolbAtWork
Copy link
Author

Yes, I did a clean build.

I'm actually not surprised, because IHMO, the settings from Makefile override the otherwise provided settings.

You can try the Makefile build on your own:

make --directory=<repo-dir> build-capi 
make --directory=<repo-dir> package-capi 

After the two build steps, simply check with objdump if SONAME is present:
objdump -p <repo-dir>/package/lib/libwasmer.so | grep SONAME

Thanks, Martin

@bors bors bot closed this as completed in 99b24a9 Jun 18, 2021
@MartinKolbAtWork
Copy link
Author

MartinKolbAtWork commented Jun 21, 2021

Hi @Hywan and @syrusakbary,

thanks for the provided fix.
However, as mentioned before, the fix does not work when building libwasmer.so via Makefile, like so:

make --directory=<repo-dir> build-capi 
make --directory=<repo-dir> package-capi 

After the two build steps, you can check with objdump that SONAME is not present:
objdump -p <repo-dir>/package/lib/libwasmer.so | grep SONAME

Could you please reopen the issue and consider the solution that I suggested (using build.rs) or any other solution that works via the Makefile build?

Thanks, Martin

@Hywan Hywan reopened this Jun 21, 2021
@Hywan
Copy link
Contributor

Hywan commented Jun 21, 2021

I'm surprised it doesn't work because the .cargo/config file should configure the linker as expected. I'll investigate :-).

@lu-zero
Copy link

lu-zero commented Jun 21, 2021

Meanwhile I'm making sure cargo-c can be used with minimal changes on your side :P

If you do not want to use it please consider using https://crates.io/crates/cdylib-link-lines instead of hand-crafting.

@Hywan
Copy link
Contributor

Hywan commented Jun 21, 2021

Ah nice. Why aren't you using this crate to replace this code https://github.com/lu-zero/cargo-c/blob/03823d03add503326c82045f8a4edfadecdc35dc/src/target.rs#L54-L91 :-) (honest question)?

@lu-zero
Copy link

lu-zero commented Jun 21, 2021

I wrote both, ideally cdylib-link-lines is a stop-gap since cargo-c provides lots more (pkg-config file generation, installation) and reduces a lot the details you need to care about as crate developer.

I could make a new release of cdylib-link-lines that is in sync with the current cargo-c if there are enough users.

@Hywan
Copy link
Contributor

Hywan commented Jun 21, 2021

I'm more concerned about the impact on the CI when compiling cargo-c. cdylib-link-lines seems more appropriate to our needs for the moment. I can update the crate to make it synchronized with cargo-c if you want.

@lu-zero
Copy link

lu-zero commented Jun 21, 2021

Pull requests are always welcome :)

You can fetch the pre-built cargo-c from the releases and it would have a negligible impact.

It would reduce the build time if you could move away some of the custom header logic, but that could be the discussion for another issue I guess :)

Hywan added a commit to Hywan/cdylib-link-lines that referenced this issue Jul 5, 2021
As discussed in
wasmerio/wasmer#2429 (comment),
it would be ideal if this crate could get synchronized with `cargo-c`
which has a very similar code but which supports more targets.

That's exactly what this patch does. It changes the code from a
`if`+`else` workflow to a `match` version. It also adds support for
`android`, `freebsd`, `dragonfly`, `netbsd` and `ios`.
@Hywan
Copy link
Contributor

Hywan commented Jul 5, 2021

@lu-zero I've updated cdylib-link-lines to support the same set of targets than cargo-c.

lu-zero pushed a commit to lu-zero/cdylib-link-lines that referenced this issue Jul 5, 2021
As discussed in
wasmerio/wasmer#2429 (comment),
it would be ideal if this crate could get synchronized with `cargo-c`
which has a very similar code but which supports more targets.

That's exactly what this patch does. It changes the code from a
`if`+`else` workflow to a `match` version. It also adds support for
`android`, `freebsd`, `dragonfly`, `netbsd` and `ios`.
@Hywan
Copy link
Contributor

Hywan commented Jul 6, 2021

In #2449, I'm using the cdylib-link-lines crate to handle everything properly. I hope it will solve the problem for all platforms @MartinKolbAtWork :-).

@MartinKolbAtWork
Copy link
Author

MartinKolbAtWork commented Jul 7, 2021

Hi @Hywan,

thanks for providing the fix #2449

Let me share some thoughts on this:

(1) The (clean) build with this fix included does not create an “libwasmer.so” that has an SONAME.

(2) To check if the SONAME is present, the following three statements will do a perfect job. If objdump/grep does not emit any output, then no SONAME is present.

make --directory=<repo-dir> build-capi 
make --directory=<repo-dir> package-capi 
objdump -p <repo-dir>/package/lib/libwasmer.so | grep SONAME

(3) Looking at the code changes, I observed that build_cdylib is supposed to produce the desired outcome:

fn build_cdylib() {
    // Configure the `soname`, `install_name`, `current_version`,
    // `out-implib`, `output-def` etc. for as much platforms as
    // possible.
    cdylib_link_lines::shared_object_link_args(
        "wasmer",
        &env::var("CARGO_PKG_VERSION_MAJOR").unwrap(),
        &env::var("CARGO_PKG_VERSION_MINOR").unwrap(),
        &env::var("CARGO_PKG_VERSION_PATCH").unwrap(),
        &env::var("CARGO_CFG_TARGET_ARCH").unwrap(),
        &env::var("CARGO_CFG_TARGET_OS").unwrap(),
        &env::var("CARGO_CFG_TARGET_ENV").unwrap(),
        "/usr/local/lib".into(),
        env::var_os("CARGO_TARGET_DIR").map_or(
            {
                let manifest_dir: PathBuf = env::var_os("CARGO_MANIFEST_DIR").unwrap().into();
                manifest_dir
                    .join("target")
                    .join(env::var("PROFILE").unwrap())
            },
            Into::into,
        ),
    );
}

Calling shared_object_link_args creates a vector of strings that contain linker arguments. These must be passed to the linker, otherwise this statement has no effect. Passing statements to the linker within a “build.rs” files is done be printing it to the standard output. Changing build_cdylib like so will pass these arguments to the linker:

fn build_cdylib() {
    // Configure the `soname`, `install_name`, `current_version`,
    // `out-implib`, `output-def` etc. for as much platforms as
    // possible.
    let lines = cdylib_link_lines::shared_object_link_args(
        "wasmer",
...
    let link = "cargo:rustc-cdylib-link-arg=";
    for line in lines {
        println!("{}{}", link, line);
    }
}

(4) The modified code in build_cdylib creates an SONAME that is versioned. As Wasmer’s current version is 2, the output of objdump/grep will look like this:

SONAME libwasmer.so.2

Having an SONAME of “libwasmer.so.2” and the actual file name is “libwasmer.so” will cause client code to break. The client code will compile and link, but at runtime the client program will no longer start. Such an error message will appear:

error while loading shared libraries: libwasmer.so.2: cannot open shared object file: No such file or directory

(5) Creating versioned SONAMES (like libwasmer.so.2) will make life more complicated for both Wasmer development, as well as client code using Wasmer’s shared libraries.
Wasmer development will need to make explicit decisions about compatibility rules, about how to name the files and about how the compatibility will affect the SONAME entries.
For Wasmer client code, it makes life more complicated, as the created/shipped file names of the shared libraries might change repeatedly.

SUGGESTION
I like Wasmer and my intention is to make it easier to consume Wasmer from C/C++ code. Therefore, my initial ask in this issue was to add an unversioned SONAME to libwasmer.so. This can be done by simply adding the two lines of my initial suggestion to the build.rs right at the place where build_cdylib() was called:

fn main() {
...
    build_inline_c_env_vars();

    // Add SONAME entry to shared library
    if cfg!(target_os = "linux") {
        println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libwasmer.so");
    }    
}

If you don’t like my suggestion, I’m perfectly fine with this. I can easily live with the CMake workaround that allows me to use “libwasmer.so”.
But please remove the current code again that tries to introduce versioning to “libwasmer.so”. This will make life more complicated without any gain.

Best,
Martin

@jcaesar
Copy link
Contributor

jcaesar commented Jul 7, 2021

@MartinKolbAtWork I've tried this, and the CI doesn't like it. If you've got any idea why, it might be good to share it.

(I thought it's because the SONAME doesn't match the output file name (which is libwasmer_c_api.so), so I tried creating a symlink with the correct name, to no avail. Also, it builds and tests just fine on my arch linux, so I guess this depends on the linker somehow?)

@Hywan Hywan reopened this Jul 8, 2021
@Hywan
Copy link
Contributor

Hywan commented Jul 8, 2021

@MartinKolbAtWork Yeah sorry, it's a stupid error… My Linux machine is now repaired and I can test it.

The problem is that real library name is libwasmer_c_api but we are renaming it for libwasmer when packaging everything. I need to find a workaround so that the CI is happy and the packaging is happy too. Working on it.

@Hywan
Copy link
Contributor

Hywan commented Jul 8, 2021

OK, #2457 should fix everything. By renaming libwasmer_c_api to libwasmer, we had some implications when testing and when documenting the library. I hope everything is fine now.

@MartinKolbAtWork
Copy link
Author

Hi @Hywan,

with #2457 it works as expected.
Thanks again for your efforts 👍

Best, Martin

@Hywan
Copy link
Contributor

Hywan commented Jul 9, 2021

@MartinKolbAtWork Thank you for your patience and for the feature request!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment