Skip to content

[Autolink] Autolinking on COFF for Cygwin/MinGW #3887

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 1 commit into from
Aug 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/Testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ code for the target that is not the build machine:
``armv7``, ``armv7k``, ``arm64``).

* ``%target-os``: the target operating system (``macosx``, ``darwin``,
``linux``, ``freebsd``).
``linux``, ``freebsd``, ``windows-cygnus``, ``windows-gnu``).

* ``%target-object-format``: the platform's object format (``elf``, ``macho``,
``coff``).
Expand Down
3 changes: 2 additions & 1 deletion lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1316,7 +1316,8 @@ void Driver::buildActions(const ToolChain &TC,
if (OI.shouldLink() && !AllLinkerInputs.empty()) {
auto *LinkAction = new LinkJobAction(AllLinkerInputs, OI.LinkAction);

if (TC.getTriple().getObjectFormat() == llvm::Triple::ELF) {
if (TC.getTriple().getObjectFormat() == llvm::Triple::ELF ||
TC.getTriple().isOSCygMing()) {
// On ELF platforms there's no built in autolinking mechanism, so we
// pull the info we need from the .o files directly and pass them as an
// argument input file to the linker.
Expand Down
1 change: 1 addition & 0 deletions test/AutolinkExtract/empty.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
// REQUIRES: autolink-extract

// CHECK-elf: -lswiftCore
// CHECK-coff: -lswiftCore
1 change: 1 addition & 0 deletions test/AutolinkExtract/empty_archive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
// REQUIRES: autolink-extract

// CHECK-elf: -lswiftCore
// CHECK-coff: -lswiftCore
3 changes: 3 additions & 0 deletions test/AutolinkExtract/import.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
// CHECK-elf-DAG: -lswiftCore
// CHECK-elf-DAG: -lempty

// CHECK-coff-DAG: -lswiftCore
// CHECK-coff-DAG: -lempty

import empty
3 changes: 3 additions & 0 deletions test/AutolinkExtract/import_archive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@
// CHECK-elf-DAG: -lswiftCore
// CHECK-elf-DAG: -lempty

// CHECK-coff-DAG: -lswiftCore
// CHECK-coff-DAG: -lempty

import empty
2 changes: 2 additions & 0 deletions test/AutolinkExtract/linker-order.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
; RUN: llc -mtriple armv7--linux-gnueabihf -filetype obj -o - %s | %target-swift-autolink-extract -o - - | FileCheck %s
; RUN: llc -mtriple x86_64--windows-gnu -filetype obj -o - %s | %target-swift-autolink-extract -o - - | FileCheck %s
; RUN: llc -mtriple x86_64--windows-cygnus -filetype obj -o - %s | %target-swift-autolink-extract -o - - | FileCheck %s
; REQUIRES: autolink-extract

; Ensure that the options in the object file preserve ordering. The linker
Expand Down
25 changes: 19 additions & 6 deletions test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -691,17 +691,30 @@ if run_vendor == 'apple':
"%s clang++ %s" %
(xcrun_prefix, config.target_cc_options))

elif run_os == 'linux-gnu' or run_os == 'linux-gnueabihf' or run_os == 'freebsd':
# Linux/FreeBSD
if run_os == 'freebsd':
elif run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'windows-cygnus', 'windows-gnu']:
# Linux/FreeBSD/Cygwin
if run_os == 'windows-cygnus':
lit_config.note("Testing Cygwin " + config.variant_triple)
config.target_object_format = "coff"
config.target_dylib_extension = "dll"
config.target_sdk_name = "cygwin"
elif run_os == 'windows-gnu':
lit_config.note("Testing MinGW " + config.variant_triple)
config.target_object_format = "coff"
config.target_dylib_extension = "dll"
config.target_sdk_name = "mingw"
elif run_os == 'freebsd':
lit_config.note("Testing FreeBSD " + config.variant_triple)
config.target_object_format = "elf"
config.target_dylib_extension = "so"
config.target_sdk_name = "freebsd"
else:
lit_config.note("Testing Linux " + config.variant_triple)
config.target_object_format = "elf"
config.target_dylib_extension = "so"
config.target_object_format = "elf"
config.target_dylib_extension = "so"
config.target_sdk_name = "linux"
config.target_runtime = "native"
config.target_swift_autolink_extract = inferSwiftBinary("swift-autolink-extract")
config.target_sdk_name = "freebsd" if run_os == "freebsd" else "linux"
config.target_build_swift = (
'%s -target %s %s %s %s %s'
% (config.swiftc, config.variant_triple, resource_dir_opt, mcp_opt,
Expand Down
55 changes: 34 additions & 21 deletions tools/driver/autolink_extract_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "llvm/Option/Option.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ELFObjectFile.h"

using namespace swift;
Expand Down Expand Up @@ -106,32 +107,44 @@ class AutolinkExtractInvocation {
}
};

// Look inside the binary 'Bin' and append any linker flags found in its
// ".swift1_autolink_entries" section to 'LinkerFlags'. If 'Bin' is an archive,
// recursively look inside all children within the archive. Return 'true' if
// there was an error, and 'false' otherwise.
/// Look inside the object file 'ObjectFile' and append any linker flags found in
/// its ".swift1_autolink_entries" section to 'LinkerFlags'.
static void
extractLinkerFlagsFromObjectFile(const llvm::object::ObjectFile *ObjectFile,
std::vector<std::string> &LinkerFlags) {
// Search for the section we hold autolink entries in
for (auto &Section : ObjectFile->sections()) {
llvm::StringRef SectionName;
Section.getName(SectionName);
if (SectionName == ".swift1_autolink_entries") {
Copy link
Member

Choose a reason for hiding this comment

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

This isn't valid for COFF. COFF has a 8-character limit on section names.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I quoted the description of the name field in 3. Section Table (Section Headers) from MS PECOFF.

... For longer names, this field contains a slash (/) that is followed by an ASCII 
representation of a decimal number that is an offset into the string table.
Executable images do not use a string table and do not support section names longer
than 8 characters. Long names in object files are truncated if they are emitted to 
an executable file.

Because the section '.swift1_autolink_entries' will not be emitted to an executable file, we can use the long name for autolink feature. But we can not use the long names '.swift2_protocol_conformances' and '.swift2_type_metadata' instead of '.sw2prtc' or '.sw2tymd', because they are emitted to an executable and loaded into the system memory.

llvm::StringRef SectionData;
Section.getContents(SectionData);

// entries are null-terminated, so extract them and push them into
// the set.
llvm::SmallVector<llvm::StringRef, 4> SplitFlags;
SectionData.split(SplitFlags, llvm::StringRef("\0", 1), -1,
/*KeepEmpty=*/false);
for (const auto &Flag : SplitFlags)
LinkerFlags.push_back(Flag);
}
}
}

/// Look inside the binary 'Bin' and append any linker flags found in its
/// ".swift1_autolink_entries" section to 'LinkerFlags'. If 'Bin' is an archive,
/// recursively look inside all children within the archive. Return 'true' if
/// there was an error, and 'false' otherwise.
static bool extractLinkerFlags(const llvm::object::Binary *Bin,
CompilerInstance &Instance,
StringRef BinaryFileName,
std::vector<std::string> &LinkerFlags) {
if (auto *ObjectFile = llvm::dyn_cast<llvm::object::ELFObjectFileBase>(Bin)) {
// Search for the section we hold autolink entries in
for (auto &Section : ObjectFile->sections()) {
llvm::StringRef SectionName;
Section.getName(SectionName);
if (SectionName == ".swift1_autolink_entries") {
llvm::StringRef SectionData;
Section.getContents(SectionData);

// entries are null-terminated, so extract them and push them into
// the set.
llvm::SmallVector<llvm::StringRef, 4> SplitFlags;
SectionData.split(SplitFlags, llvm::StringRef("\0", 1), -1,
/*KeepEmpty=*/false);
for (const auto &Flag : SplitFlags)
LinkerFlags.push_back(Flag);
}
}
extractLinkerFlagsFromObjectFile(ObjectFile, LinkerFlags);
return false;
} else if (auto *ObjectFile =
llvm::dyn_cast<llvm::object::COFFObjectFile>(Bin)) {
extractLinkerFlagsFromObjectFile(ObjectFile, LinkerFlags);
Copy link
Member

Choose a reason for hiding this comment

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

Why duplicate the same logic? It seems that you should do:

if (auto *OF = llvm::dyn_cast<llvm::object::ObjectFile>(Bin))
  extractLinkerFlagsFromObjectFile(ObjectFile, OF, LinkerFlags);

Copy link
Contributor Author

@tinysun212 tinysun212 Aug 7, 2016

Choose a reason for hiding this comment

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

That's a good point.

I considered the side effect that the MachO object files would not generate the error (of message "Don't know how to extract from object file").

If we don't care for the handling of MachO object files, the patch can be more simpler.
@gribozavr, How do you think about this ?

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 don't care about MachO files being passed to this utility. This utility is not user-facing, it is only invoked by the driver.

return false;
} else if (auto *Archive = llvm::dyn_cast<llvm::object::Archive>(Bin)) {
for (const auto &Child : Archive->children()) {
Expand Down