Skip to content

Load shared libraries locally#4876

Merged
ryanking13 merged 35 commits intopyodide:mainfrom
ryanking13:lib-no-global
Jul 30, 2024
Merged

Load shared libraries locally#4876
ryanking13 merged 35 commits intopyodide:mainfrom
ryanking13:lib-no-global

Conversation

@ryanking13
Copy link
Member

@ryanking13 ryanking13 commented Jun 20, 2024

Description

Fixes issues with symbol resolution and changes the behavior of loading shared libraries globally to local.

I found several libraries misconfigured while working on this: not having correct dependencies, so this PR fixes them as well.

Checklists

@hoodmane
Copy link
Member

hoodmane commented Jun 20, 2024

Thanks @ryanking13. The patch is so small for something that's caused us so much trouble!

}

// Step 2) try to find the library by searching child directories of the library directory
// (This should not be necessary in most cases, but some libraries have dependencies in the child directories)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is odd. Does normal linux have logic like this? Or does it have some different behavior with LD_LIBRARY_PATH or something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Locating the path of the shared library is handled two ways in linux.

  1. Using LD_LIBRARY_PATH
  2. Using rpath

In emscripten, LD_LIBRARY_PATH is used in _dlopen, but we are not using it.

rpath is not supported in emscripten. I made a proposal to add it before (#3854), I guess I need to open a same proposal in emscripten too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a // TODO: add rpath to Emscripten dsos and remove this logic?

@ryanking13
Copy link
Member Author

The patch is so small for something that's caused us so much trouble!

Yes it is. Actually, this one (emscripten-core/emscripten#18924) was a more critical fix (which is already included in Emscripten < 3.1.58), but it's also quite a small PR.

hoodmane pushed a commit that referenced this pull request Jun 21, 2024
Split off from #4876.

What this PR does is basically fixing incorrect shared library dependencies.

For instance, `cryptography` package depends on `openssl`, so the python
extension should link to `libssl.so`.

However, before this PR, it was not linked correctly.

```sh
$  pyodide auditwheel show cryptography-42.0.5-cp312-cp312-pyodide_2024_0_wasm32.whl
{'cryptography\\hazmat\\bindings\\_rust.cpython-312-wasm32-emscripten.so': []}
```

After this PR, it is linked correctly.

```bash
$ pyodide auditwheel show cryptography-42.0.5-cp312-cp312-pyodide_2024_0_wasm32.whl
{
│   'cryptography\\hazmat\\bindings\\_rust.cpython-312-wasm32-emscripten.so': [
│   │   'libssl.so',
│   │   'libcrypto.so'
│   ]
}
```

__Why do we need this change?__

Because it is how shared libraries should work.

__Why was this working before?__

Because we loaded dependencies globally. `openssl` is loaded globally, so its
symbols were visible to `cryptography` even if they are not linked. However,
#4876 will change all library loading mechanism to local, so all shared
libraries should now have correct dependencies to work.
Copy link
Member

@hoodmane hoodmane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ryanking13! Great work.

}

// Step 2) try to find the library by searching child directories of the library directory
// (This should not be necessary in most cases, but some libraries have dependencies in the child directories)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a // TODO: add rpath to Emscripten dsos and remove this logic?

@hoodmane
Copy link
Member

duckdb/test_duckdb.py::duckdb_with_pandas[firefox] times out a lot, we should xfail it.

@ryanking13
Copy link
Member Author

ryanking13 commented Jul 26, 2024

duckdb/test_duckdb.py::duckdb_with_pandas[firefox] times out a lot, we should xfail it.

Yes, agreed. Or increase the timeout.

@ryanking13
Copy link
Member Author

It seems like there is an additional error in the Scipy test suite. It's good to have a Scipy test so we can catch this.

2024-07-28T04:09:24.8596305Z Error: Dynamic linking error: cannot resolve symbol d_sign
2024-07-28T04:09:24.8597626Z     at stubs.<computed> (/home/runner/work/pyodide/pyodide/node_modules/pyodide/pyodide.asm.js:10:147647)
2024-07-28T04:09:24.8598748Z     at wasm://wasm/0003e6d6:wasm-function[97]:0x8d59
2024-07-28T04:09:24.8599535Z     at dynCall (/home/runner/work/pyodide/pyodide/node_modules/pyodide/pyodide.asm.js:10:145556)
2024-07-28T04:09:24.8600933Z     at /home/runner/work/pyodide/pyodide/node_modules/pyodide/pyodide.asm.js:10:145675
2024-07-28T04:09:24.8602645Z     at stubs.<computed> (/home/runner/work/pyodide/pyodide/node_modules/pyodide/pyodide.asm.js:10:147720)
2024-07-28T04:09:24.8603878Z     at wasm://wasm/0003e6d6:wasm-function[89]:0x2991
2024-07-28T04:09:24.8604701Z     at wasm://wasm/0003e6d6:wasm-function[107]:0xbfa0
2024-07-28T04:09:24.8605738Z     at wasm://wasm/0267c526:wasm-function[1143]:0x1c3691
2024-07-28T04:09:24.8606739Z     at wasm://wasm/0267c526:wasm-function[1144]:0x1c36c2
2024-07-28T04:09:24.8607550Z     at wasm://wasm/0267c526:wasm-function[3406]:0x29e6f6 {
2024-07-28T04:09:24.8608040Z   pyodide_fatal_error: true
2024-07-28T04:09:24.8608321Z }

@ryanking13
Copy link
Member Author

The d_sign symbol comes from openblas, and 13 modules in scipy are importing it:

scipy/integrate/_dop.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/integrate/_lsoda.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/integrate/_odepack.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/integrate/_vode.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/interpolate/dfitpack.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/odr/__odrpack.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/optimize/_slsqp.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/special/_specfun.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/special/_ufuncs.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/special/cython_special.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/sparse/linalg/_propack/_dpropack.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/sparse/linalg/_propack/_zpropack.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign
scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-312-wasm32-emscripten.so:
       env      FUNC	d_sign

and among them, _dop, _slsqp, and _specfun modules are not linked to openblas correctly.

│   'scipy/integrate/_dop.cpython-312-wasm32-emscripten.so': [],
│   'scipy/integrate/_lsoda.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/integrate/_odepack.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/integrate/_vode.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/interpolate/dfitpack.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/odr/__odrpack.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/optimize/_slsqp.cpython-312-wasm32-emscripten.so': [],
│   'scipy/special/_specfun.cpython-312-wasm32-emscripten.so': [],
│   'scipy/special/_ufuncs.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/special/cython_special.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/sparse/linalg/_propack/_dpropack.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/sparse/linalg/_propack/_zpropack.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],
│   'scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-312-wasm32-emscripten.so': ['libopenblas.so'],

@ryanking13
Copy link
Member Author

Okay, I think I figured out what is happening.

  1. There are some symbols that come from libf2c.a, and scipy depends on it.
  2. Currently, these symbols are provided to scipy modules indirectly, libf2c.a is statically linked to openblas.so and openblas.so is linked to scipy modules.
  3. Some of scipy modules like _dop.so require symbols from libf2c.a, however they do not require symbols from openblas.so, so they are not linked to openblas.so.

The possible workarounds would be:

  • Linking openblas.so to those modules or
  • Linking libf2c.a to those modules

cc: @lesteve

@hoodmane
Copy link
Member

Why not link openblas to these? Seems like the most reasonable approach to me.

@lesteve
Copy link
Contributor

lesteve commented Jul 29, 2024

It's good to have a Scipy test so we can catch this.

Great to hear that ❤️

Other than this, no strong opinion/expertise on this so do whatever you think makes sense. I guess at the time I worked on OpenBLAS packaging I tried a few things and stuck with the first thing I managed to get working (probably #3331 (comment) is the most relevant comment related to this) ...

@ryanking13
Copy link
Member Author

ryanking13 commented Jul 29, 2024

Why not link openblas to these? Seems like the most reasonable approach to me.

Yes, this patch will link those modules to openblas. We may even link openblas.so to every C-extension modules, which would make our life easier... but it may impact the overall load time, so let me see if I can locate all fortran-based C-extension modules and link to them

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants