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

[Updated] Append to RPATH (instead of overwrite) #245

Merged
merged 20 commits into from
Jun 22, 2020

Conversation

fbrennen
Copy link
Contributor

@fbrennen fbrennen commented Jun 5, 2020

Closes #137 (also closes #69 as far as I can tell)

A refreshed version of work done by @thomaslima in #140

@codecov
Copy link

codecov bot commented Jun 5, 2020

Codecov Report

Merging #245 into master will increase coverage by 0.49%.
The diff coverage is 93.47%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #245      +/-   ##
==========================================
+ Coverage   89.72%   90.21%   +0.49%     
==========================================
  Files          20       20              
  Lines        1012     1053      +41     
  Branches      215      223       +8     
==========================================
+ Hits          908      950      +42     
+ Misses         64       61       -3     
- Partials       40       42       +2     
Impacted Files Coverage Δ
auditwheel/patcher.py 95.89% <93.33%> (+5.56%) ⬆️
auditwheel/repair.py 83.09% <100.00%> (ø)
auditwheel/genericpkgctx.py 44.44% <0.00%> (+4.44%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a9b4794...c0bb677. Read the comment docs.

@auvipy auvipy requested a review from lkollar June 5, 2020 08:08
@@ -61,6 +61,10 @@ def set_rpath(self,
file_name: str,
rpath: str) -> None:

old_rpath = check_output(['patchelf', '--print-rpath',
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this logic should not live in this method as other implementations of ElfPatcher in the future will not work consistently (my understanding is that having to append the RPATH is not a patchelf quirk, we should always do it). set_rpath should just set the provided RPATH.

We should either have get_rpath and append_rpath methods on the interface, or have an append flag which can trigger this behaviour.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good -- if you don't have a particular preference I'll extend the interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How's this?

@fbrennen
Copy link
Contributor Author

fbrennen commented Jun 5, 2020

I don't quite understand how to parse the coverage failures -- it looks like adding to the ElfPatcher interface has reduced coverage, but there are no tests for that at all. I exempted NotImplementedError from the coverage calculation and that addressed the issue.

@lkollar
Copy link
Contributor

lkollar commented Jun 5, 2020

Thanks @fbrennen, the change looks good. I thought about this a bit more and, still have a question about the overall change: what happens if an extension has a relative RPATH to some library, and this library is present on the system where the wheel is installed? I can imagine a situation where the wrong library is picked up from the system, instead of the vendored library. For example, liba.so has RUNPATH=/opt/lib/ and after we graft it into the wheel, it will still pick up dependencies from that location if they are present. This is not a problem at the moment because we clear out the RUNPATH, "isolating" the dependency. What do you think?

@fbrennen
Copy link
Contributor Author

fbrennen commented Jun 5, 2020

I see what you're saying -- the core issue that I (and I think @thomaslima ) have is that the RPATHS for libraries that are already in the wheel are getting trampled -- the place where those RPATHs are adjusted is separate from the one applied to generic libraries grafted into the wheel. I could make the first one use append and the second use set -- that way you're preserving RPATH entries only for libraries you've already explicitly included. How does that sound?

@lkollar
Copy link
Contributor

lkollar commented Jun 6, 2020

Yes, I think that works. However, I would like to better understand the problem before we make this change, just to make sure we are not missing something here, as this is quite a significant change in existing logic. See #134 (comment) as well. Sorry for holding up the PR, I'll try to report back ASAP.

@lkollar lkollar self-assigned this Jun 6, 2020
@fbrennen
Copy link
Contributor Author

fbrennen commented Jun 6, 2020

Not a problem -- I appreciate this could have unexpected consequences.

@fbrennen
Copy link
Contributor Author

fbrennen commented Jun 6, 2020

Ah, I see that the first RPATH adjustment is done inside an InWheelCtx -- I imagine it would not be hard to use that to require that preserved RPATH entries only point to paths within the wheel.

@lkollar
Copy link
Contributor

lkollar commented Jun 7, 2020

I looked into this a bit more and I think we have to address the following issues:

  1. We need to avoid the RPATH pointing to libraries outside of the wheel. This is not a problem at the moment as we always remove the existing RPATH, but the append logic can cause issues if the existing RPATH points to a location outside of the wheel. I think the best solution for this is to actually resolve the existing RPATH (replacing tokens like $ORIGIN as described in the "Rpath token expansion" section in the ld.so man page) and strip it if it points outside the wheel. We can also consider only appending the RPATH of the extension modules, but this still removes the guarantee we provide today, that an existing RPATH on the extension will be stripped. This can cause issues.
  2. If we blindly append the RPATH, multiple auditwheel invocations can add the same RPATH multiple times. We should check if what we are appending is already present.

I think given these, the best solution would be to create a function which is used to append the RPATH of Python extensions (but not other .sos), but it also checks if the RPATH is already present, and resolves the existing RPATH or RUNPATH entries into "real" paths and strips them if they point outside of the wheel. How does this sound @fbrennen?

@fbrennen
Copy link
Contributor Author

fbrennen commented Jun 7, 2020

That sound good to me, and not too hard to do -- I'll adjust append_rpath() to additionally require the main wheel directory, which should make it straightforward to preserve only "safe" RPATH entries. We can deal with duplicate entries at the same time. auditwheel already isolates the Python extensions and deals with them separately to other libraries, so that will come for free.

@fbrennen
Copy link
Contributor Author

Hi @lkollar -- sorry for the delay. Here's an initial go at implementing what I think you were after. What do you think?

Copy link
Contributor

@lkollar lkollar left a comment

Choose a reason for hiding this comment

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

Hey @fbrennen thanks for taking the time and implementing the changes. I think the overall approach looks good. I left a few comments, but I haven't had time to review the tests yet.

file_name: str,
rpath: str,
wheel_base_dir: str) -> None:
"""Add a new rpath entry to a file while preserving as many existing
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should keep the ELF patcher implementation as a thin layer abstracting ELF operations, and move anything which is not an implementation detail of a specific ELF patcher to the caller. I think append should really just append and the caller should implement all the checks on duplicates, etc. Consider that all of this logic would probably be implemented the exact same way in a different implementation of ELFPatcher, so it's probably a better idea to extract it into a set of common functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, good call, ok -- so move this into repair_wheel()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I moved all this into repair_wheel() along with the tests. There is a slight change to the way patching works -- the patcher calls now always modify the absolute path to the library. I can't think of a reason why this would be a problem but it is different. If you object it would be very easy to change.

auditwheel/patcher.py Outdated Show resolved Hide resolved
2) Not be a duplicate of an already-existing rpath entry.
"""
old_rpaths = self.get_rpath(file_name)
old_rpaths = _preserve_existing_rpaths(old_rpaths, file_name,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this implementation is OK, but I think it could be simplified a bit by breaking _preserve_existing_rpaths up.

Create a single function to check if an RPATH is under wheel_base (e.g _is_valid_rpath) and do

rpaths = filter(is_valid_rpath, old_rpaths.split(':'))

Then create the ordered set out of rpaths, and stick the new RPATH in the end. This way we don't have to do the duplicate check.

I might have missed something, but this is the general idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not a problem -- that does sound simpler. =)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Better?

@fbrennen
Copy link
Contributor Author

@lkollar the s390x build appears to have exploded before it could run -- it did pass here. Could you get it to run again? Thanks!

auditwheel/repair.py Outdated Show resolved Hide resolved
@lkollar
Copy link
Contributor

lkollar commented Jun 18, 2020

@lkollar the s390x build appears to have exploded before it could run -- it did pass here. Could you get it to run again? Thanks!

There seems to be an issue on Travis with s390x builds: https://travis-ci.community/t/s390-xenial-builds-fail-with-an-error-occurred-while-generating-the-build-script/9039. This platform has been problematic on Travis, I would just ignore the failure.

@lkollar lkollar merged commit 4709edb into pypa:master Jun 22, 2020
@lkollar
Copy link
Contributor

lkollar commented Jun 22, 2020

Thanks @fbrennen!

@fbrennen
Copy link
Contributor Author

No problem =)

@sc1f
Copy link

sc1f commented Jun 23, 2020

Thanks y'all for this fix. I was working on this issue yesterday and saw this PR was open, and I'm glad it's been merged!

@finos/perspective suffers from the same issue as described in #69 and #137 - we have a libbinding.so of Pybind bindings, which depend on libpsp.so and libtbb.so in the same folder next to libbinding.

Because it's detected via $ORIGIN in rpath, auditwheel does not include it in the external libs and so does not copy it out during auditwheel repair. Thus, when the rpath is overwritten to the libs folder created by auditwheel, libbinding.so can't find the libraries.

Looking over this patch it seems like that issue will be fixed in the next release? Thanks - appreciate the hard work!

Update I've been using auditwheel from master and it fixes our issue entirely. Thanks!

pramodk pushed a commit to neuronsimulator/nrn that referenced this pull request Apr 16, 2021
alexsavulescu added a commit to neuronsimulator/nrn that referenced this pull request Apr 19, 2021
alexsavulescu added a commit to neuronsimulator/nrn that referenced this pull request Apr 30, 2021
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.

RPATH is being incorrectly overwritten Auditwheel stripping rpath for relative paths
5 participants