Skip to content

Commit 27a46bf

Browse files
committed
[SR-648] Add option to create statically linked binaries
- Adds -static-executable option to swiftc to use along with -emit-executable - Only works on Linux for now, adds a stub to replace select libdl functions with a fatal error message to catch other libs that use dlopen() etc (eg libicu) to aid in debugging since they wont work on static binaries anyway and could just silently fail otherwise - Darwin doesnt support static binaries, see https://developer.apple.com/library/content/qa/qa1118/_index.html
1 parent f71d988 commit 27a46bf

File tree

8 files changed

+128
-5
lines changed

8 files changed

+128
-5
lines changed

include/swift/Option/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,12 @@ def no_static_stdlib: Flag<["-"], "no-static-stdlib">,
278278
Flags<[HelpHidden]>,
279279
HelpText<"Don't statically link the Swift standard library">;
280280

281+
def static_executable : Flag<["-"], "static-executable">,
282+
HelpText<"Statically link the executable">;
283+
def no_static_executable : Flag<["-"], "no-static-executable">,
284+
Flags<[HelpHidden]>,
285+
HelpText<"Don't statically link the executable">;
286+
281287
def use_ld : Joined<["-"], "use-ld=">,
282288
Flags<[DoesNotAffectIncrementalBuild]>,
283289
HelpText<"Specifies the linker to be used">;

lib/Driver/ToolChains.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,12 @@ toolchains::Darwin::constructInvocation(const LinkJobAction &job,
948948
assert(context.Output.getPrimaryOutputType() == types::TY_Image &&
949949
"Invalid linker output type.");
950950

951+
if (context.Args.hasFlag(options::OPT_static_executable,
952+
options::OPT_no_static_executable,
953+
false)) {
954+
llvm::report_fatal_error("-static-executable is not supported on Darwin");
955+
}
956+
951957
const Driver &D = getDriver();
952958
const llvm::Triple &Triple = getTriple();
953959

@@ -1353,9 +1359,38 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job,
13531359

13541360
// Link the standard library.
13551361
Arguments.push_back("-L");
1356-
if (context.Args.hasFlag(options::OPT_static_stdlib,
1357-
options::OPT_no_static_stdlib,
1358-
false)) {
1362+
if (context.Args.hasFlag(options::OPT_static_executable,
1363+
options::OPT_no_static_executable,
1364+
false)) {
1365+
SmallString<128> StaticRuntimeLibPath;
1366+
getRuntimeStaticLibraryPath(StaticRuntimeLibPath, context.Args, *this);
1367+
Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath));
1368+
1369+
SmallString<128> StaticStubObjectPath = StaticRuntimeLibPath;
1370+
llvm::sys::path::append(StaticStubObjectPath, "static_stub.o");
1371+
auto ObjectPath = StaticStubObjectPath.str();
1372+
1373+
if (llvm::sys::fs::is_regular_file(ObjectPath)) {
1374+
// FIXME: It would be better if these were extracted from static_stub.o
1375+
// using `swift-autolink-extract'
1376+
Arguments.push_back(context.Args.MakeArgString(ObjectPath));
1377+
Arguments.push_back("-static");
1378+
Arguments.push_back("-Xlinker");
1379+
Arguments.push_back("--defsym=__swift2_protocol_conformances_start=.swift2_protocol_conformances_start");
1380+
Arguments.push_back("-Xlinker");
1381+
Arguments.push_back("--defsym=__swift2_type_metadata_start=.swift2_type_metadata_start");
1382+
Arguments.push_back("-lswiftCore");
1383+
Arguments.push_back("-licui18n");
1384+
Arguments.push_back("-licuuc");
1385+
Arguments.push_back("-licudata");
1386+
Arguments.push_back("-lpthread");
1387+
} else {
1388+
llvm::report_fatal_error("-static-executable not supported on this platform");
1389+
}
1390+
}
1391+
else if (context.Args.hasFlag(options::OPT_static_stdlib,
1392+
options::OPT_no_static_stdlib,
1393+
false)) {
13591394
SmallString<128> StaticRuntimeLibPath;
13601395
getRuntimeStaticLibraryPath(StaticRuntimeLibPath, context.Args, *this);
13611396
Arguments.push_back(context.Args.MakeArgString(StaticRuntimeLibPath));

stdlib/public/runtime/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ set(LLVM_OPTIONAL_SOURCES
6565
MutexPThread.cpp
6666
MutexWin32.cpp
6767
CygwinPort.cpp
68+
static_stub.c
6869
${swift_runtime_sources}
6970
${swift_runtime_objc_sources}
7071
${swift_runtime_leaks_sources})
@@ -131,3 +132,15 @@ foreach(sdk ${ELFISH_SDKS})
131132
endforeach()
132133

133134
add_custom_target(section_magic ALL DEPENDS ${object_target_list})
135+
136+
if(SWIFT_BUILD_STATIC_STDLIB)
137+
foreach(sdk ${SWIFT_SDKS})
138+
if("${sdk}" STREQUAL "LINUX")
139+
add_library(static_stub OBJECT static_stub.c)
140+
swift_install_in_component(stdlib
141+
FILES "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/static_stub.dir/static_stub.c${CMAKE_C_OUTPUT_EXTENSION}"
142+
RENAME "static_stub.o"
143+
DESTINATION "lib/swift_static/linux")
144+
endif()
145+
endforeach()
146+
endif()

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ static void _addImageTypeMetadataRecordsBlock(const uint8_t *records,
8080

8181
static InspectArgs metadataSectionArgs = {
8282
_addImageTypeMetadataRecordsBlock,
83-
SWIFT_TYPE_METADATA_SECTION
83+
SWIFT_TYPE_METADATA_SECTION,
84+
#if defined(SUPPORTS_STATIC_BINARIES)
85+
&__swift2_type_metadata_start
86+
#endif
8487
};
8588

8689
#if defined(__APPLE__) && defined(__MACH__)

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,10 @@ static void _addImageProtocolConformancesBlock(const uint8_t *conformances,
217217

218218
static InspectArgs conformanceSectionArgs = {
219219
_addImageProtocolConformancesBlock,
220-
SWIFT_PROTOCOL_CONFORMANCES_SECTION
220+
SWIFT_PROTOCOL_CONFORMANCES_SECTION,
221+
#if defined(SUPPORTS_STATIC_BINARIES)
222+
&__swift2_protocol_conformances_start
223+
#endif
221224
};
222225

223226
#if defined(__APPLE__) && defined(__MACH__)

stdlib/public/runtime/SectionData.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ swift::_swift_initializeCallbacksForSectionData(
4949
}
5050
#elif defined(__ELF__) || defined(__ANDROID__)
5151

52+
#if defined(SUPPORTS_STATIC_BINARIES)
53+
// If creating a static binary using --static-executable,
54+
// gold will set these to the data sections via --defsym
55+
const void *__swift2_protocol_conformances_start = nullptr;
56+
const void *__swift2_type_metadata_start = nullptr;
57+
#endif // SUPPORTS_STATIC_BINARIES
58+
5259
static int
5360
_addImageSectionData(struct dl_phdr_info *info, size_t size, void *data) {
5461
// inspectArgs contains addImage*Block function and the section name
@@ -79,6 +86,18 @@ _addImageSectionData(struct dl_phdr_info *info, size_t size, void *data) {
7986

8087
void
8188
swift::_swift_initializeCallbacksForSectionData(InspectArgs *inspectArgs) {
89+
#if defined(SUPPORTS_STATIC_BINARIES)
90+
const void **sectionDataAddr = inspectArgs->sectionDataAddr;
91+
assert(sectionDataAddr != nullptr );
92+
if (*sectionDataAddr) {
93+
auto blockAddr = reinterpret_cast<const uint8_t*>(sectionDataAddr);
94+
auto blockSize = *reinterpret_cast<const uint64_t*>(blockAddr);
95+
blockAddr += sizeof(blockSize);
96+
inspectArgs->fnAddImageBlock(blockAddr, blockSize);
97+
return;
98+
}
99+
#endif // SUPPORTS_STATIC_BINARIES
100+
82101
// Search the loaded dls. Unlike the above, this only searches the already
83102
// loaded ones.
84103
// FIXME: Find a way to have this continue to happen after.

stdlib/public/runtime/SectionData.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@
3636
#elif defined(__ELF__)
3737
#define SWIFT_PROTOCOL_CONFORMANCES_SECTION ".swift2_protocol_conformances_start"
3838
#define SWIFT_TYPE_METADATA_SECTION ".swift2_type_metadata_start"
39+
40+
#if defined(__linux__)
41+
#define SUPPORTS_STATIC_BINARIES
42+
// Add a declaration for each section
43+
extern const void *__swift2_protocol_conformances_start;
44+
extern const void *__swift2_type_metadata_start;
45+
#endif // __linux__
46+
3947
#elif defined(__CYGWIN__) || defined(_MSC_VER)
4048
#define SWIFT_PROTOCOL_CONFORMANCES_SECTION ".sw2prtc"
4149
#define SWIFT_TYPE_METADATA_SECTION ".sw2tymd"
@@ -47,6 +55,9 @@ namespace swift {
4755
struct InspectArgs {
4856
void (*fnAddImageBlock)(const uint8_t *, size_t);
4957
const char *sectionName;
58+
#if defined(SUPPORTS_STATIC_BINARIES)
59+
const void **sectionDataAddr;
60+
#endif
5061
};
5162

5263
#if defined(__APPLE__) && defined(__MACH__)

stdlib/public/runtime/static_stub.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#if defined(__LINUX__) && defined(__ELF__)
2+
#error "This only works on Linux/ELF" // Needs testing on other ELF platforms
3+
#else
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <pthread.h>
7+
8+
9+
// This forces resolving of these weak symbols but keeps them hidden
10+
// externally
11+
const void *unused1 __attribute__ ((unused, visibility("internal"))) = pthread_self;
12+
const void *unused2 __attribute__ ((unused, visibility("internal"))) = pthread_key_create;
13+
const void *unused3 __attribute__ ((unused, visibility("internal"))) = pthread_once;
14+
15+
16+
// linking libdl into static binaries produces this message:
17+
// "warning: Using 'dlopen' in statically linked applications requires at
18+
// runtime the shared libraries from the glibc version used for linking"
19+
//
20+
// Instead of letting the calls silently fail, show an error and quit.
21+
// This is not actually needed, it is just to aid debugging
22+
23+
#define UNSUPPORTED_FUNC(x) void x() { \
24+
fprintf(stderr, "Unsupported dynamic linker call: %s\n", __func__); \
25+
abort(); \
26+
}
27+
28+
UNSUPPORTED_FUNC(dlopen)
29+
UNSUPPORTED_FUNC(dlsym)
30+
UNSUPPORTED_FUNC(dladdr)
31+
UNSUPPORTED_FUNC(dlclose)
32+
33+
#endif // linux && ELF

0 commit comments

Comments
 (0)