Libsqlite3.so: No such file or directory

I tried to start a Flutter project with a sqlite database.
For testing I run into an issue:
Invalid argument(s): Failed to load dynamic library ‘libsqlite3.so’: libsqlite3.so: cannot open shared object file: No such file or directory.

I installed it in the configuration.nix (environment.systemPackages = with pkgs; [sqlite]), then i do an nix-rebuild switch.
When I prompt the command sqlite3, the programms starts without any issue.

Why does this happen, btw. how to solve this, or is it a problem inside the Flutter project?

Thanks for help :smiley:

This is a very common issue with dynamic library loading as the dynamic libraries in the packages you added did not get added to your ld path automatically.

If you try to check your current LD_LIBRARY_PATH, you will find it will be empty:

> echo $LD_LIBRARY_PATH

This class of problems in my projects is solved by entering a nix-shell with an added LD_LIBRARY_PATH in shell hook like this:

#shell.nix
{pkgs ? import <nixpkgs> {} }:
let 
all_deps = with pkgs; [
  sqlite
  # your other dependancies
];

in
pkgs.mkShell {
  
  nativeBuildInputs = all_deps;
  shellHook = ''
    export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${pkgs.lib.makeLibraryPath all_deps}"
    '';
}

Then you just run nix-shell everytime you are working with your project

It is not recommend to add the LD_LIBRARY_PATH globally by adding it directly into your configuration.nix, you can refer to this post:

2 Likes

You generally do not want to use LD_LIBRARY_PATH if it can at all be avoided. I won’t go into details but know that it can cause all sorts of odd hard to debug issues and you don’t want that.

You should instead look into patching whatever part of the code that attempts to load the dynamic library to look inside pkgs.sqlite’s lib directory rather than global-FHS paths. If it’s an ELF binary doing this, you’d patchelf its rpath to include that for instance.

1 Like

I accept that it may be best practice, but from a usability perspective that’s pretty horrible.

Thanks for the reply. For me it takes some time to answer as I had to understand the concepts of nix, how and why the libs are already assembled at compile-time and not at compile-time.

I can understand jjpe, this may not look very user friendly and may seem daunting.
But I can also understand the answers from Chunhou20 and Atemu and the added value gained by this approach.

I wanted to try if i can patch the binary.
Are there any things to watch out for here, or can you do more harm than good?
Since I haven’t done anything like this before, how do I know that the error is in a package and how do I know which package it is?

I had never develop flutter apps before, so I do not know what will your code compiles into. There is no harm to patch the binary, the worst case scenario will just be messing up the compiled binary, making it unable to run properly, but then you just have to recompile.

I can only give you an example. My rust project compiles into a single binary and it requires the runtime library libxkbcommon. If I want to make a nix package out of this binary and want it to run without those library not found error, I will patch the result binary in the build process hook, then I can just run the binary with nix run without error.

this is part of the snippet of the package for my project, you can see I actually patch the binary in postInstall process (I patch for needed library because autoPatchelfhook will remove the rpath in the next process if the library is not needed and just so happen these libraries are loaded at runtime, so cargo will not add them to the needed list):

	packages = rec {
	  wgsl_app = rustPlatform.buildRustPackage {
	    pname = "wgsl";
	    version = "0.1.0";

	    src = ./.;
	    cargoLock = {
	      lockFile = ./Cargo.lock;
	    };
	    doCheck = false;

	    buildInputs = buildInputs ++ [overlay_pkgs.stdenv.cc.cc];
	    nativeBuildInputs = nativeBuildInputs ++ [overlay_pkgs.autoPatchelfHook];

	    postInstall = ''
	      patchelf --add-needed libxkbcommon-x11.so.0.0.0 $out/bin/${wgsl_app.pname}
	      patchelf --add-needed libwayland-client.so.0 $out/bin/${wgsl_app.pname}
	      patchelf --add-needed libvulkan.so.1 $out/bin/${wgsl_app.pname}
	      patchelf --set-rpath ${libPath} $out/bin/${wgsl_app.pname}
	    '';

	  };
	
	  default = wgsl_app;
	};

But it doesn’t make much sense when I am doing development, because I will have to manually patch the debug binary after running cargo build, of course it is possible with alias or shell script, but it just feel so weird.

So for your case, you need to know which is the main binary of your build and do run patchelf on the binary, adding rpath to it.

To check the existing existing metadata of your binary, you can use readelf command like so:


❯ readelf -d <your-binary-path>

File: /nix/store/5mh7kaj2fyv8mk4sfq1brwxgc02884wi-bash-5.2p37/bin/bash

Dynamic section at offset 0xdc720 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [/nix/store/65h17wjrrlsj2rj540igylrx7fqcd6vq-glibc-2.40-36/lib]
 0x000000000000000c (INIT)               0x41a000
 0x000000000000000d (FINI)               0x4ae1ac
 0x0000000000000019 (INIT_ARRAY)         0x4db270
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x4db278
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x0000000000000004 (HASH)               0x4003f8
 0x000000006ffffef5 (GNU_HASH)           0x403018
 0x0000000000000005 (STRTAB)             0x4108c0
 0x0000000000000006 (SYMTAB)             0x4060f0
 0x000000000000000a (STRSZ)              26823 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x4dc940
 0x0000000000000002 (PLTRELSZ)           4896 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x418140
 0x0000000000000007 (RELA)               0x418068
 0x0000000000000008 (RELASZ)             216 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000000000001e (FLAGS)              BIND_NOW
 0x000000006ffffffb (FLAGS_1)            Flags: NOW
 0x000000006ffffffe (VERNEED)            0x417f88
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x417188
 0x0000000000000000 (NULL)               0x0

The RUNPATH in the output is the one you want to patch, after you successfully run patchelf, you can do a readelf -d again to check. If your patch is correct, you will able to run your binary even you are not in your development environment.

In my opinion, patching the binary is essential if you want to package your application for nix, but if you just treat it as a development environment, and planning to compile it somewhere else then you might not need to patch it.

Refer to GitHub - NixOS/patchelf: A small utility to modify the dynamic linker and RPATH of ELF executables for patchelf instruction

1 Like