textstub_dylib_file.cpp [plain text]
#include <sys/param.h>
#include <sys/mman.h>
#include <tapi/tapi.h>
#include <vector>
#include "Architectures.hpp"
#include "bitcode.hpp"
#include "MachOFileAbstraction.hpp"
#include "MachOTrie.hpp"
#include "generic_dylib_file.hpp"
#include "textstub_dylib_file.hpp"
namespace textstub {
namespace dylib {
template <typename A>
class File final : public generic::dylib::File<A>
{
using Base = generic::dylib::File<A>;
public:
File(const char* path, const uint8_t* fileContent, uint64_t fileLength,
time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool enforceDylibSubtypesMatch,
bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
bool logAllFiles, const char* installPath, bool indirectDylib);
virtual ~File() noexcept {}
private:
void buildExportHashTable(const tapi::LinkerInterfaceFile* file);
};
static ld::File::ObjcConstraint mapObjCConstraint(tapi::ObjCConstraint constraint) {
switch (constraint) {
case tapi::ObjCConstraint::None:
return ld::File::objcConstraintNone;
case tapi::ObjCConstraint::Retain_Release:
return ld::File::objcConstraintRetainRelease;
case tapi::ObjCConstraint::Retain_Release_For_Simulator:
return ld::File::objcConstraintRetainReleaseForSimulator;
case tapi::ObjCConstraint::Retain_Release_Or_GC:
return ld::File::objcConstraintRetainReleaseOrGC;
case tapi::ObjCConstraint::GC:
return ld::File::objcConstraintGC;
}
return ld::File::objcConstraintNone;
}
static Options::Platform mapPlatform(tapi::Platform platform) {
switch (platform) {
case tapi::Platform::Unknown:
return Options::kPlatformUnknown;
case tapi::Platform::OSX:
return Options::kPlatformOSX;
case tapi::Platform::iOS:
return Options::kPlatformiOS;
case tapi::Platform::watchOS:
return Options::kPlatformWatchOS;
case tapi::Platform::tvOS:
return Options::kPlatform_tvOS;
#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 2) || (TAPI_API_VERSION_MAJOR > 1))
case tapi::Platform::bridgeOS:
return Options::kPlatform_bridgeOS;
#endif
}
return Options::kPlatformUnknown;
}
template <typename A>
File<A>::File(const char* path, const uint8_t* fileContent, uint64_t fileLength,
time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs, Options::Platform platform,
uint32_t linkMinOSVersion, bool allowWeakImports, cpu_type_t cpuType, cpu_subtype_t cpuSubType,
bool enforceDylibSubtypesMatch, bool allowSimToMacOSX, bool addVers,
bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath,
bool indirectDylib)
: Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
hoistImplicitPublicDylibs, allowSimToMacOSX, addVers)
{
std::unique_ptr<tapi::LinkerInterfaceFile> file;
std::string errorMessage;
#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 1) || (TAPI_API_VERSION_MAJOR > 1))
if (tapi::APIVersion::isAtLeast(1, 1)) {
tapi::ParsingFlags flags = tapi::ParsingFlags::None;
if (enforceDylibSubtypesMatch)
flags |= tapi::ParsingFlags::ExactCpuSubType;
if (!allowWeakImports)
flags |= tapi::ParsingFlags::DisallowWeakImports;
file.reset(tapi::LinkerInterfaceFile::create(
path, fileContent, fileLength, cpuType, cpuSubType, flags,
tapi::PackedVersion32(linkMinOSVersion), errorMessage));
} else {
auto matchingType = enforceDylibSubtypesMatch ?
tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
file.reset(tapi::LinkerInterfaceFile::create(
path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
tapi::PackedVersion32(linkMinOSVersion), errorMessage));
}
#else
auto matchingType = enforceDylibSubtypesMatch ?
tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
file.reset(tapi::LinkerInterfaceFile::create(
path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
tapi::PackedVersion32(linkMinOSVersion), errorMessage));
#endif
if (file == nullptr)
throw strdup(errorMessage.c_str());
munmap((caddr_t)fileContent, fileLength);
if ( logAllFiles )
printf("%s\n", path);
this->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(nullptr, 0));
this->_noRexports = !file->hasReexportedLibraries();
this->_hasWeakExports = file->hasWeakDefinedExports();
this->_dylibInstallPath = strdup(file->getInstallName().c_str());
this->_installPathOverride = file->isInstallNameVersionSpecific();
this->_dylibCurrentVersion = file->getCurrentVersion();
this->_dylibCompatibilityVersion = file->getCompatibilityVersion();
this->_swiftVersion = file->getSwiftVersion();
this->_objcConstraint = mapObjCConstraint(file->getObjCConstraint());
this->_parentUmbrella = file->getParentFrameworkName().empty() ? nullptr : strdup(file->getParentFrameworkName().c_str());
this->_appExtensionSafe = file->isApplicationExtensionSafe();
const char* lastSlash = strrchr(this->_dylibInstallPath, '/');
if ( lastSlash != NULL ) {
const char* leafName = lastSlash+1;
char frname[strlen(leafName)+32];
strcpy(frname, leafName);
strcat(frname, ".framework/");
if ( strstr(this->_dylibInstallPath, frname) != NULL )
this->_frameworkName = leafName;
}
for (auto &client : file->allowableClients())
this->_allowableClients.push_back(strdup(client.c_str()));
this->_hasPublicInstallName = file->hasAllowableClients() ? false : this->isPublicLocation(file->getInstallName().c_str());
for (const auto &client : file->allowableClients())
this->_allowableClients.emplace_back(strdup(client.c_str()));
auto dylibPlatform = mapPlatform(file->getPlatform());
if ( (dylibPlatform != platform) && (platform != Options::kPlatformUnknown) ) {
this->_wrongOS = true;
if ( this->_addVersionLoadCommand && !indirectDylib ) {
if ( buildingForSimulator ) {
if ( !this->_allowSimToMacOSXLinking )
throwf("building for %s simulator, but linking against dylib built for %s (%s).",
Options::platformName(platform), Options::platformName(dylibPlatform), path);
} else {
throwf("building for %s, but linking against dylib built for %s (%s).",
Options::platformName(platform), Options::platformName(dylibPlatform), path);
}
}
}
for (const auto& reexport : file->reexportedLibraries()) {
const char *path = strdup(reexport.c_str());
if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) )
this->_dependentDylibs.emplace_back(path, true);
}
for (const auto& symbol : file->ignoreExports())
this->_ignoreExports.insert(strdup(symbol.c_str()));
if ( linkingFlatNamespace && linkingMainExecutable && (file->hasTwoLevelNamespace() == false) ) {
std::vector<const char*> importNames;
importNames.reserve(file->undefineds().size());
for (const auto &sym : file->undefineds())
importNames.emplace_back(sym.getName().c_str());
this->_importAtom = new generic::dylib::ImportAtom<A>(*this, importNames);
}
buildExportHashTable(file.get());
}
template <typename A>
void File<A>::buildExportHashTable(const tapi::LinkerInterfaceFile* file) {
if (this->_s_logHashtable )
fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path());
for (const auto &sym : file->exports()) {
const char* name = sym.getName().c_str();
bool weakDef = sym.isWeakDefined();
bool tlv = sym.isThreadLocalValue();
typename Base::AtomAndWeak bucket = { nullptr, weakDef, tlv, 0 };
if ( this->_s_logHashtable )
fprintf(stderr, " adding %s to hash table for %s\n", name, this->path());
this->_atoms[strdup(name)] = bucket;
}
}
template <typename A>
class Parser
{
public:
using P = typename A::P;
static ld::dylib::File* parse(const char* path, const uint8_t* fileContent,
uint64_t fileLength, time_t mTime,
ld::File::Ordinal ordinal, const Options& opts,
bool indirectDylib)
{
return new File<A>(path, fileContent, fileLength,mTime, ordinal,
opts.flatNamespace(),
opts.linkingMainExecutable(),
opts.implicitlyLinkIndirectPublicDylibs(),
opts.platform(),
opts.minOSversion(),
opts.allowWeakImports(),
opts.architecture(),
opts.subArchitecture(),
opts.enforceDylibSubtypesMatch(),
opts.allowSimulatorToLinkWithMacOSX(),
opts.addVersionLoadCommand(),
opts.targetIOSSimulator(),
opts.logAllFiles(),
opts.installPath(),
indirectDylib);
}
};
ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
bool bundleLoader, bool indirectDylib)
{
switch ( opts.architecture() ) {
#if SUPPORT_ARCH_x86_64
case CPU_TYPE_X86_64:
if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
return Parser<x86_64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
#endif
#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
return Parser<x86>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
#endif
#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
return Parser<arm>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
#endif
#if SUPPORT_ARCH_arm64
case CPU_TYPE_ARM64:
if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
return Parser<arm64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
#endif
}
return nullptr;
}
} }