Fix an issue on COFF/ELF targets where the runtime would register each loaded image twice (at least early on.)

This commit is contained in:
Jonathan Grynspan
2021-12-17 13:17:18 -05:00
parent d1bb98b11e
commit 099fdc2e41
12 changed files with 222 additions and 101 deletions

View File

@@ -39,7 +39,25 @@ typedef struct MetadataSectionRange {
/// Identifies the address space ranges for the Swift metadata required by the Swift runtime.
struct MetadataSections {
__swift_uintptr_t version;
__swift_uintptr_t reserved;
/// The base address of the image where this metadata section was defined, as
/// reported when the section was registered with the Swift runtime.
///
/// The value of this field is equivalent to the value of
/// \c SymbolInfo::baseAddress as returned from \c lookupSymbol() for a symbol
/// in the image that contains these sections.
///
/// For Mach-O images, set this field to \c __dso_handle (i.e. the Mach header
/// for the image.) For ELF images, set it to \c __dso_handle (the runtime
/// will adjust it to the start of the ELF image when the image is loaded.)
/// For COFF images, set this field to \c __ImageBase.
///
/// For platforms that have a single statically-linked image or no dynamic
/// loader (i.e. no equivalent of \c __dso_handle or \c __ImageBase), this
/// field is ignored and should be set to \c nullptr.
///
/// \sa swift_addNewDSOImage()
const void *baseAddress;
/// `next` and `prev` are used by the runtime to construct a
/// circularly doubly linked list to quickly iterate over the metadata

View File

@@ -98,8 +98,8 @@ static void _registerAccessibleFunctions(AccessibleFunctionsState &C,
C.SectionsToScan.push_back(section);
}
void swift::addImageAccessibleFunctionsBlockCallbackUnsafe(const void *functions,
uintptr_t size) {
void swift::addImageAccessibleFunctionsBlockCallbackUnsafe(
const void *baseAddress, const void *functions, uintptr_t size) {
assert(
size % sizeof(AccessibleFunctionRecord) == 0 &&
"accessible function section not a multiple of AccessibleFunctionRecord");
@@ -108,10 +108,10 @@ void swift::addImageAccessibleFunctionsBlockCallbackUnsafe(const void *functions
_registerAccessibleFunctions(C, AccessibleFunctionsSection{functions, size});
}
void swift::addImageAccessibleFunctionsBlockCallback(const void *functions,
uintptr_t size) {
void swift::addImageAccessibleFunctionsBlockCallback(
const void *baseAddress, const void *functions, uintptr_t size) {
Functions.get();
addImageAccessibleFunctionsBlockCallbackUnsafe(functions, size);
addImageAccessibleFunctionsBlockCallbackUnsafe(baseAddress, functions, size);
}
static const AccessibleFunctionRecord *

View File

@@ -77,22 +77,31 @@ void initializeDynamicReplacementLookup();
void initializeAccessibleFunctionsLookup();
// Callbacks to register metadata from an image to the runtime.
void addImageProtocolsBlockCallback(const void *start, uintptr_t size);
void addImageProtocolsBlockCallbackUnsafe(const void *start, uintptr_t size);
void addImageProtocolConformanceBlockCallback(const void *start,
void addImageProtocolsBlockCallback(const void *baseAddress,
const void *start, uintptr_t size);
void addImageProtocolsBlockCallbackUnsafe(const void *baseAddress,
const void *start, uintptr_t size);
void addImageProtocolConformanceBlockCallback(const void *baseAddress,
const void *start,
uintptr_t size);
void addImageProtocolConformanceBlockCallbackUnsafe(const void *start,
void addImageProtocolConformanceBlockCallbackUnsafe(const void *baseAddress,
const void *start,
uintptr_t size);
void addImageTypeMetadataRecordBlockCallback(const void *start,
void addImageTypeMetadataRecordBlockCallback(const void *baseAddress,
const void *start,
uintptr_t size);
void addImageTypeMetadataRecordBlockCallbackUnsafe(const void *start,
void addImageTypeMetadataRecordBlockCallbackUnsafe(const void *baseAddress,
const void *start,
uintptr_t size);
void addImageDynamicReplacementBlockCallback(const void *start, uintptr_t size,
void addImageDynamicReplacementBlockCallback(const void *baseAddress,
const void *start, uintptr_t size,
const void *start2,
uintptr_t size2);
void addImageAccessibleFunctionsBlockCallback(const void *start,
void addImageAccessibleFunctionsBlockCallback(const void *baseAddress,
const void *start,
uintptr_t size);
void addImageAccessibleFunctionsBlockCallbackUnsafe(const void *start,
void addImageAccessibleFunctionsBlockCallbackUnsafe(const void *baseAddress,
const void *start,
uintptr_t size);
int lookupSymbol(const void *address, SymbolInfo *info);

View File

@@ -28,9 +28,10 @@
namespace swift {
#ifndef NDEBUG
static swift::MetadataSections *registered = nullptr;
void record(swift::MetadataSections *sections) {
static void record(swift::MetadataSections *sections) {
if (registered == nullptr) {
registered = sections;
sections->next = sections->prev = sections;
@@ -41,34 +42,70 @@ void record(swift::MetadataSections *sections) {
registered->prev = sections;
}
}
#endif
static const void *
getMetadataSectionBaseAddress(swift::MetadataSections *sections) {
// If the base address was not set by the caller of swift_addNewDSOImage()
// then we can assume that the caller was built against an older version of
// the runtime that did not capture a value for this field. Currently nothing
// is actively using the image's base address outside of tests that are built
// with the runtime/stdlib, so there's no need to try to fix up the value. If
// something in the runtime starts using it, we will want to either:
// 1. Resolve the address from a known-good address like swift5_protocols when
// the image is first loaded (in this function);
// 1. Resolve the address from a known-good address like swift5_protocols when
// the address is first used (and atomically swap the address back so we
// don't incur the cost of lookupSymbol() each time we need it; or
// 3. Introduce an ABI-breaking change so that all binaries are rebuilt and
// start supplying a value for this field.
#ifndef NDEBUG
#if defined(__ELF__)
// If the base address was set but the image is an ELF image, it is going to
// be __dso_handle which is not the value we expect (Dl_info::dli_fbase), so
// we need to fix it up. Since the base address is currently unused by the
// runtime outside tests, we don't normally do this work.
if (auto baseAddress = sections->baseAddress) {
swift::SymbolInfo symbolInfo;
if (lookupSymbol(baseAddress, &symbolInfo) && symbolInfo.baseAddress) {
sections->baseAddress = symbolInfo.baseAddress;
}
}
#endif
#endif
return sections->baseAddress;
}
}
SWIFT_RUNTIME_EXPORT
void swift_addNewDSOImage(const void *addr) {
// We cast off the const in order to update the linked list
// data structure. This is safe to do since we don't touch
// any other fields.
swift::MetadataSections *sections =
static_cast<swift::MetadataSections *>(const_cast<void *>(addr));
void swift_addNewDSOImage(swift::MetadataSections *sections) {
#ifndef NDEBUG
record(sections);
#endif
auto baseAddress = swift::getMetadataSectionBaseAddress(sections);
const auto &protocols_section = sections->swift5_protocols;
const void *protocols = reinterpret_cast<void *>(protocols_section.start);
if (protocols_section.length)
swift::addImageProtocolsBlockCallback(protocols, protocols_section.length);
swift::addImageProtocolsBlockCallback(baseAddress,
protocols, protocols_section.length);
const auto &protocol_conformances = sections->swift5_protocol_conformances;
const void *conformances =
reinterpret_cast<void *>(protocol_conformances.start);
if (protocol_conformances.length)
swift::addImageProtocolConformanceBlockCallback(conformances,
swift::addImageProtocolConformanceBlockCallback(baseAddress, conformances,
protocol_conformances.length);
const auto &type_metadata = sections->swift5_type_metadata;
const void *metadata = reinterpret_cast<void *>(type_metadata.start);
if (type_metadata.length)
swift::addImageTypeMetadataRecordBlockCallback(metadata, type_metadata.length);
swift::addImageTypeMetadataRecordBlockCallback(baseAddress,
metadata,
type_metadata.length);
const auto &dynamic_replacements = sections->swift5_replace;
const auto *replacements =
@@ -77,7 +114,7 @@ void swift_addNewDSOImage(const void *addr) {
const auto &dynamic_replacements_some = sections->swift5_replac2;
const auto *replacements_some =
reinterpret_cast<void *>(dynamic_replacements_some.start);
swift::addImageDynamicReplacementBlockCallback(
swift::addImageDynamicReplacementBlockCallback(baseAddress,
replacements, dynamic_replacements.length, replacements_some,
dynamic_replacements_some.length);
}
@@ -87,70 +124,22 @@ void swift_addNewDSOImage(const void *addr) {
reinterpret_cast<void *>(accessible_funcs_section.start);
if (accessible_funcs_section.length)
swift::addImageAccessibleFunctionsBlockCallback(
functions, accessible_funcs_section.length);
baseAddress, functions, accessible_funcs_section.length);
}
void swift::initializeProtocolLookup() {
const swift::MetadataSections *sections = registered;
while (true) {
const swift::MetadataSectionRange &protocols =
sections->swift5_protocols;
if (protocols.length)
addImageProtocolsBlockCallbackUnsafe(
reinterpret_cast<void *>(protocols.start), protocols.length);
if (sections->next == registered)
break;
sections = sections->next;
}
}
void swift::initializeProtocolConformanceLookup() {
const swift::MetadataSections *sections = registered;
while (true) {
const swift::MetadataSectionRange &conformances =
sections->swift5_protocol_conformances;
if (conformances.length)
addImageProtocolConformanceBlockCallbackUnsafe(
reinterpret_cast<void *>(conformances.start), conformances.length);
if (sections->next == registered)
break;
sections = sections->next;
}
}
void swift::initializeTypeMetadataRecordLookup() {
const swift::MetadataSections *sections = registered;
while (true) {
const swift::MetadataSectionRange &type_metadata =
sections->swift5_type_metadata;
if (type_metadata.length)
addImageTypeMetadataRecordBlockCallbackUnsafe(
reinterpret_cast<void *>(type_metadata.start), type_metadata.length);
if (sections->next == registered)
break;
sections = sections->next;
}
}
void swift::initializeDynamicReplacementLookup() {
}
void swift::initializeAccessibleFunctionsLookup() {
const swift::MetadataSections *sections = registered;
while (true) {
const swift::MetadataSectionRange &functions =
sections->swift5_accessible_functions;
if (functions.length)
addImageAccessibleFunctionsBlockCallbackUnsafe(
reinterpret_cast<void *>(functions.start), functions.length);
if (sections->next == registered)
break;
sections = sections->next;
}
}
#ifndef NDEBUG
@@ -173,9 +162,10 @@ const swift::MetadataSections *swift_getMetadataSection(size_t index) {
}
SWIFT_RUNTIME_EXPORT
const char *swift_getMetadataSectionName(void *metadata_section) {
const char *
swift_getMetadataSectionName(const swift::MetadataSections *section) {
swift::SymbolInfo info;
if (lookupSymbol(metadata_section, &info)) {
if (lookupSymbol(section, &info)) {
if (info.fileName) {
return info.fileName;
}
@@ -183,6 +173,20 @@ const char *swift_getMetadataSectionName(void *metadata_section) {
return "";
}
SWIFT_RUNTIME_EXPORT
void swift_getMetadataSectionBaseAddress(const swift::MetadataSections *section,
void const **out_actual,
void const **out_expected) {
swift::SymbolInfo info;
if (lookupSymbol(section, &info)) {
*out_actual = info.baseAddress;
} else {
*out_actual = nullptr;
}
*out_expected = section->baseAddress;
}
SWIFT_RUNTIME_EXPORT
size_t swift_getMetadataSectionCount() {
if (swift::registered == nullptr)

View File

@@ -52,7 +52,7 @@
namespace swift {
struct MetadataSections;
static constexpr const uintptr_t CurrentSectionMetadataVersion = 1;
static constexpr const uintptr_t CurrentSectionMetadataVersion = 2;
}
struct SectionInfo {
@@ -60,14 +60,27 @@ struct SectionInfo {
const char *data;
};
// Called by injected constructors when a dynamic library is loaded.
/// Called by injected constructors when a dynamic library is loaded.
///
/// \param sections A structure describing the metadata sections in the
/// newly-loaded image.
///
/// \warning The runtime keeps a reference to \a sections and may mutate it, so
/// it \em must be mutable and long-lived (that is, statically or dynamically
/// allocated.) The effect of passing a pointer to a local value is undefined.
SWIFT_RUNTIME_EXPORT
void swift_addNewDSOImage(const void *addr);
void swift_addNewDSOImage(struct swift::MetadataSections *sections);
#ifndef NDEBUG
SWIFT_RUNTIME_EXPORT
const char *swift_getMetadataSectionName(void *metadata_section);
const char *
swift_getMetadataSectionName(const struct swift::MetadataSections *section);
SWIFT_RUNTIME_EXPORT
void swift_getMetadataSectionBaseAddress(
const struct swift::MetadataSections *section,
void const **out_actual, void const **out_expected);
SWIFT_RUNTIME_EXPORT
size_t swift_getMetadataSectionCount();

View File

@@ -54,7 +54,8 @@ using mach_header_platform = mach_header;
#endif
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
void CONSUME_BLOCK(const void *start, uintptr_t size)>
void CONSUME_BLOCK(const void *baseAddress,
const void *start, uintptr_t size)>
void addImageCallback(const mach_header *mh) {
#if __POINTER_WIDTH__ == 64
assert(mh->magic == MH_MAGIC_64 && "loaded non-64-bit image?!");
@@ -70,17 +71,19 @@ void addImageCallback(const mach_header *mh) {
if (!section)
return;
CONSUME_BLOCK(section, size);
CONSUME_BLOCK(mh, section, size);
}
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
void CONSUME_BLOCK(const void *start, uintptr_t size)>
void CONSUME_BLOCK(const void *baseAddress,
const void *start, uintptr_t size)>
void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) {
addImageCallback<SEGMENT_NAME, SECTION_NAME, CONSUME_BLOCK>(mh);
}
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
const char *SEGMENT_NAME2, const char *SECTION_NAME2,
void CONSUME_BLOCK(const void *start, uintptr_t size,
void CONSUME_BLOCK(const void *baseAddress,
const void *start, uintptr_t size,
const void *start2, uintptr_t size2)>
void addImageCallback2Sections(const mach_header *mh) {
#if __POINTER_WIDTH__ == 64
@@ -106,11 +109,12 @@ void addImageCallback2Sections(const mach_header *mh) {
if (!section2)
size2 = 0;
CONSUME_BLOCK(section, size, section2, size2);
CONSUME_BLOCK(mh, section, size, section2, size2);
}
template <const char *SEGMENT_NAME, const char *SECTION_NAME,
const char *SEGMENT_NAME2, const char *SECTION_NAME2,
void CONSUME_BLOCK(const void *start, uintptr_t size,
void CONSUME_BLOCK(const void *baseAddress,
const void *start, uintptr_t size,
const void *start2, uintptr_t size2)>
void addImageCallback2Sections(const mach_header *mh, intptr_t vmaddr_slide) {
addImageCallback2Sections<SEGMENT_NAME, SECTION_NAME,

View File

@@ -22,6 +22,8 @@
#include "ImageInspection.h"
#include "ImageInspectionCommon.h"
extern "C" const char __dso_handle[];
using namespace swift;
#define GET_SECTION_START_AND_SIZE(start, size, _seg, _sec) \
@@ -37,7 +39,7 @@ void swift::initializeProtocolLookup() {
MachOProtocolsSection);
if (start == nullptr || size == 0)
return;
addImageProtocolsBlockCallbackUnsafe(start, size);
addImageProtocolsBlockCallbackUnsafe(__dso_handle, start, size);
}
void swift::initializeProtocolConformanceLookup() {
@@ -47,7 +49,7 @@ void swift::initializeProtocolConformanceLookup() {
MachOProtocolConformancesSection);
if (start == nullptr || size == 0)
return;
addImageProtocolConformanceBlockCallbackUnsafe(start, size);
addImageProtocolConformanceBlockCallbackUnsafe(__dso_handle, start, size);
}
void swift::initializeTypeMetadataRecordLookup() {
void *start;
@@ -56,7 +58,7 @@ void swift::initializeTypeMetadataRecordLookup() {
MachOTypeMetadataRecordSection);
if (start == nullptr || size == 0)
return;
addImageTypeMetadataRecordBlockCallbackUnsafe(start, size);
addImageTypeMetadataRecordBlockCallbackUnsafe(__dso_handle, start, size);
}
void swift::initializeDynamicReplacementLookup() {
@@ -72,7 +74,8 @@ void swift::initializeDynamicReplacementLookup() {
MachODynamicReplacementSection);
if (start2 == nullptr || size2 == 0)
return;
addImageDynamicReplacementBlockCallback(start1, size1, start2, size2);
addImageDynamicReplacementBlockCallback(__dso_handle,
start1, size1, start2, size2);
}
void swift::initializeAccessibleFunctionsLookup() {
void *start;
@@ -81,7 +84,7 @@ void swift::initializeAccessibleFunctionsLookup() {
MachOAccessibleFunctionsSection);
if (start == nullptr || size == 0)
return;
addImageAccessibleFunctionsBlockCallbackUnsafe(start, size);
addImageAccessibleFunctionsBlockCallbackUnsafe(__dso_handle, start, size);
}
#endif // defined(__MACH__) && defined(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION)

View File

@@ -263,6 +263,7 @@ _registerTypeMetadataRecords(TypeMetadataPrivateState &T,
}
void swift::addImageTypeMetadataRecordBlockCallbackUnsafe(
const void *baseAddress,
const void *records, uintptr_t recordsSize) {
assert(recordsSize % sizeof(TypeMetadataRecord) == 0
&& "weird-sized type metadata section?!");
@@ -282,10 +283,12 @@ void swift::addImageTypeMetadataRecordBlockCallbackUnsafe(
recordsBegin, recordsEnd);
}
void swift::addImageTypeMetadataRecordBlockCallback(const void *records,
void swift::addImageTypeMetadataRecordBlockCallback(const void *baseAddress,
const void *records,
uintptr_t recordsSize) {
TypeMetadataRecords.get();
addImageTypeMetadataRecordBlockCallbackUnsafe(records, recordsSize);
addImageTypeMetadataRecordBlockCallbackUnsafe(baseAddress,
records, recordsSize);
}
void
@@ -834,7 +837,8 @@ _registerProtocols(ProtocolMetadataPrivateState &C,
C.SectionsToScan.push_back(ProtocolSection{begin, end});
}
void swift::addImageProtocolsBlockCallbackUnsafe(const void *protocols,
void swift::addImageProtocolsBlockCallbackUnsafe(const void *baseAddress,
const void *protocols,
uintptr_t protocolsSize) {
assert(protocolsSize % sizeof(ProtocolRecord) == 0 &&
"protocols section not a multiple of ProtocolRecord");
@@ -851,10 +855,11 @@ void swift::addImageProtocolsBlockCallbackUnsafe(const void *protocols,
recordsBegin, recordsEnd);
}
void swift::addImageProtocolsBlockCallback(const void *protocols,
void swift::addImageProtocolsBlockCallback(const void *baseAddress,
const void *protocols,
uintptr_t protocolsSize) {
Protocols.get();
addImageProtocolsBlockCallbackUnsafe(protocols, protocolsSize);
addImageProtocolsBlockCallbackUnsafe(baseAddress, protocols, protocolsSize);
}
void swift::swift_registerProtocols(const ProtocolRecord *begin,
@@ -2477,6 +2482,7 @@ public:
} // anonymous namespace
void swift::addImageDynamicReplacementBlockCallback(
const void *baseAddress,
const void *replacements, uintptr_t replacementsSize,
const void *replacementsSome, uintptr_t replacementsSomeSize) {

View File

@@ -530,6 +530,7 @@ static void _registerProtocolConformances(ConformanceState &C,
}
void swift::addImageProtocolConformanceBlockCallbackUnsafe(
const void *baseAddress,
const void *conformances, uintptr_t conformancesSize) {
assert(conformancesSize % sizeof(ProtocolConformanceRecord) == 0 &&
"conformances section not a multiple of ProtocolConformanceRecord");
@@ -574,9 +575,11 @@ void swift::addImageProtocolConformanceBlockCallbackUnsafe(
}
void swift::addImageProtocolConformanceBlockCallback(
const void *baseAddress,
const void *conformances, uintptr_t conformancesSize) {
Conformances.get();
addImageProtocolConformanceBlockCallbackUnsafe(conformances,
addImageProtocolConformanceBlockCallbackUnsafe(baseAddress,
conformances,
conformancesSize);
}

View File

@@ -15,6 +15,8 @@
#include <cstdint>
extern "C" const char __ImageBase[];
#define PASTE_EXPANDED(a,b) a##b
#define PASTE(a,b) PASTE_EXPANDED(a,b)
@@ -62,7 +64,7 @@ static void swift_image_constructor() {
sections = {
swift::CurrentSectionMetadataVersion,
0,
__ImageBase,
nullptr,
nullptr,

View File

@@ -15,6 +15,8 @@
#include <cstddef>
extern "C" const char __dso_handle[];
// Create empty sections to ensure that the start/stop symbols are synthesized
// by the linker. Otherwise, we may end up with undefined symbol references as
// the linker table section was never constructed.
@@ -53,7 +55,7 @@ static void swift_image_constructor() {
sections = {
swift::CurrentSectionMetadataVersion,
0,
__dso_handle,
nullptr,
nullptr,

View File

@@ -0,0 +1,57 @@
// RUN: %target-run-simple-swift
// REQUIRES: executable_test
import StdlibUnittest
// On Darwin platforms, dyld is expected to manage loaded sections/images for us
// so there's no requirement to test that sections are correctly uniqued.
#if INTERNAL_CHECKS_ENABLED && !canImport(Darwin)
@_silgen_name("swift_getMetadataSection")
internal func _getMetadataSection(_ index: UInt) -> UnsafeRawPointer?
@_silgen_name("swift_getMetadataSectionCount")
internal func _getMetadataSectionCount() -> UInt
@_silgen_name("swift_getMetadataSectionName")
internal func _getMetadataSectionName(
_ metadata_section: UnsafeRawPointer
) -> UnsafePointer<CChar>
@_silgen_name("swift_getMetadataSectionBaseAddress")
internal func _getMetadataSectionBaseAddress(
_ metadata_section: UnsafeRawPointer,
_ outActual: UnsafeMutablePointer<UnsafeRawPointer?>,
_ outExpected: UnsafeMutablePointer<UnsafeRawPointer?>
) -> Void
do {
let sectionCount = _getMetadataSectionCount()
expectGT(sectionCount, 0)
var sections = Set<UnsafeRawPointer>()
var images = Set<UnsafeRawPointer?>()
for i in 0 ..< sectionCount {
guard let section = _getMetadataSection(i) else {
fatalError("Section \(i) failed to resolve.")
}
let name = String(cString: _getMetadataSectionName(section))
var actual: UnsafeRawPointer? = nil
var expected: UnsafeRawPointer? = nil
_getMetadataSectionBaseAddress(section, &actual, &expected)
expectEqual(
actual, expected,
"""
Section \(name) was expected at \(String(describing: expected)) but was
found at \(String(describing: actual)) instead.
"""
)
expectFalse(sections.contains(section), "Section \(name) was found twice!")
sections.insert(section)
expectFalse(images.contains(expected), "Image \(name) was found twice!")
images.insert(expected)
}
}
#endif