dfccc4a268
The algorithm for ordering sections in `CreateProgramTable` permitted skew between the order of sections in snapshot and corresponding debug info. This skew can lead to inconsistencies in `.text` section memory offset between two generated ELF files making debug info invalid. This skew was left unnoticed for a long time because executable segment has a huge alignment requirement (64K) while the effect of the skew was usually rather small and thus hidden by realignment. This CL adds a strict verification pass (always enabled) which guards against such skew in the future. A better architecture would be to ditch parallel generation of snapshot and corresponding ELF files altogether and other implement splitting as a post processing path over generated ELF or rely on external tooling. But this is a much bigger change than I am willing to invest time into right now. TEST=ci Cq-Include-Trybots: luci.dart.try:vm-aot-linux-product-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-release-x64-try Change-Id: I0881783237faa6534eb3c076b6cae472bbdc7243 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/384742 Commit-Queue: Slava Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
189 lines
6.1 KiB
C++
189 lines
6.1 KiB
C++
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_ELF_H_
|
|
#define RUNTIME_VM_ELF_H_
|
|
|
|
#include "platform/globals.h"
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
#include "vm/allocation.h"
|
|
#include "vm/compiler/runtime_api.h"
|
|
#include "vm/datastream.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/zone.h"
|
|
#endif
|
|
|
|
namespace dart {
|
|
|
|
// The max page size on all supported architectures. Used to determine
|
|
// the alignment of load segments, so that they are guaranteed page-aligned,
|
|
// and no ELF section or segment should have a larger alignment.
|
|
#if defined(DART_TARGET_OS_LINUX) && defined(TARGET_ARCH_ARM64)
|
|
// Some Linux distributions on ARM64 select 64 KB page size.
|
|
// Follow LLVM (https://reviews.llvm.org/D25079) and set maximum page size
|
|
// to 64 KB for ARM64 Linux builds.
|
|
static constexpr intptr_t kElfPageSize = 64 * KB;
|
|
#elif defined(DART_TARGET_OS_ANDROID) && defined(TARGET_ARCH_IS_64_BIT)
|
|
static constexpr intptr_t kElfPageSize = 64 * KB;
|
|
#else
|
|
static constexpr intptr_t kElfPageSize = 16 * KB;
|
|
#endif
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
|
|
class Dwarf;
|
|
class ProgramTable;
|
|
class Section;
|
|
class SectionTable;
|
|
class SymbolTable;
|
|
|
|
class Elf : public ZoneAllocated {
|
|
public:
|
|
enum class Type {
|
|
// A snapshot that should include segment contents.
|
|
Snapshot,
|
|
// Separately compiled debugging information that should not include
|
|
// most segment contents.
|
|
DebugInfo,
|
|
};
|
|
|
|
Elf(Zone* zone, BaseWriteStream* stream, Type type, Dwarf* dwarf = nullptr);
|
|
|
|
static void AssertConsistency(Elf* snapshot, Elf* debug_info);
|
|
|
|
static constexpr intptr_t kPageSize = kElfPageSize;
|
|
|
|
bool IsStripped() const { return dwarf_ == nullptr; }
|
|
|
|
Zone* zone() const { return zone_; }
|
|
const Dwarf* dwarf() const { return dwarf_; }
|
|
Dwarf* dwarf() { return dwarf_; }
|
|
const SymbolTable& symtab() const {
|
|
ASSERT(symtab_ != nullptr);
|
|
return *symtab_;
|
|
}
|
|
const SectionTable& section_table() const { return *section_table_; }
|
|
|
|
// Stores the information needed to appropriately generate a
|
|
// relocation from the target to the source at the given section offset.
|
|
struct Relocation {
|
|
size_t size_in_bytes;
|
|
intptr_t section_offset;
|
|
intptr_t source_label;
|
|
intptr_t source_offset;
|
|
intptr_t target_label;
|
|
intptr_t target_offset;
|
|
|
|
// Used when the corresponding offset is relative from the location of the
|
|
// relocation itself.
|
|
static constexpr intptr_t kSelfRelative = -1;
|
|
// Used when the corresponding offset is relative to the start of the
|
|
// snapshot.
|
|
static constexpr intptr_t kSnapshotRelative = -2;
|
|
|
|
Relocation(size_t size_in_bytes,
|
|
intptr_t section_offset,
|
|
intptr_t source_label,
|
|
intptr_t source_offset,
|
|
intptr_t target_label,
|
|
intptr_t target_offset)
|
|
: size_in_bytes(size_in_bytes),
|
|
section_offset(section_offset),
|
|
source_label(source_label),
|
|
source_offset(source_offset),
|
|
target_label(target_label),
|
|
target_offset(target_offset) {
|
|
// Other than special values, all labels should be positive.
|
|
ASSERT(source_label > 0 || source_label == kSelfRelative ||
|
|
source_label == kSnapshotRelative);
|
|
ASSERT(target_label > 0 || target_label == kSelfRelative ||
|
|
target_label == kSnapshotRelative);
|
|
}
|
|
};
|
|
|
|
// Stores the information needed to appropriately generate a symbol
|
|
// during finalization.
|
|
struct SymbolData {
|
|
const char* name;
|
|
intptr_t type;
|
|
intptr_t offset;
|
|
size_t size;
|
|
// A positive unique ID only used internally in the Dart VM, not part of
|
|
// the Elf output.
|
|
intptr_t label;
|
|
|
|
SymbolData(const char* name,
|
|
intptr_t type,
|
|
intptr_t offset,
|
|
size_t size,
|
|
intptr_t label)
|
|
: name(name), type(type), offset(offset), size(size), label(label) {
|
|
ASSERT(label > 0);
|
|
}
|
|
};
|
|
|
|
// Must be the same value as the values returned by ImageWriter::SectionLabel
|
|
// for the appropriate section and vm values.
|
|
static constexpr intptr_t kVmBssLabel = 5;
|
|
static constexpr intptr_t kIsolateBssLabel = 6;
|
|
static constexpr intptr_t kBuildIdLabel = 7;
|
|
|
|
void AddText(const char* name,
|
|
intptr_t label,
|
|
const uint8_t* bytes,
|
|
intptr_t size,
|
|
const ZoneGrowableArray<Relocation>* relocations,
|
|
const ZoneGrowableArray<SymbolData>* symbol);
|
|
void AddROData(const char* name,
|
|
intptr_t label,
|
|
const uint8_t* bytes,
|
|
intptr_t size,
|
|
const ZoneGrowableArray<Relocation>* relocations,
|
|
const ZoneGrowableArray<SymbolData>* symbols);
|
|
|
|
void Finalize();
|
|
|
|
private:
|
|
static constexpr const char kBuildIdNoteName[] = ".note.gnu.build-id";
|
|
static constexpr const char kTextName[] = ".text";
|
|
static constexpr const char kDataName[] = ".rodata";
|
|
static constexpr const char kBssName[] = ".bss";
|
|
static constexpr const char kDynamicTableName[] = ".dynamic";
|
|
|
|
void CreateBSS();
|
|
void GenerateBuildId();
|
|
void InitializeSymbolTables();
|
|
void FinalizeDwarfSections();
|
|
void FinalizeEhFrame();
|
|
void ComputeOffsets();
|
|
|
|
Zone* const zone_;
|
|
BaseWriteStream* const unwrapped_stream_;
|
|
const Type type_;
|
|
|
|
// If nullptr, then the ELF file should be stripped of static information like
|
|
// the static symbol table (and its corresponding string table).
|
|
Dwarf* const dwarf_;
|
|
|
|
// Contains all sections that will have entries in the section header table.
|
|
SectionTable* const section_table_;
|
|
|
|
// Contains all segments in the program header table. Set after finalizing
|
|
// the section table.
|
|
ProgramTable* program_table_ = nullptr;
|
|
|
|
// The static tables are always created for use in relocation calculations,
|
|
// even though they may not end up in the final ELF file.
|
|
SymbolTable* symtab_ = nullptr;
|
|
|
|
friend class SectionTable; // For section name static fields.
|
|
};
|
|
|
|
#endif // DART_PRECOMPILER
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_ELF_H_
|