Support darwin linker options:

-current_version, -compatibility_version, and -install_name.

Patch by Joe Ranieri

llvm-svn: 190452
This commit is contained in:
Nick Kledzik 2013-09-10 23:55:14 +00:00
parent e850d9dedd
commit e773e327d3
5 changed files with 193 additions and 1 deletions

View File

@ -72,6 +72,52 @@ public:
void setDoNothing(bool value) { _doNothing = value; }
bool doNothing() const { return _doNothing; }
/// \brief The dylib's binary compatibility version, in the raw uint32 format.
///
/// When building a dynamic library, this is the compatibility version that
/// gets embedded into the result. Other Mach-O binaries that link against
/// this library will store the compatibility version in its load command. At
/// runtime, the loader will verify that the binary is compatible with the
/// installed dynamic library.
uint32_t compatibilityVersion() const { return _compatibilityVersion; }
/// \brief The dylib's current version, in the the raw uint32 format.
///
/// When building a dynamic library, this is the current version that gets
/// embedded into the result. Other Mach-O binaries that link against
/// this library will store the compatibility version in its load command.
uint32_t currentVersion() const { return _currentVersion; }
/// \brief The dylib's install name.
///
/// Binaries that link against the dylib will embed this path into the dylib
/// load command. When loading the binaries at runtime, this is the location
/// on disk that the loader will look for the dylib.
StringRef installName() const { return _installName; }
/// \brief Whether or not the dylib has side effects during initialization.
///
/// Dylibs marked as being dead strippable provide the guarantee that loading
/// the dylib has no side effects, allowing the linker to strip out the dylib
/// when linking a binary that does not use any of its symbols.
bool deadStrippableDylib() const { return _deadStrippableDylib; }
/// \brief The path to the executable that will load the bundle at runtime.
///
/// When building a Mach-O bundle, this executable will be examined if there
/// are undefined symbols after the main link phase. It is expected that this
/// binary will be loading the bundle at runtime and will provide the symbols
/// at that point.
StringRef bundleLoader() const { return _bundleLoader; }
void setCompatibilityVersion(uint32_t vers) { _compatibilityVersion = vers; }
void setCurrentVersion(uint32_t vers) { _currentVersion = vers; }
void setInstallName(StringRef name) { _installName = name; }
void setDeadStrippableDylib(bool deadStrippable) {
_deadStrippableDylib = deadStrippable;
}
void setBundleLoader(StringRef loader) { _bundleLoader = loader; }
static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype);
static Arch archFromName(StringRef archName);
static uint32_t cpuTypeFromArch(Arch arch);
@ -91,6 +137,11 @@ private:
OS _os;
uint32_t _osMinVersion;
uint64_t _pageZeroSize;
uint32_t _compatibilityVersion;
uint32_t _currentVersion;
StringRef _installName;
bool _deadStrippableDylib;
StringRef _bundleLoader;
mutable std::unique_ptr<mach_o::KindHandler> _kindHandler;
mutable std::unique_ptr<Reader> _machoReader;
mutable std::unique_ptr<Writer> _writer;

View File

@ -154,6 +154,47 @@ bool DarwinLdDriver::parse(int argc, const char *argv[],
if (parsedArgs->getLastArg(OPT_all_load))
globalWholeArchive = true;
// Handle -install_name
if (llvm::opt::Arg *installName = parsedArgs->getLastArg(OPT_install_name))
ctx.setInstallName(installName->getValue());
// Handle -mark_dead_strippable_dylib
if (parsedArgs->getLastArg(OPT_mark_dead_strippable_dylib))
ctx.setDeadStrippableDylib(true);
// Handle -compatibility_version and -current_version
if (llvm::opt::Arg *vers =
parsedArgs->getLastArg(OPT_compatibility_version)) {
if (ctx.outputFileType() != mach_o::MH_DYLIB) {
diagnostics
<< "error: -compatibility_version can only be used with -dylib\n";
return true;
}
uint32_t parsedVers;
if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
diagnostics << "error: -compatibility_version value is malformed\n";
return true;
}
ctx.setCompatibilityVersion(parsedVers);
}
if (llvm::opt::Arg *vers = parsedArgs->getLastArg(OPT_current_version)) {
if (ctx.outputFileType() != mach_o::MH_DYLIB) {
diagnostics << "-current_version can only be used with -dylib\n";
return true;
}
uint32_t parsedVers;
if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
diagnostics << "error: -current_version value is malformed\n";
return true;
}
ctx.setCurrentVersion(parsedVers);
}
// Handle -bundle_loader
if (llvm::opt::Arg *loader = parsedArgs->getLastArg(OPT_bundle_loader))
ctx.setBundleLoader(loader->getValue());
// Handle -arch xxx
if (llvm::opt::Arg *archStr = parsedArgs->getLastArg(OPT_arch)) {
ctx.setArch(MachOLinkingContext::archFromName(archStr->getValue()));

View File

@ -37,6 +37,30 @@ def entry : Separate<["-"], "e">, HelpText<"entry symbol name">,Group<grp_main>;
// dylib executable options
def grp_dylib : OptionGroup<"opts">, HelpText<"DYLIB EXECUTABLE OPTIONS">;
def install_name : Separate<["-"], "install_name">,
HelpText<"The dylib's install name">, Group<grp_dylib>;
def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">,
HelpText<"Marks the dylib as having no side effects during initialization">,
Group<grp_dylib>;
def compatibility_version : Separate<["-"], "compatibility_version">,
HelpText<"The dylib's compatibility version">, Group<grp_dylib>;
def current_version : Separate<["-"], "current_version">,
HelpText<"The dylib's current version">, Group<grp_dylib>;
// dylib executable options - compatibility aliases
def dylib_install_name : Separate<["-"], "dylib_install_name">,
Alias<install_name>;
def dylib_compatibility_version :
Separate<["-"], "dylib_compatibility_version">,
Alias<compatibility_version>;
def dylib_current_version : Separate<["-"], "dylib_current_version">,
Alias<current_version>;
// bundle executable options
def grp_bundle : OptionGroup<"opts">, HelpText<"BUNDLE EXECUTABLE OPTIONS">;
def bundle_loader : Separate<["-"], "bundle_loader">,
HelpText<"The executable that will be loading this Mach-O bundle">,
Group<grp_bundle>;
// library options
def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARY OPTIONS">;

View File

@ -124,7 +124,8 @@ uint32_t MachOLinkingContext::cpuSubtypeFromArch(Arch arch) {
MachOLinkingContext::MachOLinkingContext()
: _outputFileType(mach_o::MH_EXECUTE), _outputFileTypeStatic(false),
_doNothing(false), _arch(arch_unknown), _os(OS::macOSX), _osMinVersion(0),
_pageZeroSize(0x1000), _kindHandler(nullptr) {}
_pageZeroSize(0x1000), _compatibilityVersion(0), _currentVersion(0),
_deadStrippableDylib(false), _kindHandler(nullptr) {}
MachOLinkingContext::~MachOLinkingContext() {}
@ -204,6 +205,29 @@ bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) {
}
}
if (_currentVersion && _outputFileType != mach_o::MH_DYLIB) {
diagnostics << "error: -current_version can only be used with dylibs\n";
return true;
}
if (_compatibilityVersion && _outputFileType != mach_o::MH_DYLIB) {
diagnostics
<< "error: -compatibility_version can only be used with dylibs\n";
return true;
}
if (_deadStrippableDylib && _outputFileType != mach_o::MH_DYLIB) {
diagnostics
<< "error: -mark_dead_strippable_dylib can only be used with dylibs.\n";
return true;
}
if (!_bundleLoader.empty() && outputFileType() != mach_o::MH_BUNDLE) {
diagnostics
<< "error: -bundle_loader can only be used with Mach-O bundles\n";
return true;
}
return false;
}

View File

@ -169,4 +169,56 @@ TEST_F(DarwinLdParserTest, iOS_Simulator6) {
EXPECT_TRUE(_context.minOS("", "6.0"));
}
TEST_F(DarwinLdParserTest, compatibilityVersion) {
EXPECT_FALSE(
parse("ld", "-dylib", "-compatibility_version", "1.2.3", "a.o", nullptr));
EXPECT_EQ(_context.compatibilityVersion(), 0x10203U);
}
TEST_F(DarwinLdParserTest, compatibilityVersionInvalidType) {
EXPECT_TRUE(parse("ld", "-bundle", "-compatibility_version", "1.2.3", "a.o",
nullptr));
}
TEST_F(DarwinLdParserTest, compatibilityVersionInvalidValue) {
EXPECT_TRUE(parse("ld", "-bundle", "-compatibility_version", "1,2,3", "a.o",
nullptr));
}
TEST_F(DarwinLdParserTest, currentVersion) {
EXPECT_FALSE(
parse("ld", "-dylib", "-current_version", "1.2.3", "a.o", nullptr));
EXPECT_EQ(_context.currentVersion(), 0x10203U);
}
TEST_F(DarwinLdParserTest, currentVersionInvalidType) {
EXPECT_TRUE(
parse("ld", "-bundle", "-current_version", "1.2.3", "a.o", nullptr));
}
TEST_F(DarwinLdParserTest, currentVersionInvalidValue) {
EXPECT_TRUE(
parse("ld", "-bundle", "-current_version", "1,2,3", "a.o", nullptr));
}
TEST_F(DarwinLdParserTest, bundleLoader) {
EXPECT_FALSE(
parse("ld", "-bundle", "-bundle_loader", "/bin/ls", "a.o", nullptr));
EXPECT_EQ(_context.bundleLoader(), "/bin/ls");
}
TEST_F(DarwinLdParserTest, bundleLoaderInvalidType) {
EXPECT_TRUE(parse("ld", "-bundle_loader", "/bin/ls", "a.o", nullptr));
}
TEST_F(DarwinLdParserTest, deadStrippableDylib) {
EXPECT_FALSE(
parse("ld", "-dylib", "-mark_dead_strippable_dylib", "a.o", nullptr));
EXPECT_EQ(true, _context.deadStrippableDylib());
}
TEST_F(DarwinLdParserTest, deadStrippableDylibInvalidType) {
EXPECT_TRUE(parse("ld", "-mark_dead_strippable_dylib", "a.o", nullptr));
}
} // end anonymous namespace