elixir-lang / elixir

Elixir is a dynamic, functional language for building scalable and maintainable applications
https://elixir-lang.org/
Apache License 2.0
24.26k stars 3.34k forks source link

escript: wrong path for NIF libraries #5444

Closed pmontrasio closed 7 years ago

pmontrasio commented 7 years ago

Environment

Erlang/OTP 19 [erts-8.1] [source] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Elixir 1.3.4
$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 8.0 (jessie)
Release:    8.0
Codename:   jessie

To provide some context, this is a Raspeberry PI 3 with an external WiFi card. The program I'm working on uses :wierl and :epcap to put that WiFi card in monitor mode and read packets. The :epcap module includes a shared library which is built as deps/procket/priv/procket.so (btw, the built in WiFi card doesn't support monitor mode, that's why I'm using an external one.)

Current behavior

The project directory is sensor and mix escript.build builds the app as sensor. It worked until I added the dependency from :epcap.

Then

$ ./sensor wlan1
11:22:03.317 [warn]  The on_load function for module procket returned {:error,
 {:load_failed,
  'Failed to load NIF library: \'/home/me/sensor/sensor/../priv/procket.so: cannot open
shared object file: Not a directory\''}}

** (UndefinedFunctionError) function :procket.socket/3 is undefined (module :procket
is not available)
    :procket.socket(:inet, :dgram, 0)
    src/wierl_config.erl:175: :wierl_config.down/1
    (sensor) lib/antenna.ex:4: Antenna.enter_monitor_mode/1
    (sensor) lib/sensor.ex:13: Sensor.start/1
    (elixir) lib/kernel/cli.ex:76: anonymous fn/3 in Kernel.CLI.exec_fun/2

Note that the path /home/me/sensor/sensor/../priv/procket.so is obviously wrong because of two reasons:

1) The second occurrence of sensor is actually a file (the app) and not a directory, and the path is wrong even if I manually create a priv dir under /home/me/sensor

2) The shared library is in deps/procket/priv/procket.so

There is another shared library in deps/epcap/priv/epcap_drv.so but it probably failed first on loading procket.so.

Running the application as a mix task or from iex -S mix works.

Expected behavior

Escript should load the shared library from its path /home/me/sensor/deps/procket/priv/procket.so

josevalim commented 7 years ago

Unfortunately that's a limitation of escripts. They cannot access anything in priv and therefore they cannot access embedded .so files. In other words, it is not currently possible to build escripts with NIFs in them.

MathiasWedeken commented 3 years ago

For me, it works if I copy the NIFs into the path that my command line app built with mix escript.build states in its error message. In my case, I'm using fast_xml which comes with two NIFs, fxml.so and fxml_stream.so.

Without any further action by me, using my app results in

[error] failed to load NIF /Users/...../priv/lib/fxml_stream: Failed to load NIF library: 'dlopen(/Users/......./priv/lib/fxml_stream.so, 2): image not found'

Copying the libs into /priv/lib, the app works as expected. It's a little crude for a production environment, but can probably be automatized with a shell script.