2

Failing to compile an amazing piece of code by Henri Menke, he pointed me out in comments that ffi was maybe not able to find the right dynamic library on my system.

Here is a MCE to work with (must be compiled with lualatex --shell-escape).

\documentclass{standalone}
\usepackage{luacode}
\begin{luacode*}
  local ffi = require("ffi")
  local gsl = ffi.load("gsl")
\end{luacode*}
\begin{document}
Test
\end{document}

I'm running an up-to-date vanilla TeXLive 2019 on archlinux. The library (gsl) is installed in /usr/lib/ by my package manager:

christophe@plutonium ~ % ls -1 /usr/lib/libgsl*
/usr/lib/libgslcblas.so
/usr/lib/libgslcblas.so.0
/usr/lib/libgslcblas.so.0.0.0
/usr/lib/libgsl.so
/usr/lib/libgsl.so.25
/usr/lib/libgsl.so.25.0.0

Header files are in /usr/include/gsl/ (see file list).

For now, I'm getting this error:

[\directlua]:2: could not load library gsl
stack traceback:
    [C]: in function 'FFISUPPORTED'
    [\directlua]:2: in main chunk.
\luacode@dbg@exec ...code@maybe@printdbg {#1} #1 }

l.6 \end{luacode*}

My questions are:

  1. Where does ffi.load looks by default for the library (here gsl).
  2. Is there a way to configure this default path globally in TeXLive?
  3. Why does an explicit path like `ffi.load("/usr/lib/gsl") does not work either?

By the way with a slightly different error message:

[\directlua]:2: could not load library /usr/lib/gsl
stack traceback:
    [C]: in function 'ffi.load'
    [\directlua]:2: in main chunk.
\luacode@dbg@exec ...code@maybe@printdbg {#1} #1 }

l.6 \end{luacode*}

Update after Henri's comment.

Here is a complete view of the issue.

christophe@plutonium /tmp % cat t.tex 
\documentclass{standalone}
\usepackage{luacode}
\begin{luacode*}
  local ffi = require("ffi")
  local gsl = ffi.load("/usr/lib/libgsl.so")
\end{luacode*}
\begin{document}
Test
\end{document}
% Local Variables:
% coding: utf-8-unix
% TeX-engine: luatex
% End:
christophe@plutonium /tmp % luatex --version
This is LuaTeX, Version 1.10.0 (TeX Live 2019)

Execute  'luatex --credits'  for credits and version details.

There is NO warranty. Redistribution of this software is covered by
the terms of the GNU General Public License, version 2 or (at your option)
any later version. For more information about these matters, see the file
named COPYING and the LuaTeX source.

LuaTeX is Copyright 2019 Taco Hoekwater and the LuaTeX Team.

christophe@plutonium /tmp % luatex --credits
This is LuaTeX, Version 1.10.0 (TeX Live 2019)

The LuaTeX team is Hans Hagen, Hartmut Henkel, Taco Hoekwater, Luigi Scarso.

LuaTeX merges and builds upon (parts of) the code from these projects:

tex       : Donald Knuth
etex      : Peter Breitenlohner, Phil Taylor and friends
omega     : John Plaice and Yannis Haralambous
aleph     : Giuseppe Bilotta
pdftex    : Han The Thanh and friends
kpathsea  : Karl Berry, Olaf Weber and others
lua       : Roberto Ierusalimschy, Waldemar Celes and Luiz Henrique de Figueiredo
metapost  : John Hobby, Taco Hoekwater, Luigi Scarso, Hans Hagen and friends
pplib     : Paweł Jackowski
fontforge : George Williams (partial)
luajit    : Mike Pall (used in LuajitTeX)

Compiled with libpng 1.6.36; using 1.6.36
Compiled with lua version 5.3.5
Compiled with mplib version 2.00
Compiled with zlib 1.2.11; using 1.2.11

Development id: 7127

christophe@plutonium /tmp % ls /usr/lib/*gsl*
/usr/lib/libgslcblas.so  /usr/lib/libgslcblas.so.0  /usr/lib/libgslcblas.so.0.0.0  /usr/lib/libgsl.so  /usr/lib/libgsl.so.25  /usr/lib/libgsl.so.25.0.0
christophe@plutonium /tmp % lualatex --shell-escape t.tex
This is LuaTeX, Version 1.10.0 (TeX Live 2019) 
 system commands enabled.
(./t.tex
LaTeX2e <2019-10-01> patch level 2
tput: unknown terminal "xterm-termite"

luaotfload | main : initialization completed in 0.138 seconds
(/usr/local/texlive/2019/texmf-dist/tex/latex/standalone/standalone.cls
Document Class: standalone 2018/03/26 v1.3a Class to compile TeX sub-files stan
dalone
(/usr/local/texlive/2019/texmf-dist/tex/latex/tools/shellesc.sty)
(/usr/local/texlive/2019/texmf-dist/tex/generic/iftex/ifluatex.sty
(/usr/local/texlive/2019/texmf-dist/tex/generic/iftex/iftex.sty))
(/usr/local/texlive/2019/texmf-dist/tex/latex/xkeyval/xkeyval.sty
(/usr/local/texlive/2019/texmf-dist/tex/generic/xkeyval/xkeyval.tex
(/usr/local/texlive/2019/texmf-dist/tex/generic/xkeyval/xkvutils.tex
(/usr/local/texlive/2019/texmf-dist/tex/generic/xkeyval/keyval.tex))))
(/usr/local/texlive/2019/texmf-dist/tex/latex/standalone/standalone.cfg)
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/article.cls
Document Class: article 2019/10/25 v1.4k Standard LaTeX document class
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2019/texmf-dist/tex/generic/luatex85/luatex85.sty)
(/usr/local/texlive/2019/texmf-dist/tex/generic/oberdiek/pdftexcmds.sty
(/usr/local/texlive/2019/texmf-dist/tex/generic/oberdiek/infwarerr.sty)
(/usr/local/texlive/2019/texmf-dist/tex/generic/oberdiek/ltxcmds.sty)
(/usr/local/texlive/2019/texmf-dist/tex/generic/iftex/ifpdf.sty)))
(/usr/local/texlive/2019/texmf-dist/tex/lualatex/luacode/luacode.sty
(/usr/local/texlive/2019/texmf-dist/tex/luatex/luatexbase/luatexbase.sty
(/usr/local/texlive/2019/texmf-dist/tex/luatex/ctablestack/ctablestack.sty)))[\
directlua]:2: could not load library /usr/lib/libgsl.so
stack traceback:
    [C]: in function 'ffi.load'
    [\directlua]:2: in main chunk.
\luacode@dbg@exec ...code@maybe@printdbg {#1} #1 }

l.6 \end{luacode*}


? 

Update after second comment.

christophe@plutonium /tmp % LD_DEBUG=libs lualatex --shell-escape --interaction=batchmode t.tex   
      2773: find library=libdl.so.2 [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:   trying file=/usr/lib/libdl.so.2
      2773: 
      2773: find library=libm.so.6 [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:   trying file=/usr/lib/libm.so.6
      2773: 
      2773: find library=libc.so.6 [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:   trying file=/usr/lib/libc.so.6
      2773: 
      2773: 
      2773: calling init: /usr/lib/libc.so.6
      2773: 
      2773: 
      2773: calling init: /usr/lib/libm.so.6
      2773: 
      2773: 
      2773: calling init: /usr/lib/libdl.so.2
      2773: 
      2773: 
      2773: initialize program: lualatex
      2773: 
      2773: 
      2773: transferring control: lualatex
      2773: 
      2773: find library=libc.so [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:  search path=/usr/lib/tls/haswell/x86_64:/usr/lib/tls/haswell:/usr/lib/tls/x86_64:/usr/lib/tls:/usr/lib/haswell/x86_64:/usr/lib/haswell:/usr/lib/x86_64:/usr/lib        (system search path)
      2773:   trying file=/usr/lib/tls/haswell/x86_64/libc.so
      2773:   trying file=/usr/lib/tls/haswell/libc.so
      2773:   trying file=/usr/lib/tls/x86_64/libc.so
      2773:   trying file=/usr/lib/tls/libc.so
      2773:   trying file=/usr/lib/haswell/x86_64/libc.so
      2773:   trying file=/usr/lib/haswell/libc.so
      2773:   trying file=/usr/lib/x86_64/libc.so
      2773:   trying file=/usr/lib/libc.so
      2773: find library=libgcc.so [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:  search path=/usr/lib       (system search path)
      2773:   trying file=/usr/lib/libgcc.so
      2773: 
      2773: find library=libm.so [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:  search path=/usr/lib       (system search path)
      2773:   trying file=/usr/lib/libm.so
      2773: find library=libdl.so [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:   trying file=/usr/lib/libdl.so
      2773: 
This is LuaTeX, Version 1.10.0 (TeX Live 2019) 
 system commands enabled.
      2775: find library=libreadline.so.8 [0]; searching
      2775:  search cache=/etc/ld.so.cache
      2775:   trying file=/usr/lib/libreadline.so.8
      2775: 
      2775: find library=libdl.so.2 [0]; searching
      2775:  search cache=/etc/ld.so.cache
      2775:   trying file=/usr/lib/libdl.so.2
      2775: 
      2775: find library=libc.so.6 [0]; searching
      2775:  search cache=/etc/ld.so.cache
      2775:   trying file=/usr/lib/libc.so.6
      2775: 
      2775: find library=libncursesw.so.6 [0]; searching
      2775:  search cache=/etc/ld.so.cache
      2775:   trying file=/usr/lib/libncursesw.so.6
      2775: 
      2775: 
      2775: calling init: /usr/lib/libc.so.6
      2775: 
      2775: 
      2775: calling init: /usr/lib/libncursesw.so.6
      2775: 
      2775: 
      2775: calling init: /usr/lib/libdl.so.2
      2775: 
      2775: 
      2775: calling init: /usr/lib/libreadline.so.8
      2775: 
      2775: 
      2775: initialize program: sh
      2775: 
      2775: 
      2775: transferring control: sh
      2775: 
      2775: find library=libtinfow.so.6 [0]; searching
      2775:  search path=/home/christophe/anaconda3/bin/../lib/tls/haswell/x86_64:/home/christophe/anaconda3/bin/../lib/tls/haswell:/home/christophe/anaconda3/bin/../lib/tls/x86_64:/home/christophe/anaconda3/bin/../lib/tls:/home/christophe/anaconda3/bin/../lib/haswell/x86_64:/home/christophe/anaconda3/bin/../lib/haswell:/home/christophe/anaconda3/bin/../lib/x86_64:/home/christophe/anaconda3/bin/../lib        (RPATH from file tput)
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/tls/haswell/x86_64/libtinfow.so.6
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/tls/haswell/libtinfow.so.6
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/tls/x86_64/libtinfow.so.6
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/tls/libtinfow.so.6
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/haswell/x86_64/libtinfow.so.6
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/haswell/libtinfow.so.6
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/x86_64/libtinfow.so.6
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/libtinfow.so.6
      2775: 
      2775: find library=libc.so.6 [0]; searching
      2775:  search path=/home/christophe/anaconda3/bin/../lib      (RPATH from file tput)
      2775:   trying file=/home/christophe/anaconda3/bin/../lib/libc.so.6
      2775:  search cache=/etc/ld.so.cache
      2775:   trying file=/usr/lib/libc.so.6
      2775: 
      2775: 
      2775: calling init: /usr/lib/libc.so.6
      2775: 
      2775: 
      2775: calling init: /home/christophe/anaconda3/bin/../lib/libtinfow.so.6
      2775: 
      2775: 
      2775: initialize program: tput
      2775: 
      2775: 
      2775: transferring control: tput
      2775: 
tput: unknown terminal "xterm-termite"
      2775: 
      2775: calling fini: tput [0]
      2775: 
      2775: 
      2775: calling fini: /home/christophe/anaconda3/bin/../lib/libtinfow.so.6 [0]
      2775: 

luaotfload | main : initialization completed in 0.134 seconds
      2773: find library=gsl [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:  search path=/usr/lib       (system search path)
      2773:   trying file=/usr/lib/gsl
      2773: 
      2773: find library=gsl.so [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:  search path=/usr/lib       (system search path)
      2773:   trying file=/usr/lib/gsl.so
      2773: 
      2773: find library=libgsl.so [0]; searching
      2773:  search cache=/etc/ld.so.cache
      2773:   trying file=/usr/lib/libgsl.so
      2773: 
      2773: /usr/lib/libgsl.so: error: symbol lookup error: undefined symbol: cblas_ctrmv (fatal)
      2773: 
      2773: calling fini: lualatex [0]
      2773: 
      2773: 
      2773: calling fini: /usr/lib/libdl.so.2 [0]
      2773: 
      2773: 
      2773: calling fini: /usr/lib/libm.so.6 [0]
4
  • ffi.load("/usr/lib/gsl") is wrong. There is no file called gsl. Try ffi.load("/usr/lib/libgsl.so"). Nov 9 '19 at 23:18
  • @HenriMenke I get [\directlua]:2: could not load library /usr/lib/libgsl.so stack traceback:[C]: in function 'FFISUPPORTED' [\directlua]:2: in main chunk. \luacode@dbg@exec ...code@maybe@printdbg {#1} #1 }. BTW, how ffi.load("gsl") is supposed to resolve to /some/path/to/libgsl?
    – cjorssen
    Nov 10 '19 at 9:04
  • That is not possible. Are you running with --shell-escape? That is mandatory for ffi. Nov 10 '19 at 9:09
  • Could you show the output of LD_DEBUG=libs lualatex --shell-escape --interaction=batchmode test.tex? That should print debug information for library loading. The --interaction=batchmode will suppress TeX log output. Nov 11 '19 at 4:16
5

The default paths are just your regular system default library paths. Technically the name is passed (at least on UNIX-like systems like Linux) to the system function dlopen. This function is responsible for searching the library and it's behaviour is documented in it's man page DLOPEN(3). (On Windows the function is called LoadLibraryA)

But you should never have to worry about it, just put the libraries where all your normal libraries are and you will be fine. You can also pass a path, but then you need to use the right filename: There is no file named /usr/lib/gsl, the library is in /usr/lib/libgsl.so. (This is automatically inserted if you omit the path)

But in your case, the problem isn't that the library isn't found, the problem is that the library can't be loaded alone:

On your system, the GSL always has to be loaded together with a CBLAS implementation, for example gslcblas which is shipped with GSL. (See the documentation for details on linking GSL, especially on how to load another BLAS implementation to get better performance)

As Henri Menke mentioned, this problem is not necessary on all platforms, especially Debian links the library in a way which ensures that this is not necessary. But even to Debian users I would recommend to always load a CBLAS implementation manually: This makes your document more portable and ensures that it can also be compiled on other systems.

So your code works if you add the additional library:

\documentclass{standalone}
\usepackage{luacode}
\begin{luacode*}
  local ffi = require("ffi")
  ffi.load("gslcblas", true) -- Make the symbols available globally
  local gsl = ffi.load"gsl"
\end{luacode*}
\begin{document}
Test
\end{document}
6
  • 1
    At least on most Linux systems it's enforced because GSL isn't linked to BLAS in any way, so not loading a BLAS library leads to dynamic linker errors. But even on other systems I would recommend loading BLAS given that it is documented as a requirement: "To link against the library you need to specify both the main library and a supporting CBLAS library, ..." Nov 11 '19 at 14:18
  • @MarcelKrüger Indeed, although I believe that this is a bug in Arch Linux packaging, because on Debian (and derivatives) I can dynamically load libgsl.so without libgslcblas.so, which works as long I don't use any symbols from libgslcblas.so. Please edit your answer, so I can convert my downvote into an upvote. Nov 11 '19 at 19:57
  • @MarcelKrüger Oh, it is actually not a bug in Arch Linux packaging but a feature in Debian packaging: salsa.debian.org/edd/gsl/blob/master/debian/patches/… Nov 11 '19 at 20:02
  • @MarcelKrüger Why does gslcblas need to be ffi.loaded with true and not gsl?
    – cjorssen
    Nov 11 '19 at 20:35
  • Basically ffi could "hide" the symbols from other libraries, such that e.g. gsl can't see the symbols from gslcblas and gslcblas can only be accessed through the return value form ffi.load. That would be ad because we want gsl to use these symbols automatically, so we set the parameter true to load them globally. (At least on my system this isn't strictly required but it is safer to set it) For gsl this isn't necessary because we only access it though the returned variable gsl anyway. Nov 11 '19 at 20:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.