Ticket UUID: | 113be1991e7262479a1843f3924540d7a08d8a88 | |||
Title: | zipfs on mac | |||
Type: | Bug | Version: | core-8-branch | |
Submitter: | bll | Created on: | 2018-11-23 17:01:39 | |
Subsystem: | 53. Configuration and Build Tools | Assigned To: | marc_culler | |
Priority: | 5 Medium | Severity: | Critical | |
Status: | Closed | Last Modified: | 2021-04-02 10:30:37 | |
Resolution: | Fixed | Closed By: | jan.nijtmans | |
Closed on: | 2021-04-02 10:30:37 | |||
Description: |
This error occurs when running the macos build: /Library/Developer/CommandLineTools/usr/bin/redo_prebinding: the __LINKEDIT segment does not cover the end of the file (can't be processed) in: .//Library/Frameworks/Tcl.framework/Versions/8.7/Tcl References: https://stackoverflow.com/questions/48324224/codesign-an-old-director-projector-on-os-x-10-13-perhaps-manipulate-linkedit https://github.com/pyinstaller/pyinstaller/wiki/Recipe-OSX-Code-Signing (see pyinstaller fix implementation) # some good information on how to create a new TEXT segment when linking. https://github.com/HaxeFoundation/neko/issues/130 | |||
User Comments: |
jan.nijtmans added on 2021-04-02 10:30:37:
Basically, this ticket is now solved in two ways: 1) When using a MacOS framework, the zip-file is not attached any more 2) When building standalone executables, marc_culler's "macher" utility fixes the job: https://github.com/culler/macher. Just make sure "macher" is somewhere on your path, then the Tcl "configure" script will find it and use it. So, this ticket can be closed. marc_culler (claiming to be Marc Culler) added on 2021-01-31 18:49:03: I am not saying that the zipfs idea is totally useless on macOS, nor that it is a non-starter. I am saying that it should never be used in a framework or in a standalone application. But it does have a limited utility. Where I do think the zipfs feature could be useful is for single file executables which are meant to be run from the command line. An example of such a thing is fossil. I build my own fossil because Apple's openssl is totally broken. When I build fossil with Tk support (because I find fossil diff -tk to be incredibly useful) I am not able to use it on a machine which does not have Tcl installed in /Library/Frameworks. Specifically, when I create a new macOS image in VMWare and I want to use fossil to clone the Tcl and Tk repositories for the first time, I cannot use a fossil built with Tk support. So I build two versions of fossil, called fossil0 and fossil. The first one does not have Tk support, and I can use it for my first clone. Then I switch to using fossil. But, once again, standalone macOS apps should be bundles and should never use the zipfs feature. Ditto for frameworks. kevin_walzer added on 2021-01-31 14:55:25: Just to expand on Marc's observations, the single-file-executable question has been addressed on the Mac for 20+ years with app bundles (app directories that appear to the user as single files), frameworks, and related technologies. The zipfs approach, as currently laid out, is completely incompatible with that and, in fact, seems to break Tcl/Tk on the Mac. I agree with Marc that the current approach is a non-starter on macOS. Historically, tclkits have worked on the Mac by embedding everything, including the frameworks, within the virtual filesystem. Unpack a tclkit and Tcl.framework and Tk.framework are among the bits dumped out in to the vfs folder. While not very Mac-like, it does work, and can even be wrapped up in an app bundle as well so the user doesn't see a raw executable sitting on their desktop. Why are we not look at this approach for zipfs? marc_culler (claiming to be Marc Culler) added on 2021-01-31 14:44:43: It does not make sense for a macOS framework build. The macOS framework structure completely solves the problem of how a shared library can find its initialization scripts. And it solves that problem in a way which is completely standardized throughout the operating system and understood and expected by developers. It is also well thought out. I think it is one of the better designed parts of macOS. It would be crazy to throw that away in favor of some quirky one-off scheme that applies to Tcl and nothing else and makes Tcl incompatible with everything else in the OS. I have no issue with using the zip scheme for other OS's and I think it is fine for single-file executables but please don't do that to the Tcl and Tk frameworks. That would be a big mistake. jan.nijtmans added on 2021-01-31 08:53:04: Well, I think zips makes very much sense in a shared build! Just in a very different way than in static builds. In shared builds, the zip-file is attached to the shared library, not to the exe. So the shared library no longer has to search for its initialization scripts, it already has them in its own zip-file. Unfortunately, in Tk this implementation is only half-baked yet. My intention is to fix that first, and then we can see how to deal with that on MacOS. Thanks for all your info and the fix_macho script. That is very useful. Could we have such script in Tcl? :-; Thanks very much! marc_culler (claiming to be Marc Culler) added on 2021-01-30 18:00:07: Just to expand on my comment about pointlessness ... We have already disabled the zipfs option for framework builds on macOS but we should also disable it for dynamic builds on any platform. If a single file executable requires a shared library then it is not a single file executable. This only make sense if tclsh is statically linked. marc_culler (claiming to be Marc Culler) added on 2021-01-30 17:41:59: Appending a zip file to a mach-O binary corrupts the mach-O binary. This is because a mach-O binary has structure. It has a header followed by a sequence of load commands. Some load commands define segments, such as __TEXT or __DATA. Some load commands define sections within those segments. The load command which defines a segment specifies the offset and size of the segment within the file and within the virtual image after loading. The last segment is the __LINKEDIT segment, which is reserved for use by the dynamic linker. In the binary file this segment is required to extend to the end of the file. This is no longer the case after you append data to the end of the binary. It is possible to adjust the load commands in the binary so that the extra data becomes part of the __LINKEDIT segment, and appears to the loader as if it were part of the string table in the LC_SYMTAB section of the __LINKEDIT segment. I am attaching a python script which does that, using the pypi package macholib. It is named fix_macho. If you build the hello world example as described in TIP #430 and run $ ./fix_macho hello you will get a single file executable which runs and which also can be processed by install_name_tool with no errors -- the @bll test passes. NOTE: Building a "single file executable" for macOS using the recipe in the TIP is a **truly pointless exercise**. The executable that you get needs to load the shared library /Library/Frameworks/Tcl.framework/Tcl. If that shared library exists, then you could simply run the Tcl script with tclsh. If it does not exist then the executable is useless. It will crash because it cannot load the shared library that it depends upon. bll added on 2021-01-29 09:57:39: Related tickets: https://core.tcl-lang.org/tcl/tktview?name=16e9b297d https://core.tcl-lang.org/tcl/info/92c2169782c2e006 bll added on 2021-01-22 23:38:03: Just an example that proves the library is corrupted. I would have to re-run my build scripts against 8.7 to see if that error pops up when install_name_tool is run on the executables. Previously, I may have had some other package installed that required the library load path be changed in a shared library. I do not appear to have any such now. The goal is simply to have the .zip file in a different location other than appended to the shared library for Mac OS. In order to build with a different location for the .zip file, this ticket becomes relevant: https://core.tcl-lang.org/tcl/tktview?name=16e9b297d As there needs to be a way to build Tcl such that the .zip file is located elsewhere. When Tcl is built as a framework, this location is easy, somewhere in the Resources/ directory, accessed via a relative path. When built via ../unix/configure and the standard Makefile, the location may need to be hard-coded. marc_culler (claiming to be Marc Culler) added on 2021-01-22 17:40:04: Given that macOS has a concept of a "framework" which is designed for this purpose, I agree that it would make more sense to use it. Of course it is not one file, it is a directory, but it is easily distributed as one file, either as a disk image or a .pkg file. Using a zip filesystem would seem to be a Windows solution to this problem which is not such a good match for macOS. There is also a notion of a "subframework" in macOS. An application bundle is allowed to contain a Frameworks directory at the same level as the MacOS directory that contains the main executable of the application. When Tcl is built as a framework, all of the needed files are already available to Tcl. This requires no change. If you want to use the Tcl framework as a subframework placed in the Frameworks directory of an application bundle then you can make the Tcl library available to the application by setting the library load path of the main executable of the application to include: @executable_path/../Frameworks/Tcl.framework/Versions/Current/Tcl or even @executable_path/../Frameworks/Tcl.framework/Tcl This is easily done with install_name_tool and works fine with code signing as well. So using a subframework would seem to be a more natural means of doing this on macOS if I understand correctly what the goal is. jan.nijtmans added on 2021-01-22 16:53:05: The appending of the zip file is done for TIP #430. It is - basically - all files normally stored on disk under $tcl_library, which are put into a zip-file and appended to the shared library. Tcl is able to access this zip-file at run-time, which means that we just have a single shared library containing everything in stead of relying on a bunch of files on disk. The shared library contains everything needed, instead of it needing to search where "init.tcl" is. For MacOS, appending a zip-file to a shared library is not such a good idea. It would be better to store the zip-file in a resource, and adapt TIP #430 such that Tcl can access the resource instead of accessing the zip-file. That's what this ticket is about. This is 'critical' IMHO for 8.7, but since 8.7 final is still far off it isn't something that needs to be solved now. marc_culler (claiming to be Marc Culler) added on 2021-01-22 16:38:18: If you are only using install_name_tool to check whether a dynamic library is corrupt then I think you would be better off using otool -l to see what the actual structure of the mach-O library is. But I can't contribute anything here until I find out what causes a zip file to be appended to a dynamic library and why this is being done. marc_culler (claiming to be Marc Culler) added on 2021-01-22 16:33:19: I really don't understand your invocation of install_name_tool. You seem to be trying to change the "identification name" of the .dylib file, not the load path for any of the shared libraries that it depends upon. To do that you are supposed to use the -id option. I am able to change the id of the Tcl library that I built using the standard make -C macosx command. The library dependencies of that Tcl library are all system libraries, for which a static global path should be fine. They are: /usr/lib/libz.1.dylib /usr/lib/libSystem.B.dylib /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation I can't imagine why you would want to change any of those, which is all that can be done with the -change option. I also don't understand how when or why a zip file would be getting appended to the Tcl dynamic library. I can believe that there is a make option that does that, but I don't know what it is. I can also believe that simply appending a random file to the end of a mach-O dynamic library does not have the effect of extending the TEXT segment. I need a lot more explanation about what the goal is here and also what the mechanism is for appending a zip file. A dynamic library (often but not always named with the .dylib extension) can be used for either linking or for loading dynamically. It is not possible to link against a "shared library" (usually .so or .bundle). The Tcl library created by the standard build is a dynamic library but it is just named Tcl. My understanding is that the dynamic library's identification name is only used when an executable or library is linked against it, and that it gets copied into the list of library load paths for the executable, i.e. the paths that get displayed by otool -L below the identification name. bll added on 2021-01-22 02:50:20: bll-mac:/Volumes/Users/bll/t/localE/lib$ install_name_tool -change /Volumes/Users/bll/t/localE/lib/libtcl8.7.dylib @executable_path/../lib/libtcl8.7.dylib libtcl8.7.dylib /Library/Developer/CommandLineTools/usr/bin/install_name_tool: fatal error: the __LINKEDIT segment does not cover the end of the file (can't be processed) in: libtcl8.7.dylib bll added on 2021-01-22 02:35:39: I don't recall the conditions under which I built. It's not so much a problem with what error may or may not print, but rather the fact that appending a .zip file to the .dylib corrupts the .dylib. The test will be to run otool and install_name_tool on the newly built libraries and make sure they are able to process the library. marc_culler (claiming to be Marc Culler) added on 2021-01-21 19:57:01: I see that this ticket has been assigned to me, but I do not know what the ticket is about. I just build the tip of core-8-branch on Big Sur and there were no errors, and no warnings. I just used the usual command: make -C macosx Maybe this problem, whatever it was, got fixed? sebres added on 2018-11-23 22:42:50: Hmm, indeed... Or may be it is just some after effect, e. g. map/lib file should be regenerated after melting with zip-content, or there is a better zip-version for macos, that doesn't have this issue. Anyway linking the zip as resource or object file would be definitely better strategy. A bit more complicated, because of mutual dependencies, but... it looks like here it could be the single proper way, possibly. bll added on 2018-11-23 22:19:47: > As zip seems to do the correction of the entry offsets here, I don't understand > why the offsets of __LINKEDIT segment should be still invalid. What do you see > here (or do you see this or similar message)? I doubt very much that zip knows anything about Mac OS X dynamic library structure. bll added on 2018-11-23 21:57:19: XCode 8 is a bit old now... I can also test with High Sierra/10.1 or Sierra/9.2. MacPorts zip is the same version, perhaps different compilation options. Mojave 10.14.1 XCode command line tools 10.1 bll-mac:/Users/bll/t/tcl-work/tcl$ fossil status repository: /Volumes/Users/bll/t/tcl-work/tcl/../../tcl.fossil local-root: /Volumes/Users/bll/t/tcl-work/tcl/ config-db: /Users/bll/.fossil checkout: 78fe053f4a93d61c5a6ef3901a346cf74b953eb0 2018-11-23 19:59:12 UTC parent: fc958af3807acc9eaadded3b0345be933f80071c 2018-11-23 11:59:53 UTC merged-into: 56b5fa3d12208e41177d7257c335a1b986fc9fd9 2018-11-23 21:00:57 UTC tags: core-8-branch comment: make: better algo to wrap to absolute zip-path (for systems resp. toolchains without readlink/realpath), e. g. avoid xcode/macosx build error "readlink: illegal option -- m" (user: sebres) # The zip that comes with Mojave... zip --version Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license. This is Zip 3.0 (July 5th 2008), by Info-ZIP. make -C macosx PREFIX="" INSTALL_ROOT=$HOME/t/local-test install checking for zip... /usr/bin/zip Found INFO Zip in environment ld: warning: option -prebind is obsolete and being ignored Zip entry offsets appear off by 2827284 bytes - correcting... /Library/Developer/CommandLineTools/usr/bin/redo_prebinding: the __LINKEDIT segment does not cover the end of the file (can't be processed) in: .//Library/Frameworks/Tcl.framework/Versions/8.7/Tcl sebres added on 2018-11-23 21:06:49: Do you have tried it with the latest fixes? Or could you try it with newest since [78fe053f4a], because it had previously some typo's and mistakes in unix/Makefile and in several configure scripts. I'm not sure this is completely valid for macos, because is not tested there, but Xcode build (xcode8 C) seems to be OK (BUILD_DIR=unix as well as BUILD_DIR=macosx), at least on TravisCI. As zip seems to do the correction of the entry offsets here, I don't understand why the offsets of __LINKEDIT segment should be still invalid. What do you see here (or do you see this or similar message)? Which revision exactly produced this error? Did you see more info better describing the error (failing group or line number where it is failed, some another errors/warnings)? Which zip version exactly is used by build? e. g. is zip installed on yoour system or rather a minizip was used (built within tcl as zip-replacement fallback in build-directory if zip is missing)? Or what do you see by `zip --version`? Because I'm not sure minizip can fix segments/sfx-info (especially by macosx), so could you install zip in order to test it works with? |
Attachments:
- fix_macho [download] added by marc_culler on 2021-01-30 17:43:44. [details]