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

Python bindings should use -undefined dynamic_lookup when Py_ENABLE_SHARED is 0 #92

Closed
leonardt opened this issue Oct 5, 2020 · 18 comments

Comments

@leonardt
Copy link
Contributor

leonardt commented Oct 5, 2020

Walked through the README steps and ran into a seg fault trying to run the test suite:

~/repos/pono master*
base ❯ pytest ./tests
===================================================================== test session starts ======================================================================
platform darwin -- Python 3.8.3, pytest-6.0.0, py-1.9.0, pluggy-0.13.1
rootdir: /Users/lenny/repos/pono
plugins: pycodestyle-2.2.0
collecting ... Fatal Python error: Segmentation fault

Current thread 0x000000011de01dc0 (most recent call first):
  File "<frozen importlib._bootstrap>", line 219 in _call_with_frames_removed
  File "<frozen importlib._bootstrap_external>", line 1101 in create_module
  File "<frozen importlib._bootstrap>", line 556 in module_from_spec
  File "<frozen importlib._bootstrap>", line 657 in _load_unlocked
  File "<frozen importlib._bootstrap>", line 975 in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 991 in _find_and_load
  File "/Users/lenny/repos/pono/tests/python/test_encoders.py", line 2 in <module>
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/assertion/rewrite.py", line 170 in exec_module
  File "<frozen importlib._bootstrap>", line 671 in _load_unlocked
  File "<frozen importlib._bootstrap>", line 975 in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 991 in _find_and_load
  File "<frozen importlib._bootstrap>", line 1014 in _gcd_import
  File "/Users/lenny/miniconda3/lib/python3.8/importlib/__init__.py", line 127 in import_module
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/pathlib.py", line 520 in import_path
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/python.py", line 552 in _importtestmodule
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/python.py", line 484 in _getobj
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/python.py", line 288 in obj
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/python.py", line 500 in _inject_setup_module_fixture
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/python.py", line 487 in collect
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/runner.py", line 324 in <lambda>
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/runner.py", line 294 in from_call
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/runner.py", line 324 in pytest_make_collect_report
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/callers.py", line 187 in _multicall
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/manager.py", line 84 in <lambda>
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/manager.py", line 93 in _hookexec
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/hooks.py", line 286 in __call__
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/runner.py", line 441 in collect_one_node
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/main.py", line 768 in genitems
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/main.py", line 568 in _perform_collect
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/main.py", line 516 in perform_collect
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/main.py", line 306 in pytest_collection
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/callers.py", line 187 in _multicall
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/manager.py", line 84 in <lambda>
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/manager.py", line 93 in _hookexec
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/hooks.py", line 286 in __call__
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/main.py", line 295 in _main
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/main.py", line 240 in wrap_session
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/main.py", line 289 in pytest_cmdline_main
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/callers.py", line 187 in _multicall
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/manager.py", line 84 in <lambda>
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/manager.py", line 93 in _hookexec
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/pluggy/hooks.py", line 286 in __call__
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/config/__init__.py", line 157 in main
  File "/Users/lenny/miniconda3/lib/python3.8/site-packages/_pytest/config/__init__.py", line 180 in console_main
  File "/Users/lenny/miniconda3/bin/pytest", line 8 in <module>
zsh: segmentation fault  pytest ./tests

I traced the issue to the import pono step, here's a backtrace produced by lldb:

~/repos/pono master*
base ❯ lldb -- ~/miniconda3/bin/python -c "import pono"
(lldb) target create "/Users/lenny/miniconda3/bin/python"
Current executable set to '/Users/lenny/miniconda3/bin/python' (x86_64).
(lldb) settings set -- target.run-args  "-c" "import pono"
(lldb) r
Process 98745 launched: '/Users/lenny/miniconda3/bin/python' (x86_64)
Process 98745 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x8)
    frame #0: 0x0000000104613797 libpython3.8.dylib`PyType_Ready + 455
libpython3.8.dylib`PyType_Ready:
->  0x104613797 <+455>: movq   0x8(%rcx), %rdx
    0x10461379b <+459>: movq   %rax, (%rdx)
    0x10461379e <+462>: movq   -0x8(%rbx), %rsi
    0x1046137a2 <+466>: andl   $0x3, %esi
Target 0: (python) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x8)
  * frame #0: 0x0000000104613797 libpython3.8.dylib`PyType_Ready + 455
    frame #1: 0x000000010461368f libpython3.8.dylib`PyType_Ready + 191
    frame #2: 0x00000001045f6e99 libpython3.8.dylib`PyModuleDef_Init + 25
    frame #3: 0x00000001001a7645 python`_PyImport_LoadDynamicModuleWithSpec + 565
    frame #4: 0x00000001001a64e5 python`_imp_create_dynamic + 181
    frame #5: 0x000000010008aea4 python`cfunction_vectorcall_FASTCALL + 84
    frame #6: 0x000000010002dbd9 python`PyVectorcall_Call + 121
    frame #7: 0x0000000100164d6f python`_PyEval_EvalFrameDefault + 47711
    frame #8: 0x000000010015752d python`_PyEval_EvalCodeWithName + 557
    frame #9: 0x000000010002e8ca python`_PyFunction_Vectorcall + 426
    frame #10: 0x000000010016430b python`_PyEval_EvalFrameDefault + 45051
    frame #11: 0x000000010002e818 python`_PyFunction_Vectorcall + 248
    frame #12: 0x0000000100164216 python`_PyEval_EvalFrameDefault + 44806
    frame #13: 0x000000010002e818 python`_PyFunction_Vectorcall + 248
    frame #14: 0x000000010016469b python`_PyEval_EvalFrameDefault + 45963
    frame #15: 0x000000010002e818 python`_PyFunction_Vectorcall + 248
    frame #16: 0x000000010016469b python`_PyEval_EvalFrameDefault + 45963
    frame #17: 0x000000010002e818 python`_PyFunction_Vectorcall + 248
    frame #18: 0x000000010016469b python`_PyEval_EvalFrameDefault + 45963
    frame #19: 0x000000010002e818 python`_PyFunction_Vectorcall + 248
    frame #20: 0x0000000100030686 python`object_vacall + 374
    frame #21: 0x000000010003090d python`_PyObject_CallMethodIdObjArgs + 237
    frame #22: 0x00000001001a060e python`PyImport_ImportModuleLevelObject + 1342
    frame #23: 0x00000001001618df python`_PyEval_EvalFrameDefault + 34255
    frame #24: 0x000000010015752d python`_PyEval_EvalCodeWithName + 557
    frame #25: 0x00000001001d2a03 python`PyRun_StringFlags + 259
    frame #26: 0x00000001001d2852 python`PyRun_SimpleStringFlags + 82
    frame #27: 0x00000001001f7ba6 python`pymain_run_command + 134
    frame #28: 0x00000001001f6bdd python`pymain_run_python + 477
    frame #29: 0x00000001001f69a5 python`Py_RunMain + 37
    frame #30: 0x00000001001f7f41 python`pymain_main + 49
    frame #31: 0x0000000100000d68 python`main + 56
    frame #32: 0x00007fff67ed7cc9 libdyld.dylib`start + 1
(lldb)

Have you seen anything like this before? I'm going to try rebuilding from scratch to make sure I didn't mess up a step.

@makaimann
Copy link
Collaborator

Hmm I might have seen something similar to this before both in pono and CVC4. In the past, it had to do with the Python binary that was found not being the same version as the default Python libraries that Cython found to link against. I think it specifically showed up because of miniconda actually. I never found a good solution for this, but can look again. I would appreciate anything that you notice. We just had people build without miniconda and make sure they were using the right Python version.

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Hmm, that makes sense, it might be related to which Python library it's finding for the build (sounds like it's not the miniconda one). I'll poke around to see if I can get it to point to that one.

@makaimann
Copy link
Collaborator

I think it's related to this: conda-forge/python-feedstock#272. Still looking into it

@makaimann
Copy link
Collaborator

makaimann commented Oct 5, 2020

Hmm, that makes sense, it might be related to which Python library it's finding for the build (sounds like it's not the miniconda one). I'll poke around to see if I can get it to point to that one.

When you configure, it should print out the Python binary and libraries that it found. Although, I'm looking at some previous correspondence, and it seems like even if we forced it to be the same we were still getting the segfault. So I'm leaning towards it being the static linking issue linked above.

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Just to confirm, the configure script seems to be picking up the right python:

-- Found PythonInterp: /Users/lenny/miniconda3/bin/python3 (found suitable version "3.8.3", minimum required is "3")
-- Found PythonInterp: /Users/lenny/miniconda3/bin/python3 (found version "3.8.3")
-- Found Cython: /Users/lenny/miniconda3/bin/cython (Required is at least version "0.29")
-- Found PythonLibs: /Users/lenny/miniconda3/lib/libpython3.8.dylib

I'll try playing around with the linker flags to see if I can fix it

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Poking around the cmake scripts, I don't see any references to python libraries/linking directly. Could this be inside the Cython package logic?

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Inside build/CMakeCache.txt it seems like it's getting the right executable CYTHON_EXECUTABLE:FILEPATH=/Users/lenny/miniconda3/bin/cython

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Ah, it's probably happening inside python_extension_module(pono)

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

I tried removing the explicit linking of the libpython in python/CMakeFiles/pono.dir/link.txt and replaced it with -undefined dynamic_lookup as per some of the linked solutions in that thread but that didn't seem to help

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Success! Turns out I had to add -undefined dynamic_lookup to the link.txt in smt-switch as well.

So it looks like the issue is resolved by fixing the linking logic. I can help look into how to configure the cmake build to do this automatically, but for now I'm going to move forward with a local build that works. We may have to submit a patch to scikit build which I think owns the cmake package for the python/cython extensions

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

For now, in case anyone needs the workaround, in build/python/CMakeFiles/pono.dir/link.txt and build/python/CMakeFiles/smt_switch.dir/link.txt I replaced /Users/lenny/miniconda3/lib/libpython3.8.dylib (the explicitly linked libpython) with -undefined dynamic_lookup

@leonardt leonardt changed the title Trouble building pono python bindings Python bindings should use -undefined dynamic_lookup when Py_ENABLE_SHARED is 0 Oct 5, 2020
@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Note from the thread that makai linked above, the -undefined dynamic_lookup flag comes from the output of python -c "import sysconfig; print(sysconfig.get_config_var('LDSHARED'))", using the output of that config var may be a better generic solution.

@makaimann
Copy link
Collaborator

Thanks for looking into this @leonardt! It's much appreciated. I'll poke around a bit more to understand if this is something we can resolve in CMake scripts or if it needs to be changed in scikit.

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

It looks like they have some logic related to this:
https://github.com/scikit-build/scikit-build/blob/master/skbuild/resources/cmake/targetLinkLibrariesWithDynamicLookup.cmake

but I'm not sure if it's used in the package flow for this build

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Hmm, seems like it should be using it: https://github.com/scikit-build/scikit-build/blob/afa978814325336ac318316ded64e3a82cf3bcf7/skbuild/resources/cmake/FindPythonExtensions.cmake#L427 I wonder if I'm using an older version of cmake that doesn't have this logic?

@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Actually that line was added 4 years ago so that seems unlikely

@makaimann
Copy link
Collaborator

Yeah that's a while ago. What version of cmake are you using? I could always bump up the required version if that is the problem after all.

leonardt added a commit to leonardt/smt-switch that referenced this issue Oct 5, 2020
Fixes stanford-centaur/pono#92

It seems that the `target_link_libraries_with_dynamic_lookup` logic
doesn't properly discover this requirement on my version of macOS
(10.15.6) and conda (conda 4.8.3).

This uses the cmake command from
conda-forge/python-feedstock#272 to read
Py_ENABLE_SHARED.  If this config var isn't set, we use `-undefined
dynamic_lookup` rather than linking the python library directly
@leonardt
Copy link
Contributor Author

leonardt commented Oct 5, 2020

Fixed by stanford-centaur/smt-switch#107

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 a pull request may close this issue.

2 participants