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

Trying to run self-contained application on Ubuntu 22 (Linux-x64), error regarding libdl.so #2598

Closed
HoofedEar opened this issue May 6, 2023 · 28 comments · Fixed by #2607 or #3562
Closed
Labels
build-and-deploy Issues regarding to building and deploying Terminal.Gui
Milestone

Comments

@HoofedEar
Copy link

Describe the bug
I'm getting this error when attempting to run an application I built with .NET 7, Terminal.Gui 1.10.1 targeting Ubuntu 22:

Unhandled exception. System.Exception: Curses failed to initialize, the exception is: Unable to load shared library 'libdl.so' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable.
   at Terminal.Gui.CursesDriver.Init(Action)
   at Terminal.Gui.Application.InternalInit(Func`1, ConsoleDriver , IMainLoopDriver , Boolean )
   at Terminal.Gui.Application.Init(ConsoleDriver , IMainLoopDriver )
   at Program.<Main>$(String[])
Aborted

After doing some research, I found this:
https://stackoverflow.com/a/75855054

I also know that v2 is being worked on, so I tried searching on Discussions and Issues for anything about libdl.so but didn't find anything so my apologies if this is already a known issue.

To Reproduce
Steps to reproduce the behavior:

  1. Create a simple application using .NET 7 and Terminal.Gui 1.10.1
  2. Publish as a single file, self-contained application targeting linux-x64
  3. chmod +x, attempt to run the application on Ubuntu 22

Expected behavior
The application should run, displaying the simple application

Desktop (please complete the following information):

  • OS: Ubuntu 22.04.2 LTS
@tznind
Copy link
Collaborator

tznind commented May 6, 2023

Hi thanks for reaching out for support on this. Please can you try running the following:

dotnet new --install Terminal.Gui.templates
dotnet new tui -n myproj
cd myproj
sed -i 's/net6.0/net7.0/g' ./myproj.csproj
dotnet publish -r linux-x64 --self-contained
cd ./bin/Debug/net7.0/linux-x64/publish/
./myproj

It should look like this:
shot-2023-05-06_05-09-58

@HoofedEar
Copy link
Author

HoofedEar commented May 6, 2023

Interesting! Ok that seemed to work (I did the commands you suggested on my Ubuntu box, had to install .NET 7 first).
I tried running my application again (which was built on Windows 11 btw, but trying to run on Ubuntu) and I got the same error. Do I need to run that sed command on my project and/or build it on Ubuntu instead of Windows?

EDIT: Moving my project and building it on Ubuntu seems to solve this. I tried to be lazy and build it on Windows targeting Linux lol lesson learned!

@tig
Copy link
Collaborator

tig commented May 6, 2023

Running an assembly compiled on Windows should work on Linux.

@tznind
Copy link
Collaborator

tznind commented May 6, 2023

Running an assembly compiled on Windows should work on Linux.

That is true for dll but I don't think the same applies when compiling self contained executable. Although it may just be that the self contained switch defaulted to windows because you were building on windows. You can probably build a linux binary with -r linux-x64 even on windows if you like.

@BDisp
Copy link
Collaborator

BDisp commented May 6, 2023

Running an assembly compiled on Windows should work on Linux.

Yes you are right, but we need to change the directive #if NET7_0 every time the project version is changed. Since this is at compile time, I don't know if this can be made programmatically when the project version is changed.

@BDisp
Copy link
Collaborator

BDisp commented May 6, 2023

@HoofedEar with the change in the PR #2600 you can now compile in Windows and run in the Linux.

tig pushed a commit that referenced this issue May 7, 2023
* Fixes #2598. Run self-contained application on Linux.

* Remove directive allowing always compile for single-file if PublishSingleFile is true.
@tig tig added the build-and-deploy Issues regarding to building and deploying Terminal.Gui label May 7, 2023
@HoofedEar
Copy link
Author

HoofedEar commented May 7, 2023

Ok so I did some testing:

  • Publishing on Windows, targeting linux-x64 (self-contained, produce single-file) and moving it to Ubuntu: running produces the libdl.so exception
  • Publishing on Ubuntu like this dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained also produces the error

So I think the single-file setting is the problem? I'd really like to be able to build my application as a single-file executable, but if that's not possible that's alright. (Note that building single-file, self contained targeting Windows works just fine)

@BDisp
Copy link
Collaborator

BDisp commented May 7, 2023

Did you used the PR branch?

@tig tig closed this as completed in #2607 May 7, 2023
tig added a commit that referenced this issue May 7, 2023
* cherry pick #2600 from v2_develop

* cherry pick #2600 from v2_develop

* fixing screwup

* in v1 #if is still needed
@HoofedEar
Copy link
Author

Confirmed that #2607 fixed it 👍
Just tested by building on Windows, targeting Linux-x64 (single file, self contained)
(I used my local repo of Terminal.Gui as a reference instead of the Nuget package)

Thanks for your help with this everyone 😁

@AdrianEggenberger
Copy link

I know that the issue above was closed long time ago, but I struggled over something that is directly releated to this change. I've built a self-contained linux application with Terminal-Gui 1.17.0. My application was using .NET 8 framework.

In my logs I've seen the "Unable to load shared library 'libdl.so' or one of its dependencies." message.
After some redings here I set the .NET framework down to .NET 7. Then everything worked as expected.

Is it possble that the fix for self contained applications on Linux is only applied in .NET 7 version (#if NET7_0)? Why is this not required for .NET6 and .NET8?

@BDisp
Copy link
Collaborator

BDisp commented Jun 24, 2024

@AdrianEggenberger I cannot repro this. Can you give more information, please? I created a self-contained project with net80 and I haven't this issue.

@AdrianEggenberger
Copy link

I've created a simple .NET8 console project with exactly the demo code from the "Getting Started" page.

  1. Open project in configuration Release
  2. Build the project
  3. Select Publish and create the standalone application for linux-arm64:
  • Configuration: Release|Any CPU
  • Target framework: net8.0
  • Deployment mode: Self-contained
  • Target runtime: linux-arm64
  1. Copy the built application to a Raspberry Pi 5 with latest Ubuntu
  2. Configure the application as executable
  3. Run the application

In my case it ends in the following error:
raspberry@raspberrypi:~/terminal-gui $ ./TerminalGuiApp
Unhandled exception. System.Exception: Curses failed to initialize, the exception is: Unable to load shared library 'libdl.so' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
/home/raspberry/terminal-gui/libdl.so: cannot open shared object file: No such file or directory
/home/raspberry/terminal-gui/liblibdl.so: cannot open shared object file: No such file or directory
/home/raspberry/terminal-gui/libdl.so.so: cannot open shared object file: No such file or directory
/home/raspberry/terminal-gui/liblibdl.so.so: cannot open shared object file: No such file or directory

at Terminal.Gui.CursesDriver.Init(Action terminalResized)
at Terminal.Gui.Application.InternalInit(Func1 topLevelFactory, ConsoleDriver driver, IMainLoopDriver mainLoopDriver, Boolean calledViaRunT) at Terminal.Gui.Application.Run[T](Func2 errorHandler, ConsoleDriver driver, IMainLoopDriver mainLoopDriver)
at Program.

$(String[] args) in C:\TerminalGuiApp\Program.cs:line 5
Aborted
TerminalGuiApp.zip

@dodexahedron
Copy link
Collaborator

V1 is not trim-friendly, which will cause problems with single-file deployments, for sure.

V2 is not trim-friendly, yet, but it is a goal, currently, for release.

Note that self-contained applications, single-file or not, do not automatically pull in runtime dependencies for PInvoke, and rely on the platform to figure it out.

Using the workaround in the exception is not something that should be done on a production system, since it is globally impacting for the whole system and can present a security problem if done improperly.

Just to check if it's a loader issue, can you grab a copy of those libs from /lib and place them in the directory the self-contained app is being published to? If that's all it is (likely), we can address it on the TG side.

@BDisp
Copy link
Collaborator

BDisp commented Jun 24, 2024

I don't have a raspberry. Does exist another way to test it?

@BDisp
Copy link
Collaborator

BDisp commented Jun 24, 2024

Ok, I was able to reproduce it. I was using debug instead of release, sorry. The solution is to add current versions and eliminate previous versions.

@BDisp BDisp reopened this Jun 24, 2024
BDisp added a commit to BDisp/Terminal.Gui that referenced this issue Jun 24, 2024
@BDisp
Copy link
Collaborator

BDisp commented Jun 24, 2024

@AdrianEggenberger please test with the PR #3562 and check if that fixes the issue. Thanks.

@dodexahedron
Copy link
Collaborator

Also, @BDisp, replacing DllImports with LibraryImports may help, as well as makes the PInvokes trim-friendly.

However, the conditional compile symbols in the PR don't fix it for people unless they build Terminal.Gui from source. Pre-built code, such as the nuget package, only get the effect of how it compiles on the system it is built on.

@AdrianEggenberger
Copy link

I checked with the change from PR 3562 and it fixes the issues with target framework "net8.0" In my opinion using "NET6_0_OR_GREATER" would be the right way.

While the above fixes the issue for ,NET8 it is still an issue for .NET6. As the library is not provided for "net6.0" it falls back on the .NET standard variant that doesn't have the fix from above as there NET6_0_OR_GREATER is not set.

@dodexahedron
Copy link
Collaborator

dodexahedron commented Jun 25, 2024

Well yes - whichever moniker has the necessary changes. My note was just following from the code already there, as a safe mention without having to load it up and test others with material changes like that. 😅

While I think the decision was made at some point not to support .net 6 any more, at least explicitly, using that preprocessor symbol there seems like a harmless bit of compatibility, to me, so long as we're clear we're not condoning the unsupported use - just not being hostile to it.

But I think ANY conditional that results in inconsistent behaviors between binaries of the same version but just different TFMs in nuget is un-great.

I kinda want to clean up some of the v1 PInvoke code anyway, more than just band-aids like this, even though we're just "maintaining" it now. Some pretty good changes wouldn't even require that much work and bring multiple benefits. And with a polyfill generator for the OS platform support stuff that didn't come til after netstandard2.0, even more cleanup is possible with very little work performed by humans.

@dodexahedron
Copy link
Collaborator

And that sort of thing is a good place to drop a [ComponentGuarantees(ComponentGuaranteesOptions.None)], too, in the old version at least.

@BDisp
Copy link
Collaborator

BDisp commented Jun 25, 2024

I checked with the change from PR 3562 and it fixes the issues with target framework "net8.0" In my opinion using "NET6_0_OR_GREATER" would be the right way.

While the above fixes the issue for ,NET8 it is still an issue for .NET6. As the library is not provided for "net6.0" it falls back on the .NET standard variant that doesn't have the fix from above as there NET6_0_OR_GREATER is not set.

To support net6.0 Terminal.Gui will also have to target net6.0, otherwise it will not work. My opinion is to actually add net6.0 to Terminal.Gui.csproj because it is a version that is still supported (LTS) unlike net7.0 which is already out of support (STS), as you can see in the image below.

image

tig added a commit that referenced this issue Jun 26, 2024
Fixes #2598. Trying to run self-contained application on Ubuntu 22 (Linux-x64), error regarding libdl.so
@tig tig closed this as completed in #3562 Jun 26, 2024
@dodexahedron
Copy link
Collaborator

That shouldn't usually be the case. netstandard2.0 ensures compatibility with .net framework 4.6.2 and up, as well as all versions of .net (core).

The fallback behavior if an exact SDK match can't be made (like .net6.0 target for the consumer's build, but net6.0 not defined in the TG TFMs) depends on the project configuration and the requirements of the whole build, including other dependencies' transient requirements. The default fallback is to use the lowest common framework (which can be changed in the project file and nuget config). In the case of someone trying to build against .net6.0, if something they use targets .net framework 4.8, netstandard 1.3, and netstandard2.0, they should get the lowest declared netstandard TFM of that dependency, unless they override it either in the PackageReference or the nuget config.

It's kind of a mess, honestly, and largely out of our hands.

But so long as consumers understand V1 is maintenance mode and V2 requires .net8.0, it's IMO harmless for us to add specific targets that don't require code changes and that don't alter package version resolution for existing cases....which is kind of a pain to try to test. 😞

@dodexahedron
Copy link
Collaborator

Hey - this gave me an idea just now...

Ok, so, this is meant for after V2 stable release is out there.

What if we put in an [Obsolete("Terminal.Gui V1 is deprecated. For applications targeting .net8.0 and up, please consider migrating to Terminal.Gui V2.")] or something like that on the Application class and its members, in the V1 code?

@dodexahedron
Copy link
Collaborator

Oops. Forgot to mention.

Inside a conditional for the NET8_0_OR_GREATER symbol, so it only shows up to people trying to use .net8 and up.

@BDisp
Copy link
Collaborator

BDisp commented Jun 28, 2024

On the V1 self-contained is working fine with net60 or greater. Only on the V2 it isn't.

@dodexahedron
Copy link
Collaborator

There's so much that changed between 5 and 6 in so many places of .net it's crazy.

And from 6 to 7.

And from 7 to 8.

😅

The defaults changes like the trim being opt-out now instead of opt-in are the ones that usually bite me, and sometimes more than once, since it's usually to something that only affects like...2 projects, in one specific configuration for each one, that is only ever used like twice a year. 😆

Ah well... Progress...

@BDisp
Copy link
Collaborator

BDisp commented Jun 28, 2024

Oops. Forgot to mention.

Inside a conditional for the NET8_0_OR_GREATER symbol, so it only shows up to people trying to use .net8 and up.

In v2 it is not necessary to use the #if NET8_0_OR_GREATER directive because it only uses NETCore.

@dodexahedron
Copy link
Collaborator

Good point! 😆

@tig tig added this to the v1.17.1 milestone Jul 11, 2024
@tig tig moved this to ✅ Done in Terminal.Gui V1 Maintenance Jul 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
build-and-deploy Issues regarding to building and deploying Terminal.Gui
Projects
Status: ✅ Done
6 participants