Skip to content

SR-648: Allow swiftpm to statically link binaries on Linux #9958

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

Merged
merged 4 commits into from
Jul 19, 2017

Conversation

spevans
Copy link
Contributor

@spevans spevans commented May 27, 2017

This PR requires swiftlang/swift-corelibs-foundation#1011 and
swiftlang/swift-corelibs-xctest#190 to be merged beforehand.

  • If --build-swift-static-stdlib option is used then also produce
    static versions of libXCTest.a and libdispatch.a and put them
    into the lib/swift_static/linux/ toolchain directory.
    libFoundation.a is already being built and deployed there.

  • Binaries with the swift libs statically linked in can then be
    built with using the command:

    swift build -Xswiftc -static-stdlib -Xlinker -lcurl -Xlinker -lxml2 -Xlinker -lbsd -Xlinker --allow-multiple-definition

    Note: This is a dynamic binary with the libswiftCore, libFoundation and libdispatch libraries statically linked in.

  • Further fixes should reduce the complexity of the above command.

There arent any unit tests in this PR, however I will add a followup one which builds a static version of TestFoundation to test xctest, libdispatch and libfoundation together.

@ddunbar
Copy link
Contributor

ddunbar commented Jun 27, 2017

@jrose-apple can you review this? We would like to get static-stdlib support into SwiftPM for 4 if possible.

The build-script changes LGTM, but the driver part I think needs you review (and @spevans it would be good to have a test encoding why this reordering is important, IMHO)

@spevans
Copy link
Contributor Author

spevans commented Jun 27, 2017

The re-ordering of the auto link files is because when linking in static libraries and object files the ordering of arguments is important and the dependant libraries need to come after the objects/libs that require them. This is also the reason that the -Xlinker arguments are moved around.

My testing so far has not shown the changes to affect normal dynamic linking either.

As for a test case I have a change for swift-corelibs-foundation that adds a static_test target that links the TestFoundation binary with the static swift libs which can be run manually on linux although I am still running some tests on it and it is not fully finished.

@ddunbar
Copy link
Contributor

ddunbar commented Jun 27, 2017

I meant just a swiftc test case which validates the driver passes the Xlinker and auto linking arguments in the correct relative order.

@spevans
Copy link
Contributor Author

spevans commented Jun 27, 2017

Oh I see, yes I should be able to add one for that.

@jrose-apple
Copy link
Contributor

I think if you're going to change it at all you should put the -Xlinker options all the way at the end. Anything else would run into some other ordering problem later.

@spevans
Copy link
Contributor Author

spevans commented Jun 28, 2017

I moved the -Xlinker options as far to the end as was possible without causing the tests to break. This meant they ended up just before the swift_end.o used on ELF to mark the end of the .swift object sections.

I added a test to check the autolink and linker options are in the correct order.

@jrose-apple
Copy link
Contributor

Why do they need to be before swift_end.o? We don't expect -Xlinker options to include extra Swift object files.

(Also, why --allow-multiple-definition? And swiftc accepts -l flags without the -Xlinker.)

@spevans
Copy link
Contributor Author

spevans commented Jun 28, 2017

I dont know the exact reason they need to be before the swift_end.o but when I moved them to just before the -o multiple tests failed. I did develop another solution where the linker args are added to a dummy object file to then be extracted as autolink arguments, which means that all of the extra linker commands can be hidden in one file. This would require some functionality to be added to swiftpm to dynamically generate the object file - currently I use the following script:

#!/bin/bash

output=$1
shift

read -r -d '' CODE <<EOF
// These commands will get extracted by 'swift-autolink-extract'
#define AUTOLINK_ENTRY(x) asm(".asciz \"" x "\"")

asm(".section .swift1_autolink_entries");
EOF

for arg in "$@"
do
    CODE="$CODE"$'\n'"AUTOLINK_ENTRY("\"${arg}\"");"
done
echo $CODE
clang++ -Wall -Werror -c -x c++ -o $output - <<<"$CODE"

Then it can be invoked as follows:
swift-autolink-create static_args.o -Xlinker --allow-multiple-definition ...

The -Xlinker args are actually going to swift-build not swiftc although it is possible to replace the -Xlinker with -Xswiftc except for when a direct path to a library is needed (which doesnt use -l).

The --allow-multiple-definition is just a temporary workaround because objc_retainAutoreleasedReturnValue is in both libdispatch.a and libFoundation.a. I do have a PR to have this function exported from libdispatch.a, then I can open another one to remove it from libFoundation.a and then this argument can be removed.

@jrose-apple
Copy link
Contributor

Ah, my bad on misreading the -Xlinker, and thanks for the explanation on --allow-multiple-definition. I'm still wondering what we're depending on in swiftc's handling of -Xlinker, though. Does swift-build pass -Xlinker arguments that need to appear early?

spevans added 3 commits June 29, 2017 13:04
- If --build-swift-static-stdlib option is used then also produce
  static versions of libXCTest.a and libdispatch.a and put them
  into the lib/swift_static/linux/ toolchain directory.
  libFoundation.a is already being built and deployed there.

- Binaries with the swift libs statically linked in can then be
  built with using the command:

  swift build -Xswiftc -static-stdlib -Xlinker -lcurl -Xlinker -l:libxml2.a -Xlinker -llzma -Xlinker /lib/x86_64-linux-gnu/libz.so.1 -Xlinker -lbsd -Xlinker --allow-multiple-definition

  Note: This is a dynamic binary with the libswiftCore,
  libFoundation and libdispatch libraries statically linked in.

- Further fixes should reduce the complexity of the above command.
- Reordering the auto link files when linking in static libraries
  and object files is required because the ordering of arguments
  is important, and the dependant libraries need to come after the
  objects/libs that require them. This is not a problem for
  libswiftCore.a but can be an issue with libs that sit on top of
  it, e.g. libFoundation.a

- Dont add an -rpath to the Swift dynamic libraries if using
  -static-stdlib
- Move the -Xlinker and -l options to just before swift_end.o

- Add testcase to check linker option ordering.
@spevans
Copy link
Contributor Author

spevans commented Jun 29, 2017

I just realised that I didn't update the build command, it should be:

swift build -Xswiftc -static-stdlib -Xlinker -lcurl -Xlinker -l:libxml2.a -Xlinker -llzma -Xlinker /lib/x86_64-linux-gnu/libz.so.1 -Xlinker -lbsd -Xlinker --allow-multiple-definition

This is because in this case libxml2 links to libicu which is being statically linked in so libxml2.a needs to be linked instead of libxml2.so. This is also why an explicit path to libz.so.1 is given.

The -l:libxml2.a is the reason that the arguments need to be in the specific order although it is the only argument that needs to be listed in a specific position - after the @static-stdlib-args.lnk argument.

You are also correct about being able to use -l as well as -Xlinker when passing arguments and so I updated the PR moving the OPT_linker_option_group as well as the OPT_Xlinker to the end.

The underlying problem is that -l and -lXlinker command line arguments are grouped by the code into 2 groups and any ordering is lost. Also having to support building via both swiftc and swift build does complicate matters.

Using these link options was only really a halfway stage to show the current progress. If moving the options to the end is an issue I can always revert that one change and use a different method such as extending the --static-stdlib option to take an alternative link file. The link files have the advantage that option ordering is preserved. It wouldn't be a major problem as either method still requires some changes in swiftpm to drive the underlying swiftc command. In fact it may be a better solution in the end.

@spevans
Copy link
Contributor Author

spevans commented Jul 12, 2017

The -Xlinker --allow-multiple-definition can now be dropped from the build command, it was fixed by the following PRs:
swiftlang/swift-corelibs-libdispatch#258
swiftlang/swift-corelibs-foundation#1090
swiftlang/swift-corelibs-foundation#1114

@alblue
Copy link
Contributor

alblue commented Jul 12, 2017

@swift-ci please smoke test

@spevans
Copy link
Contributor Author

spevans commented Jul 12, 2017

@alblue could you rerun the linux one please, from the logs it looks like it might have been a temporary network issue

@ddunbar
Copy link
Contributor

ddunbar commented Jul 12, 2017

@swift-ci please smoke test linux

Copy link
Contributor

@jrose-apple jrose-apple left a comment

Choose a reason for hiding this comment

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

All right, I guess I'm okay with this. Thanks for sticking with it, Simon!

// REQUIRES: static_stdlib
print("hello world!")
// RUN: %empty-directory(%t)
// RUN: %target-swiftc_driver -v -static-stdlib -o %t/static-stdlib %s -Xlinker --no-allow-multiple-definition 2>&1| %FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

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

You shouldn't need to actually run the linker here. Instead, you can use -### (or -driver-print-jobs) to show what would be run. That way we can run the test on other platforms.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you want me to update the test? The argument reordering change is only for Linux (actually GenericUnix) so I don't think it will make any difference on other platforms.

Copy link
Contributor

@jrose-apple jrose-apple Jul 13, 2017

Choose a reason for hiding this comment

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

Yes, please. GenericUnix is still used in other places, and it's useful to be able to run the test even on Darwin. (Also, not actually compiling and linking would run faster.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jrose-apple Ive added the -driver-print-jobs and removed the linux requirement. It tested ok on Linux and macOS. If the commit looks ok could you rerun the tests please.

- Use -driver-print-jobs to avoid actually building the test file.
// RUN: %target-swiftc_driver -v -static-stdlib -o %t/static-stdlib %s -Xlinker --no-allow-multiple-definition 2>&1| %FileCheck %s
// CHECK: Swift version
// CHECK: Target: x86_64-unknown-linux-gnu
// RUN: %target-swiftc_driver -driver-print-jobs -static-stdlib -o %t/static-stdlib %s -Xlinker --no-allow-multiple-definition 2>&1| %FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks!

@spevans
Copy link
Contributor Author

spevans commented Jul 18, 2017

Could someone please run a test on this latest change please

@aciidgh
Copy link
Contributor

aciidgh commented Jul 18, 2017

@swift-ci please smoke test

@spevans
Copy link
Contributor Author

spevans commented Jul 19, 2017

@jrose-apple can this be merged now?

@jrose-apple
Copy link
Contributor

Ah, yes, looks good! Thanks, Simon.

@jrose-apple jrose-apple merged commit e994f61 into swiftlang:master Jul 19, 2017
@jrose-apple
Copy link
Contributor

If you want to bring this into Swift 4.0 branch, please follow the process at https://swift.org/blog/swift-4-0-release-process/. You can mention that this was reviewed by me.

@ddunbar
Copy link
Contributor

ddunbar commented Jul 19, 2017

+1 on getting this into 4.

Thanks for your perseverance on this @spevans!

beccadax added a commit to beccadax/swift that referenced this pull request Jun 25, 2019
Turns out the order of the linker flags matters on Linux; see swiftlang#9958. I don’t know if the change I previously made would break something, but it’s not worth risking it.
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.

5 participants