Skip to content
Open
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
398 changes: 166 additions & 232 deletions include/swift/RemoteInspection/ReflectionContext.h

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions include/swift/StaticMirror/ObjectFileContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class Image {
uint64_t OffsetOrAddress;
};
llvm::DenseMap<uint64_t, DynamicRelocation> DynamicRelocations;
// Storage for relocated segment data (ELF only)
std::vector<std::string> RelocatedSegmentStorage;

void scanMachO(const llvm::object::MachOObjectFile *O);

Expand Down
53 changes: 43 additions & 10 deletions lib/StaticMirror/ObjectFileContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ void Image::scanELFType(const llvm::object::ELFObjectFile<ELFT> *O) {
llvm::consumeError(phdrs.takeError());
}

// Create mutable copies of segment data to apply relocations
std::vector<std::string> relocatedData;
for (auto &ph : *phdrs) {
if (ph.p_filesz == 0)
continue;
Expand All @@ -148,7 +150,9 @@ void Image::scanELFType(const llvm::object::ELFObjectFile<ELFT> *O) {
if (contents.empty() || contents.size() != ph.p_filesz)
continue;

Segments.push_back({ph.p_vaddr, contents});
// Create mutable copy for relocation application
relocatedData.emplace_back(contents.data(), contents.size());
Segments.push_back({ph.p_vaddr, StringRef()});
HeaderAddress = std::min(HeaderAddress, (uint64_t)ph.p_vaddr);
}

Expand All @@ -157,13 +161,39 @@ void Image::scanELFType(const llvm::object::ELFObjectFile<ELFT> *O) {
auto resolverSupports = resolver.first;
auto resolve = resolver.second;

if (!resolverSupports || !resolve)
if (!resolverSupports || !resolve) {
// Even without a resolver, update segments to point to our copies
for (size_t i = 0; i < Segments.size(); ++i) {
RelocatedSegmentStorage.push_back(std::move(relocatedData[i]));
Segments[i].Contents = RelocatedSegmentStorage.back();
}
return;
}

auto machine = O->getELFFile().getHeader().e_machine;
auto relativeRelocType = llvm::object::getELFRelativeRelocationType(machine);
auto globDatRelocType = getELFGlobDatRelocationType(machine);

// Helper to find which segment contains an address and apply relocation
auto applyRelocationToSegment = [&](uint64_t addr, uint64_t value) -> bool {
for (size_t i = 0; i < Segments.size(); ++i) {
uint64_t segStart = Segments[i].Addr;
uint64_t segEnd = segStart + relocatedData[i].size();

if (addr >= segStart && addr < segEnd) {
uint64_t offset = addr - segStart;
// Ensure we have enough space for the pointer
if (offset + sizeof(typename ELFT::uint) <= relocatedData[i].size()) {
// Write the relocated value in target endianness
typename ELFT::uint relocValue = value;
memcpy(&relocatedData[i][offset], &relocValue, sizeof(relocValue));
return true;
}
}
}
return false;
};

for (auto &S : static_cast<const llvm::object::ELFObjectFileBase *>(O)
->dynamic_relocation_sections()) {
bool isRela =
Expand All @@ -174,8 +204,11 @@ void Image::scanELFType(const llvm::object::ELFObjectFile<ELFT> *O) {
// have to do that ourselves.
if (isRela && R.getType() == relativeRelocType) {
auto rela = O->getRela(R.getRawDataRefImpl());
DynamicRelocations.insert(
{R.getOffset(), {{}, HeaderAddress + rela->r_addend}});
uint64_t relocatedValue = HeaderAddress + rela->r_addend;

// Apply the relocation to the segment
applyRelocationToSegment(R.getOffset(), relocatedValue);
DynamicRelocations.insert({R.getOffset(), {{}, relocatedValue}});
continue;
}

Expand Down Expand Up @@ -213,6 +246,12 @@ void Image::scanELFType(const llvm::object::ELFObjectFile<ELFT> *O) {
DynamicRelocations.insert({R.getOffset(), {*name, offset}});
}
}

// Move relocated data to persistent storage and update segment StringRefs
for (size_t i = 0; i < Segments.size(); ++i) {
RelocatedSegmentStorage.push_back(std::move(relocatedData[i]));
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not change the type of Segment's Contents from StringRef to std::string? Doesn't look like you use RelocatedSegmentStorage outside of this function. The StringRef to a reference to a mutable string makes me slightly worried that a code change in the future that mutates the string could cause a dangling pointer.

Segments[i].Contents = RelocatedSegmentStorage.back();
}
}

void Image::scanELF(const llvm::object::ELFObjectFileBase *O) {
Expand All @@ -234,12 +273,6 @@ void Image::scanELF(const llvm::object::ELFObjectFileBase *O) {
} else {
return;
}

// FIXME: ReflectionContext tries to read bits of the ELF structure that
// aren't normally mapped by a phdr. Until that's fixed,
// allow access to the whole file 1:1 in address space that isn't otherwise
// mapped.
Segments.push_back({HeaderAddress, O->getData()});
}

void Image::scanCOFF(const llvm::object::COFFObjectFile *O) {
Expand Down
14 changes: 8 additions & 6 deletions stdlib/public/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ set(swift_runtime_backtracing_sources
# Acknowledge that the following sources are known.
set(LLVM_OPTIONAL_SOURCES
SwiftRT-COFF.cpp
SwiftRT-ELF-WASM.cpp
SwiftRT-ELF.cpp
SwiftRT-WASM.cpp
${swift_runtime_sources}
${swift_runtime_objc_sources}
${swift_runtime_leaks_sources}
Expand Down Expand Up @@ -179,7 +180,7 @@ endforeach()
# with LTO, force swift runtime to compile without LTO for Linux.
add_swift_target_library(swiftImageRegistrationObjectELF
OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE
SwiftRT-ELF-WASM.cpp
SwiftRT-ELF.cpp
C_COMPILE_FLAGS
${SWIFT_RUNTIME_CORE_CXX_FLAGS}
${swift_enable_backtracing}
Expand All @@ -203,7 +204,7 @@ add_swift_target_library(swiftImageRegistrationObjectCOFF

add_swift_target_library(swiftImageRegistrationObjectWASM
OBJECT_LIBRARY IS_STDLIB IS_STDLIB_CORE
SwiftRT-ELF-WASM.cpp
SwiftRT-WASM.cpp
C_COMPILE_FLAGS
${SWIFT_RUNTIME_CORE_CXX_FLAGS}
${swift_enable_backtracing}
Expand All @@ -224,9 +225,10 @@ foreach(sdk ${SWIFT_SDKS})
# to a version which supports it.
# set(swiftrtObject "$<TARGET_OBJECTS:swiftImageRegistrationObject${SWIFT_SDK_${sdk}_OBJECT_FORMAT}-${arch_suffix}>")
set(swiftrtSourceName SwiftRT-${SWIFT_SDK_${sdk}_OBJECT_FORMAT}.cpp)
if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF" OR
"${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM")
set(swiftrtSourceName SwiftRT-ELF-WASM.cpp)
if("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "ELF")
set(swiftrtSourceName SwiftRT-ELF.cpp)
elseif("${SWIFT_SDK_${sdk}_OBJECT_FORMAT}" STREQUAL "WASM")
set(swiftrtSourceName SwiftRT-WASM.cpp)
endif()
set(swiftrtObject ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/swiftImageRegistrationObject${SWIFT_SDK_${sdk}_OBJECT_FORMAT}-${arch_suffix}.dir/${swiftrtSourceName}${CMAKE_C_OUTPUT_EXTENSION})

Expand Down
34 changes: 34 additions & 0 deletions stdlib/public/runtime/MetadataSectionNames.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===--- MetadataSectionNames.def -----------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef HANDLE_SWIFT_SECTION
#error "HANDLE_SWIFT_SECTION(elfname, coffname) not defined"
#endif

// NOTE: This table is ABI. Don't rearrange it!
// NOTE: If adding fields, remember to add the field to ReflectionContext.h

HANDLE_SWIFT_SECTION(swift5_protocols, sw5prt)
HANDLE_SWIFT_SECTION(swift5_protocol_conformances, sw5prtc)
HANDLE_SWIFT_SECTION(swift5_type_metadata, sw5tymd)
HANDLE_SWIFT_SECTION(swift5_typeref, sw5tyrf)
HANDLE_SWIFT_SECTION(swift5_reflstr, sw5rfst)
HANDLE_SWIFT_SECTION(swift5_fieldmd, sw5flmd)
HANDLE_SWIFT_SECTION(swift5_assocty, sw5asty)
HANDLE_SWIFT_SECTION(swift5_replace, sw5repl)
HANDLE_SWIFT_SECTION(swift5_replac2, sw5reps)
HANDLE_SWIFT_SECTION(swift5_builtin, sw5bltn)
HANDLE_SWIFT_SECTION(swift5_capture, sw5cptr)
HANDLE_SWIFT_SECTION(swift5_mpenum, sw5mpen)
HANDLE_SWIFT_SECTION(swift5_accessible_functions, sw5acfn)
HANDLE_SWIFT_SECTION(swift5_runtime_attributes, sw5ratt)
HANDLE_SWIFT_SECTION(swift5_tests, sw5test)
188 changes: 188 additions & 0 deletions stdlib/public/runtime/SwiftRT-ELF.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//===--- SwiftRT-ELF.cpp --------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "ImageInspectionCommon.h"
#include "swift/Runtime/Backtrace.h"
#include "swift/Runtime/Config.h"
#include "swift/shims/MetadataSections.h"

#include <cstddef>
#include <new>

#define SWIFT_METADATA_SEGMENT_TYPE 0x73356d64 // s5md or dms5 on little-endian

extern "C" const char __ehdr_start[] __attribute__((__weak__));

#if SWIFT_ENABLE_BACKTRACING
// Drag in a symbol from the backtracer, to force the static linker to include
// the code.
static const void *__backtraceRef __attribute__((used, retain)) =
(const void *)swift::runtime::backtrace::_swift_backtrace_isThunkFunction;
#endif

// Create empty sections to ensure that the start/stop symbols are synthesized
Copy link
Contributor

Choose a reason for hiding this comment

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

I haven't tried, but if we declare the symbols we add as weak, perhaps that would let us resolve the issue?

// by the linker. Otherwise, we may end up with undefined symbol references as
// the linker table section was never constructed.
#define DECLARE_EMPTY_METADATA_SECTION(name) \
__asm__("\t.section " #name ",\"aR\"\n");

#define BOUNDS_VISIBILITY \
__attribute__((__visibility__("hidden"), __aligned__(1)))

#define DECLARE_BOUNDS(name) \
BOUNDS_VISIBILITY extern const char __start_##name; \
BOUNDS_VISIBILITY extern const char __stop_##name;

#define DECLARE_SWIFT_SECTION(name) \
DECLARE_EMPTY_METADATA_SECTION(name) \
DECLARE_BOUNDS(name)

// These may or may not be present, depending on compiler switches; it's
// worth calling them out as a result.
#define DECLARE_SWIFT_REFLECTION_SECTION(name) DECLARE_SWIFT_SECTION(name)

extern "C" {
DECLARE_SWIFT_SECTION(swift5_protocols)
DECLARE_SWIFT_SECTION(swift5_protocol_conformances)
DECLARE_SWIFT_SECTION(swift5_type_metadata)

DECLARE_SWIFT_REFLECTION_SECTION(swift5_fieldmd)
DECLARE_SWIFT_REFLECTION_SECTION(swift5_builtin)
DECLARE_SWIFT_REFLECTION_SECTION(swift5_assocty)
DECLARE_SWIFT_REFLECTION_SECTION(swift5_capture)
DECLARE_SWIFT_REFLECTION_SECTION(swift5_reflstr)
DECLARE_SWIFT_REFLECTION_SECTION(swift5_typeref)
DECLARE_SWIFT_REFLECTION_SECTION(swift5_mpenum)

DECLARE_SWIFT_SECTION(swift5_replace)
DECLARE_SWIFT_SECTION(swift5_replac2)
DECLARE_SWIFT_SECTION(swift5_accessible_functions)
DECLARE_SWIFT_SECTION(swift5_runtime_attributes)

DECLARE_SWIFT_SECTION(swift5_tests)
Copy link
Contributor

Choose a reason for hiding this comment

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

We can mark this section as "maybe not present" too.

}

#undef DECLARE_SWIFT_SECTION

namespace {
struct ElfNoteHeader {
uint32_t namesz;
uint32_t descsz;
uint32_t type;
};

struct SectionRange {
uintptr_t start;
uintptr_t stop;
};

// If you update this table, remember to update the reader in
// ReflectionContext.h!
struct MetadataSections {
Copy link
Contributor

Choose a reason for hiding this comment

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

Moving this type out of SwiftShims will break Swift Testing.

Copy link
Member Author

Choose a reason for hiding this comment

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

Different metadata sections (open to a new name, did't like MetadataSectionsButAsTheyAreOnDisk). This one is confined to the anonymous namespace and represents what is written to the object file. The ranges are start/stop pointer, shims swift::MetadataSections are start pointer/byte count, which the loader doesn't have relocations for. I intend to migrate swift_addNewDSOImage to use start/stop pointers, which would allow feeding the table into the runtime directly without the translating swift_image_constructor function, but that also comes with more risk so I want to keep it separate.

The swift::MetadataSections are still defined in the MetadataSections.h shims file.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alrighty. We should synchronize our PRs so we can drop swift_addNewDSOImage entirely.

uintptr_t version;
uintptr_t base;
uintptr_t unused0;
uintptr_t unused1;

#define HANDLE_SWIFT_SECTION(name, coff) SectionRange name;
#include "MetadataSectionNames.def"
#undef HANDLE_SWIFT_SECTION
};

struct __attribute__((__packed__)) SwiftMetadataNote {
ElfNoteHeader header;
char name[8]; // "swift6\0" + 1 byte(s) of padding

MetadataSections sections;
};

/// .note.swift_metadata section stores the table of start/stop symbols for
/// ingestion into the runtime and for static tool access.
// TODO: We should replace addNewDSOImage with something that takes a pointer to
// the table directly, removing the need for the additional global constructor
// that converts the start/stop symbol pointer ranges to start/byte-count,
// before stabilizing any ELF platform ABIs. This will help reduce program
// launch times.
// This will require changes to Swift Testing and the runtime metadata
// registration.
__asm__(".section .note.swift_metadata, \"a\", @note");

SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN
__attribute__((section(".note.swift_metadata"), used, aligned(4)))
const static SwiftMetadataNote swiftMetadataNote = {
// header
.header =
{
.namesz = 7, // namesz: "swift6\0"
Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to pick a slightly more specific name in case we decide we want to do more with notes in the future.

Copy link
Member Author

@etcwilde etcwilde Feb 17, 2026

Choose a reason for hiding this comment

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

That is what the segment type is for. The type is namespaced to the name, so we can use any 4-byte number. I went with the bytes that spell out s5md, though little endian turns it into dm5s in the raw binary.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm suggesting we make the type 'swft' and then we can use the name field for different kinds of note rather than defining multiple note types, but it's not a blocker.

.descsz = sizeof(MetadataSections),
.type = SWIFT_METADATA_SEGMENT_TYPE,
}, // 4 + 4 + 4 = 12, 0x0c
.name =
{
's', 'w', 'i', 'f', 't', '6', '\0',
static_cast<char>(0xff), // padding
}, // 12 + 7 + 1 = 20, 0x14
.sections =
{
.version = 5,
.base = reinterpret_cast<uintptr_t>(&__ehdr_start),
.unused0 = 0xa1a1a1a1,
.unused1 = 0xb2b2b2b2,

#define HANDLE_SWIFT_SECTION(elfname, coffname) \
.elfname = { \
reinterpret_cast<uintptr_t>(&__start_##elfname), \
reinterpret_cast<uintptr_t>(&__stop_##elfname), \
},

#include "MetadataSectionNames.def"

#undef HANDLE_SWIFT_SECTION

},
};
SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END

} // anonymous namespace

namespace {
static swift::MetadataSections sections{};
}

SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN
__attribute__((__constructor__)) static void swift_image_constructor() {
#define SWIFT_SECTION_RANGE(name) \
{ \
swiftMetadataNote.sections.name.start, \
swiftMetadataNote.sections.name.stop - \
swiftMetadataNote.sections.name.start, \
}

::new (&sections) swift::MetadataSections{
swiftMetadataNote.sections.version,
reinterpret_cast<const char *>(swiftMetadataNote.sections.base),

reinterpret_cast<void *>(swiftMetadataNote.sections.unused0),
reinterpret_cast<void *>(swiftMetadataNote.sections.unused1),

#define HANDLE_SWIFT_SECTION(elfname, coffname) SWIFT_SECTION_RANGE(elfname),

#include "MetadataSectionNames.def"

#undef HANDLE_SWIFT_SECTION
};

#undef SWIFT_SECTION_RANGE

swift_addNewDSOImage(&sections);
}
SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END
Loading