blob: 6065eef819595fc1248d096c41f18e67448ea116 [file] [log] [blame]
//===--- ImageInspectionELF.cpp - ELF image inspection --------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file includes routines that interact with ld*.so on ELF-based platforms
// to extract runtime metadata embedded in dynamically linked ELF images
// generated by the Swift compiler.
//
//===----------------------------------------------------------------------===//
#if defined(__ELF__) || defined(__ANDROID__)
#include "ImageInspection.h"
#include "swift/Runtime/Debug.h"
#include <dlfcn.h>
#include <elf.h>
#include <link.h>
#include <string.h>
using namespace swift;
/// The symbol name in the image that identifies the beginning of the
/// protocol conformances table.
static const char ProtocolConformancesSymbol[] =
".swift2_protocol_conformances_start";
/// The symbol name in the image that identifies the beginning of the
/// type metadata record table.
static const char TypeMetadataRecordsSymbol[] =
".swift2_type_metadata_start";
/// Context arguments passed down from dl_iterate_phdr to its callback.
struct InspectArgs {
/// Symbol name to look up.
const char *symbolName;
/// Callback function to invoke with the metadata block.
void (*addBlock)(const void *start, uintptr_t size);
/// Set to true when initialize*Lookup() is called.
bool didInitializeLookup;
};
static InspectArgs ProtocolConformanceArgs = {
ProtocolConformancesSymbol,
addImageProtocolConformanceBlockCallback,
false
};
static InspectArgs TypeMetadataRecordArgs = {
TypeMetadataRecordsSymbol,
addImageTypeMetadataRecordBlockCallback,
false
};
// Extract the section information for a named section in an image. imageName
// can be nullptr to specify the main executable.
static SectionInfo getSectionInfo(const char *imageName,
const char *sectionName) {
SectionInfo sectionInfo = { 0, nullptr };
void *handle = dlopen(imageName, RTLD_LAZY | RTLD_NOLOAD);
if (!handle) {
fatalError(/* flags = */ 0, "dlopen() failed on `%s': %s", imageName,
dlerror());
}
void *symbol = dlsym(handle, sectionName);
if (symbol) {
// Extract the size of the section data from the head of the section.
const char *section = reinterpret_cast<const char *>(symbol);
memcpy(&sectionInfo.size, section, sizeof(uint64_t));
sectionInfo.data = section + sizeof(uint64_t);
}
dlclose(handle);
return sectionInfo;
}
static int iteratePHDRCallback(struct dl_phdr_info *info,
size_t size, void *data) {
InspectArgs *inspectArgs = reinterpret_cast<InspectArgs *>(data);
const char *fname = info->dlpi_name;
// While dl_iterate_phdr() is in progress it holds a lock to prevent other
// images being loaded. The initialize flag is set here inside the callback so
// that addNewDSOImage() sees a consistent state. If it was set outside the
// dl_iterate_phdr() call then it could result in images being missed or
// added twice.
inspectArgs->didInitializeLookup = true;
if (fname == nullptr || fname[0] == '\0') {
// The filename may be null for both the dynamic loader and main executable.
// So ignore null image name here and explicitly add the main executable
// in initialize*Lookup() to avoid adding the data twice.
return 0;
}
SectionInfo block = getSectionInfo(fname, inspectArgs->symbolName);
if (block.size > 0) {
inspectArgs->addBlock(block.data, block.size);
}
return 0;
}
// Add the section information in an image specified by an address in that
// image.
static void addBlockInImage(const InspectArgs *inspectArgs, const void *addr) {
const char *fname = nullptr;
if (addr) {
Dl_info info;
if (dladdr(addr, &info) == 0 || info.dli_fname == nullptr) {
return;
}
fname = info.dli_fname;
}
SectionInfo block = getSectionInfo(fname, inspectArgs->symbolName);
if (block.size > 0) {
inspectArgs->addBlock(block.data, block.size);
}
}
static void initializeSectionLookup(InspectArgs *inspectArgs) {
// Add section data in the main executable.
addBlockInImage(inspectArgs, nullptr);
// Search the loaded dls. This only searches the already
// loaded ones. Any images loaded after this are processed by
// addNewDSOImage() below.
dl_iterate_phdr(iteratePHDRCallback, reinterpret_cast<void *>(inspectArgs));
}
void swift::initializeProtocolConformanceLookup() {
initializeSectionLookup(&ProtocolConformanceArgs);
}
void swift::initializeTypeMetadataRecordLookup() {
initializeSectionLookup(&TypeMetadataRecordArgs);
}
// As ELF images are loaded, ImageInspectionInit:sectionDataInit() will call
// addNewDSOImage() with an address in the image that can later be used via
// dladdr() to dlopen() the image after the appropriate initialize*Lookup()
// function has been called.
SWIFT_RUNTIME_EXPORT
void swift_addNewDSOImage(const void *addr) {
if (ProtocolConformanceArgs.didInitializeLookup) {
addBlockInImage(&ProtocolConformanceArgs, addr);
}
if (TypeMetadataRecordArgs.didInitializeLookup) {
addBlockInImage(&TypeMetadataRecordArgs, addr);
}
}
int swift::lookupSymbol(const void *address, SymbolInfo *info) {
Dl_info dlinfo;
if (dladdr(address, &dlinfo) == 0) {
return 0;
}
info->fileName = dlinfo.dli_fname;
info->baseAddress = dlinfo.dli_fbase;
info->symbolName = dlinfo.dli_sname;
info->symbolAddress = dlinfo.dli_saddr;
return 1;
}
#endif // defined(__ELF__) || defined(__ANDROID__)