Loading...
--- dyld/dyld-1285.19/shared_cache_linker/SharedCacheLinker.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (c) 2024 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include "SharedCacheLinker.h"
-#include "AtomFileFormat.h"
-#include "SharedCacheLinker_private.h"
-
-// mach-o
-#include "Architecture.h"
-#include "Atom.h"
-#include "Containers.h"
-#include "Diagnostics.h"
-#include "StringUtils.h"
-
-// ld
-#include "Layout.h"
-#include "AtomFileConsolidator.h"
-#include "DynamicAtom.h"
-#include "JSONReader.h"
-#include "Linker.h"
-
-// Darwin
-#include <mach-o/loader.h>
-#include <sys/mman.h>
-
-using namespace ld;
-using namespace mach_o;
-using namespace json;
-
-struct JSONHeader
-{
- uint64_t version;
- PlatformAndVersions pvs;
- Architecture arch;
- std::string installName;
- std::string options;
- const Node* atoms=nullptr;
- const Node* dylibs=nullptr;
- const Node* customSections=nullptr;
-};
-
-// map to fill references to dylibIndex fixups
-using DylibIndexMap = StringViewMap<uint16_t>;
-
-static const Fixup::Kind fixupKindAndSizeFromString(CString str, uint32_t& size)
-{
- if ( str == "ptr64" ) {
- size = 8;
- return Fixup::Kind::ptr64;
- }
-
- if ( str == "ptr32" ) {
- size = 4;
- return Fixup::Kind::ptr32;
- }
-
- if ( str == "arm64_auth_ptr" ) {
- size = 8;
- return Fixup::Kind::arm64_auth_ptr;
- }
-
- if ( str == "dylibIndex" ) {
- size = 2;
- return Fixup::Kind::dylibIndex;
- }
-
- return Fixup::Kind::none;
-}
-
-static Atom::ContentType contentTypeFromString(CString str)
-{
- if ( str == "constText" )
- return Atom::ContentType::constText;
- if ( str == "cstring" )
- return Atom::ContentType::cstringLiteral;
- if ( str == "data" )
- return Atom::ContentType::data;
- if ( str == "constData" )
- return Atom::ContentType::constData;
- if ( str == "objcData" )
- return Atom::ContentType::objcData;
- if ( str == "objcConst" )
- return Atom::ContentType::objcConst;
- if ( str == "custom" )
- return Atom::ContentType::custom;
- // ld-prime doesn't need to understand these content types (yet)
- // so this uses a custom type with a custom section
- if ( str == "pointerHashTable" )
- return Atom::ContentType::custom;
- if ( str == "pointerHashTableKey" )
- return Atom::ContentType::custom;
-
- return Atom::ContentType::invalid;
-}
-
-
-static void parseHeader(Diagnostics& diag, JSONHeader& header, const Node& rootNode)
-{
- header.version = parseRequiredInt(diag, getRequiredValue(diag, rootNode, "version"));
- if ( diag.hasError() )
- return;
-
- if ( header.version != 1 ) {
- diag.error("JSON version not supported: %llu", header.version);
- return;
- }
-
- uint32_t rawPlatform = (uint32_t)parseRequiredInt(diag, getRequiredValue(diag, rootNode, "platform"));
- const std::string& rawPlatformVersion = parseRequiredString(diag, getRequiredValue(diag, rootNode, "platformVersion"));
- const std::string& rawArch = parseRequiredString(diag, getRequiredValue(diag, rootNode, "arch"));
- header.installName = parseRequiredString(diag, getRequiredValue(diag, rootNode, "installName"));
- if ( diag.hasError() )
- return;
-
- header.arch = Architecture::byName(rawArch);
- if ( header.arch == Architecture::invalid ) {
- diag.error("%s is not a valid architecture name", rawArch.c_str());
- return;
- }
-
- Platform platform(rawPlatform);
- if ( Error err = platform.valid() ) {
- diag.error(err);
- return;
- }
-
- Version32 ver;
- if ( Error err = Version32::fromString(rawPlatformVersion, ver) ) {
- diag.error(err);
- return;
- }
-
- header.pvs.platform = platform;
- header.pvs.minOS = ver;
- header.pvs.sdk = ver;
-
- header.atoms = &getRequiredValue(diag, rootNode, "atoms");
- header.dylibs = &getRequiredValue(diag, rootNode, "dylibs");
- header.customSections = getOptionalValue(diag, rootNode, "customSections");
-
- if ( const Node* node = getOptionalValue(diag, rootNode, "options") ) {
- header.options = parseRequiredString(diag, *node);
- }
-}
-
-static DynamicAtomFile* addDylib(Diagnostics& diag, File::Ordinal ordinal, AtomGroup atomGroup, const Node& node)
-{
- const std::string& installName = parseRequiredString(diag, getRequiredValue(diag, node, "installName"));
- Node exports = getRequiredValue(diag, node, "exports");
- if ( diag.hasError() )
- return nullptr;
-
- // TODO: explicit versions?
- Version32 curVer;
- Version32 compatVer;
- DynamicAtomFile* af = new DynamicAtomFile(ordinal, CString::dup(installName), atomGroup);
-
- __block std::vector<uint32_t> rawPlatforms;
- atomGroup.pvs.unzip(^(PlatformAndVersions pvs) {
- rawPlatforms.push_back(pvs.platform.value());
- });
- af->setDylibFileInfo(DylibFileInfo::makeDylibFileInfo(installName, curVer, compatVer, rawPlatforms, {}, {}, {}));
-
- DylibExportsBuilder exportsBuilder(af, curVer, compatVer);
- for ( const Node& exportNode : exports.array ) {
- const std::string& name = parseRequiredString(diag, getRequiredValue(diag, exportNode, "name"));
- bool weakDef = false;
-
- if ( const Node* weakDefNode = getOptionalValue(diag, exportNode, "weakDef") )
- weakDef = parseRequiredBool(diag, *weakDefNode);
-
- if ( diag.hasError() )
- return nullptr;
-
- exportsBuilder.addDylibExport(name, weakDef);
- }
- exportsBuilder.finalize();
- af->setActive(false, true);
- af->reclaimAllocatorResources();
- return af;
-}
-
-static void addFixup(Diagnostics& diag, const Architecture& arch, bool usesAuthPtrs,
- DynamicAtom* atom, uint32_t offset, Fixup::Kind kind, const Atom* targetAtom,
- const Node* addendNode, const Node* authPtrNode)
-{
- int64_t addend = 0;
- if ( addendNode )
- addend = parseRequiredInt(diag, *addendNode);
-
- if ( authPtrNode && kind != Fixup::Kind::arm64_auth_ptr ) {
- diag.error("only arm64_auth_ptr fixups can have 'authPtr' data");
- return;
- }
-
- if ( kind == Fixup::Kind::arm64_auth_ptr ) {
- uint8_t key = 0;
- bool addr = false;
- uint16_t diversity = 0;
-
- if ( !usesAuthPtrs ) {
- diag.error("arm64_auth_ptr fixup can't be used with %s architecture", arch.name());
- return;
- }
-
- if ( authPtrNode ) {
- if ( const Node* keyNode = getOptionalValue(diag, *authPtrNode, "key") )
- key = (uint8_t)parseRequiredInt(diag, *keyNode);
- if ( const Node* addrNode = getOptionalValue(diag, *authPtrNode, "addr") )
- addr = parseRequiredBool(diag, *addrNode);
- if ( const Node* divNode = getOptionalValue(diag, *authPtrNode, "diversity") )
- diversity = (uint16_t)parseRequiredInt(diag, *divNode);
- }
-
- atom->addFixupAuthPointer(offset, targetAtom, key, addr, diversity, (int32_t)addend);
- return;
- }
-
-
- // regular fixup
- atom->addFixup(kind, offset, targetAtom, addend);
-}
-
-static void addAtoms(Diagnostics& diag, DynamicAtomFile* af, const Node& atomsNode, const DylibIndexMap& dylibIndexMap,
- std::span<const DynamicCustomSection> customSections, size_t pointerHashTableSectIndex, size_t pointerHashTableKeySectIndex)
-{
- assert(af->atoms().empty());
-
- // keep track of defined/undefined atom indexes in the atom file
- // indexes are needed to setup fixup targets
- StringViewMap<uint32_t> atomNameToTargetIndex;
- StringViewMap<uint32_t> dylibNameToTargetIndex;
-
- // atom defaults
- const Atom::Alignment ptrSizeAlign(af->is64() ? 3 : 2);
- Architecture arch = af->arch();
- bool usesAuthPtrs = arch.usesArm64AuthPointers();
-
- // in first pass create all defined atoms to fill the name map
- for ( const Node& atomNode : atomsNode.array ) {
- const std::string& name = parseRequiredString(diag, getRequiredValue(diag, atomNode, "name"));
- const std::string& ctName = parseRequiredString(diag, getRequiredValue(diag, atomNode, "contentType"));
-
- if ( diag.hasError() )
- return;
-
- Atom::Scope scope = Atom::Scope::global;
- bool weakDef = false;
- Atom::ContentType ct = contentTypeFromString(ctName);
- Atom::Alignment align = ptrSizeAlign;
-
- if ( ct == Atom::ContentType::invalid ) {
- diag.error("unknown content type: %s", ctName.c_str());
- return;
- }
-
- if ( const Node* alignNode = getOptionalValue(diag, atomNode, "p2align") ) {
- align = Atom::Alignment((int)parseRequiredInt(diag, *alignNode));
-
- if ( diag.hasError() )
- return;
- }
-
- if ( const Node* weakDefNode = getOptionalValue(diag, atomNode, "weakDef") )
- weakDef = parseRequiredBool(diag, *weakDefNode);
-
- // default to 1-byte alignment for string literals
- if ( ct == Atom::ContentType::cstringLiteral )
- align = Atom::Alignment(0);
-
- DynamicAtom* atom = af->makeSymbolAtom(name, ct, scope, weakDef);
- atom->setAlignment(align);
- if ( atomNameToTargetIndex.find(atom->name().str()) != atomNameToTargetIndex.end() ) {
- diag.error("duplicate atom name: %s", name.c_str());
- } else {
- atomNameToTargetIndex[atom->name().str()] = atom->atomOrdinal();
- }
-
- std::optional<uint64_t> customSectIndex;
- if ( const Node* customSection = getOptionalValue(diag, atomNode, "section") ) {
- uint64_t sectIndex = parseRequiredInt(diag, *customSection);
- if ( diag.hasError() )
- return;
- customSectIndex = sectIndex;
- } else {
- if ( ctName == "pointerHashTable" ) {
- customSectIndex = pointerHashTableSectIndex;
- } else if ( ctName == "pointerHashTableKey" ) {
- customSectIndex = pointerHashTableKeySectIndex;;
- }
- }
-
- if ( customSectIndex ) {
- if ( *customSectIndex > customSections.size() ) {
- diag.error("Invalid section index (%llu) in atom %s, max allowed: %lu\n", *customSectIndex, name.c_str(), customSections.size());
- return;
- }
-
- atom->setCustomSection(customSections[*customSectIndex]);
- }
- }
-
- // now parse all atom contents and their fixups
- for ( size_t atomIndex = 0; atomIndex < atomsNode.array.size(); ++atomIndex ) {
- const Node& atomNode = atomsNode.array[atomIndex];
- const Node& contents = getRequiredValue(diag, atomNode, "contents");
- if ( diag.hasError() )
- return;
-
- // note: atoms vector of the atom file can be modified in this loop, so call atoms() each time
- DynamicAtom* atom = (DynamicAtom*)af->atoms()[atomIndex];
- std::vector<uint8_t> bytes;
-
- for ( const Node& contentEntry : contents.array ) {
- if ( contentEntry.map.empty() ) {
- // content bytes string
- std::string bytesFrag = parseRequiredString(diag, contentEntry);
- if ( bytesFrag.size() & 0x1 ) {
- diag.error("odd length (%lu) of content hex string for atom: %s",
- bytesFrag.size(),
- atom->name().c_str());
- return;
- }
-
- for ( size_t i = 0; i < bytesFrag.size() / 2; ++i ) {
- uint8_t high;
- uint8_t low;
- if ( !hexCharToUInt(bytesFrag[i * 2], high) || !hexCharToUInt(bytesFrag[(i * 2) + 1], low) ) {
- diag.error("invalid hex content");
- return;
- }
-
- bytes.push_back((high << 4) | low);
- }
- continue;
- }
-
- // handle fixup
- uint32_t targetIndex;
- const std::string& targetName = parseRequiredString(diag, getRequiredValue(diag, contentEntry, "target"));
- const std::string& kindStr = parseRequiredString(diag, getRequiredValue(diag, contentEntry, "kind"));
- if ( diag.hasError() )
- return;
-
- uint32_t fixupSize = 0;
- Fixup::Kind kind = fixupKindAndSizeFromString(kindStr, fixupSize);
- if ( kind == Fixup::Kind::none ) {
- diag.error("unsupported fixup kind: %s", kindStr.c_str());
- return;
- }
-
- // special case dylibIndex fixups
- // use anon placeholder atoms to turn dylib index fixups into constant values
- // note: alternative could be to use real fixups, but we'd need to teach shared cache builder to patch them too then
- if ( kind == Fixup::Kind::dylibIndex ) {
- if ( auto targetIt = dylibNameToTargetIndex.find(targetName); targetIt != dylibNameToTargetIndex.end() ) {
- targetIndex = targetIt->second;
- } else {
- uint16_t dylibIndex = 0;
- if ( auto dylibIt = dylibIndexMap.find(targetName); dylibIt != dylibIndexMap.end() )
- dylibIndex = dylibIt->second;
- else {
- diag.error("dylib index not found: %s", targetName.c_str());
- return;
- }
- Atom* target = af->makeAnonPlaceholder(dylibIndex);
- targetIndex = target->atomOrdinal();
- }
- } else if ( auto targetIt = atomNameToTargetIndex.find(targetName); targetIt != atomNameToTargetIndex.end() ) {
- targetIndex = targetIt->second;
- } else {
- Atom* target = af->makeUndefine(targetName);
- targetIndex = target->atomOrdinal();
- atomNameToTargetIndex[target->name().str()] = targetIndex;
- }
-
- const Node* addendNode = getOptionalValue(diag, contentEntry, "addend");
- const Node* authPtrNode = getOptionalValue(diag, contentEntry, "authPtr");
- size_t newAtomSize = bytes.size() + fixupSize;
- atom->setContentAsZeros(newAtomSize);
- addFixup(diag, arch, usesAuthPtrs, atom, (uint32_t)bytes.size(), kind,
- af->atoms()[targetIndex], addendNode, authPtrNode);
- if ( diag.hasError() )
- return;
-
- // resize atom's byte to make place for the fixup content
- bytes.resize(newAtomSize);
-
- if ( diag.hasError() )
- return;
- }
- atom->setRawContentBytes(bytes);
- }
-}
-
-Error linkerMakeFromJSON(Linker& linker, std::span<const char> jsonData, std::span<const char*> dylibList, const char* outputPath)
-{
- // read input JSON
- Diagnostics jsonDiag;
- Node rootNode = readJSON(jsonDiag, jsonData.data(), jsonData.size(), false /* useJSON5 */);
- if ( jsonDiag.hasError() )
- return jsonDiag.toError();
-
- // parse JSON header to construct linker options and get root atom nodes
- JSONHeader header;
- parseHeader(jsonDiag, header, rootNode);
- if ( jsonDiag.hasError() )
- return jsonDiag.toError();
-
- // configure linker
- char verStr[32];
- header.pvs.minOS.toString(verStr);
- std::vector<CString> rawArgv{"-arch", header.arch.name(),
- "-platform_version", header.pvs.platform.name(), verStr, verStr,
- "-dylib", "-o", outputPath,
- "-install_name", strdup(header.installName.data()),
- "-add_lldb_no_nlist_section" // rdar://146167046 (Please add `__TEXT,__lldb_no_nlist` section to libswiftPrespecialized.dylib)
- };
- // convert raw options string into options vector
- // this only splits options by a whitespace, no special logic to escape quotes or so
- CString extraArgv = header.options;
- while ( !extraArgv.empty() ) {
- if ( auto spacePos = extraArgv.find(' '); spacePos != CString::npos ) {
- rawArgv.push_back(extraArgv.dupSubstr(0, spacePos));
- extraArgv = extraArgv.substr(spacePos + 1);
- } else {
- rawArgv.push_back(extraArgv.dup());
- extraArgv = CString();
- }
- }
-
- ArgVector argv(std::move(rawArgv));
- File::Ordinal baseOrdinal = argv.nextFileOrdinal();
- if ( Error err = linker.setOptions(std::move(argv)) )
- return jsonDiag.toError();
-
- // create atom file to hold all content atoms
- baseOrdinal = baseOrdinal.nextFileListOrdinal();
- DynamicAtomFile* af = new DynamicAtomFile(baseOrdinal, "json.o",
- {linker.options().output.arch, linker.options().output.pvs});
-
- // create dylib atom files and their exports
- bool hasLibSystem = false;
- baseOrdinal = baseOrdinal.nextFileListOrdinal();
- File::Ordinal reservedLibSystemOrdinal = baseOrdinal;
-
- for ( const Node& dylib : header.dylibs->array ) {
- baseOrdinal = baseOrdinal.nextFileListOrdinal();
-
- DynamicAtomFile* dylibAf = addDylib(jsonDiag, baseOrdinal, af->atomsGroup(), dylib);
- if ( jsonDiag.hasError() )
- return jsonDiag.toError();
-
- assert(dylibAf);
- linker.addAtomFile(dylibAf);
-
- if ( const DylibFileInfo* dylibInfo = dylibAf->dylibFileInfo() )
- hasLibSystem |= dylibInfo->installName().contains("libSystem");
- }
-
- // always link with libSystem
- if ( !hasLibSystem ) {
- DynamicAtomFile* libSystem = new DynamicAtomFile(reservedLibSystemOrdinal, "/usr/lib/libSystem.B.dylib", af->atomsGroup());
-
- __block std::vector<uint32_t> rawPlatforms;
- af->atomsGroup().pvs.unzip(^(PlatformAndVersions pvs) {
- rawPlatforms.push_back(pvs.platform.value());
- });
- libSystem->setDylibFileInfo(DylibFileInfo::makeDylibFileInfo("/usr/lib/libSystem.B.dylib",
- Version32(), Version32(), rawPlatforms, {}, {}, {}));
- linker.addAtomFile(libSystem);
- }
-
- DylibIndexMap dylibIndexMap;
- for ( uint16_t i = 0; i < dylibList.size(); ++i )
- dylibIndexMap[dylibList[i]] = i;
-
- std::vector<DynamicCustomSection> customSections;
- if ( header.customSections ) {
- if ( header.customSections->array.empty() ) {
- return Error("customSections can't be an empty, either add sections or remove the field entirely");
- }
-
- for ( const Node& sectNode : header.customSections->array ) {
- const std::string& perms = parseRequiredString(jsonDiag, getRequiredValue(jsonDiag, sectNode, "segPerms"));
- const std::string& segName = parseRequiredString(jsonDiag, getRequiredValue(jsonDiag, sectNode, "segName"));
- const std::string& sectName = parseRequiredString(jsonDiag, getRequiredValue(jsonDiag, sectNode, "sectName"));
- if ( jsonDiag.hasError() )
- return jsonDiag.toError();
-
- uint32_t segPerms = 0;
- if ( perms.find('r') != std::string::npos )
- segPerms |= PROT_READ;
- if ( perms.find('w') != std::string::npos )
- segPerms |= PROT_WRITE;
- if ( perms.find('x') != std::string::npos )
- segPerms |= PROT_EXEC;
-
- uint32_t sectionFlags = S_REGULAR;
- if ( const Node* flagsNode = getOptionalValue(jsonDiag, sectNode, "sectFlags") )
- sectionFlags = (uint32_t)parseRequiredInt(jsonDiag, *flagsNode);
- if ( jsonDiag.hasError() )
- return jsonDiag.toError();
-
- customSections.push_back(af->makeCustomSection(segPerms, sectionFlags, strdup(segName.c_str()), strdup(sectName.c_str())));
- }
- }
-
- // reserve slots for pointer hash table sections
- size_t pointerHashTableSectIndex = customSections.size();
- customSections.push_back(af->makeCustomSection(PROT_READ | PROT_WRITE, S_REGULAR, "__DATA_CONST", "__ptrhashtab"));
- size_t pointerHashTableKeySectIndex = customSections.size();
- customSections.push_back(af->makeCustomSection(PROT_READ | PROT_WRITE, S_REGULAR, "__DATA_CONST", "__ptrhashtabkey"));
-
- // add all content atoms
- addAtoms(jsonDiag, af, *header.atoms, dylibIndexMap, customSections, pointerHashTableSectIndex, pointerHashTableKeySectIndex);
- if ( jsonDiag.hasError() )
- return jsonDiag.toError();
-
- linker.addAtomFile(af);
- return Error::none();
-}
-
-const char* ldMakeDylibFromJSON(std::span<const char> jsonData, std::span<const char*> dylibList, const char* outputPath)
-{
- Linker linker;
-
- if ( Error err = linkerMakeFromJSON(linker, jsonData, dylibList, outputPath) )
- return strdup(err.message());
-
- if ( Error err = linker.run() )
- return strdup(err.message());
-
- return nullptr;
-}