04ba20aa98
Implements a backend targeting RV32GC and RV64GC, based on Linux standardizing around GC. The assembler is written to make it easy to disable usage of C, but because the sizes of some instruction sequences are compile-time constants, an additional build configuration would need to be defined to make use of it. The assembler and disassembler cover every RV32/64GC instruction. The simulator covers all instructions except accessing CSRs and the floating point state accessible through such, include accrued exceptions and dynamic rounding mode. Quirks: - RISC-V is a compare-and-branch architecture, but some existing "architecture-independent" parts of the Dart compiler assume a condition code architecture. To avoid rewriting these parts, we use a peephole in the assembler to map to compare-and-branch. See Assembler::BranchIf. Luckily nothing depended on taking multiple branches on the same condition code set. - There are no hardware overflow checks, so we must use Hacker's Delight style software checks. Often these are very cheap: if the sign of one operand is known, a single branch is needed. - The ranges of RISC-V branches and jumps are such that we use 3 levels of generation for forward branches, instead of the 2 levels of near and far branches used on ARM[64]. Nearly all code is handled by the first two levels with 20-bits of range, with enormous regex matchers triggering the third level that uses aupic+jalr to get 32-bits of range. - For PC-relative calls in AOT, we always generate auipc+jalr pairs with 32-bits of range, so we never generate trampolines. - Only a subset of registers are available in some compressed instructions, so we assign the most popular uses to these registers. In particular, THR, TMP[2], CODE and PP. This has the effect of assigning CODE and PP to volatile registers in the C calling convention, whereas they are assigned preserved registers on the other architectures. As on ARM64, PP is untagged; this is so short indices can be accessed with a compressed instruction. - There are no push or pop instructions, so combining pushes and pops is preferred so we can update SP once. - The C calling convention has a strongly aligned stack, but unlike on ARM64 we don't need to use an alternate stack pointer. The author ensured language was added to the RISC-V psABI making the OS responsible for realigning the stack pointer for signal handlers, allowing Dart to leave the stack pointer misaligned from the C calling convention's point of view until a foreign call. - We don't bother with the link register tracking done on ARM[64]. Instead we make use of an alternate link register to avoid inline spilling in the write barrier. Unimplemented: - non-trivial FFI cases - Compressed pointers - No intention to implement. - Unboxed SIMD - We might make use of the V extension registers when the V extension is ratified. - BigInt intrinsics TEST=existing tests for IL level, new tests for assembler/disassembler/simulator Bug: https://github.com/dart-lang/sdk/issues/38587 Bug: https://github.com/dart-lang/sdk/issues/48164 Change-Id: I991d1df4be5bf55efec5371b767b332d37dfa3e0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/217289 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com> Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
221 lines
4.8 KiB
C++
221 lines
4.8 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_PLATFORM_ELF_H_
|
|
#define RUNTIME_PLATFORM_ELF_H_
|
|
|
|
#include "platform/globals.h"
|
|
|
|
namespace dart {
|
|
namespace elf {
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
struct ElfHeader {
|
|
uint8_t ident[16];
|
|
uint16_t type;
|
|
uint16_t machine;
|
|
uint32_t version;
|
|
#if defined(TARGET_ARCH_IS_32_BIT)
|
|
uint32_t entry_point;
|
|
uint32_t program_table_offset;
|
|
uint32_t section_table_offset;
|
|
#else
|
|
uint64_t entry_point;
|
|
uint64_t program_table_offset;
|
|
uint64_t section_table_offset;
|
|
#endif
|
|
uint32_t flags;
|
|
uint16_t header_size;
|
|
uint16_t program_table_entry_size;
|
|
uint16_t num_program_headers;
|
|
uint16_t section_table_entry_size;
|
|
uint16_t num_section_headers;
|
|
uint16_t shstrtab_section_index;
|
|
};
|
|
|
|
enum class ProgramHeaderType : uint32_t {
|
|
PT_NULL = 0,
|
|
PT_LOAD = 1,
|
|
PT_DYNAMIC = 2,
|
|
PT_NOTE = 4,
|
|
PT_PHDR = 6,
|
|
};
|
|
|
|
struct ProgramHeader {
|
|
#if defined(TARGET_ARCH_IS_32_BIT)
|
|
ProgramHeaderType type;
|
|
uint32_t file_offset;
|
|
uint32_t memory_offset;
|
|
uint32_t physical_memory_offset;
|
|
uint32_t file_size;
|
|
uint32_t memory_size;
|
|
uint32_t flags;
|
|
uint32_t alignment;
|
|
#else
|
|
ProgramHeaderType type;
|
|
uint32_t flags;
|
|
uint64_t file_offset;
|
|
uint64_t memory_offset;
|
|
uint64_t physical_memory_offset;
|
|
uint64_t file_size;
|
|
uint64_t memory_size;
|
|
uint64_t alignment;
|
|
#endif
|
|
};
|
|
|
|
enum class SectionHeaderType : uint32_t {
|
|
SHT_NULL = 0,
|
|
SHT_PROGBITS = 1,
|
|
SHT_SYMTAB = 2,
|
|
SHT_STRTAB = 3,
|
|
SHT_HASH = 5,
|
|
SHT_NOTE = 7,
|
|
SHT_NOBITS = 8,
|
|
SHT_DYNAMIC = 6,
|
|
SHT_DYNSYM = 11,
|
|
};
|
|
|
|
struct SectionHeader {
|
|
#if defined(TARGET_ARCH_IS_32_BIT)
|
|
uint32_t name;
|
|
SectionHeaderType type;
|
|
uint32_t flags;
|
|
uint32_t memory_offset;
|
|
uint32_t file_offset;
|
|
uint32_t file_size;
|
|
uint32_t link;
|
|
uint32_t info;
|
|
uint32_t alignment;
|
|
uint32_t entry_size;
|
|
#else
|
|
uint32_t name;
|
|
SectionHeaderType type;
|
|
uint64_t flags;
|
|
uint64_t memory_offset;
|
|
uint64_t file_offset;
|
|
uint64_t file_size;
|
|
uint32_t link;
|
|
uint32_t info;
|
|
uint64_t alignment;
|
|
uint64_t entry_size;
|
|
#endif
|
|
};
|
|
|
|
struct Symbol {
|
|
#if defined(TARGET_ARCH_IS_32_BIT)
|
|
uint32_t name;
|
|
uint32_t value;
|
|
uint32_t size;
|
|
uint8_t info;
|
|
uint8_t other; // Reserved by ELF.
|
|
uint16_t section;
|
|
#else
|
|
uint32_t name;
|
|
uint8_t info;
|
|
uint8_t other; // Reserved by ELF.
|
|
uint16_t section;
|
|
uint64_t value;
|
|
uint64_t size;
|
|
#endif
|
|
};
|
|
|
|
enum class DynamicEntryType : uint32_t {
|
|
DT_NULL = 0,
|
|
DT_HASH = 4,
|
|
DT_STRTAB = 5,
|
|
DT_SYMTAB = 6,
|
|
DT_STRSZ = 10,
|
|
DT_SYMENT = 11,
|
|
};
|
|
|
|
struct DynamicEntry {
|
|
#if defined(TARGET_ARCH_IS_32_BIT)
|
|
uint32_t tag;
|
|
uint32_t value;
|
|
#else
|
|
uint64_t tag;
|
|
uint64_t value;
|
|
#endif
|
|
};
|
|
|
|
enum class NoteType : uint32_t {
|
|
NT_GNU_BUILD_ID = 3,
|
|
};
|
|
|
|
struct Note {
|
|
uint32_t name_size;
|
|
uint32_t description_size;
|
|
NoteType type;
|
|
uint8_t data[];
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
static constexpr intptr_t ELFCLASS32 = 1;
|
|
static constexpr intptr_t ELFCLASS64 = 2;
|
|
|
|
static const intptr_t EI_DATA = 5;
|
|
static const intptr_t ELFDATA2LSB = 1;
|
|
|
|
static const intptr_t ELFOSABI_SYSV = 0;
|
|
|
|
static const intptr_t ET_DYN = 3;
|
|
|
|
static constexpr intptr_t EF_ARM_ABI_FLOAT_HARD = 0x00000400;
|
|
static constexpr intptr_t EF_ARM_ABI_FLOAT_SOFT = 0x00000200;
|
|
static constexpr intptr_t EF_ARM_ABI = 0x05000000;
|
|
|
|
static constexpr intptr_t EM_386 = 3;
|
|
static constexpr intptr_t EM_ARM = 40;
|
|
static constexpr intptr_t EM_X86_64 = 62;
|
|
static constexpr intptr_t EM_AARCH64 = 183;
|
|
static constexpr intptr_t EM_RISCV = 243;
|
|
|
|
static const intptr_t EV_CURRENT = 1;
|
|
|
|
static const intptr_t PF_X = 1;
|
|
static const intptr_t PF_W = 2;
|
|
static const intptr_t PF_R = 4;
|
|
|
|
static const intptr_t SHF_WRITE = 0x1;
|
|
static const intptr_t SHF_ALLOC = 0x2;
|
|
static const intptr_t SHF_EXECINSTR = 0x4;
|
|
|
|
static const intptr_t SHN_UNDEF = 0;
|
|
|
|
static const intptr_t STN_UNDEF = 0;
|
|
|
|
static const intptr_t STB_LOCAL = 0;
|
|
static const intptr_t STB_GLOBAL = 1;
|
|
|
|
static const intptr_t STT_NOTYPE = 0;
|
|
static const intptr_t STT_OBJECT = 1; // I.e., data.
|
|
static const intptr_t STT_FUNC = 2;
|
|
static const intptr_t STT_SECTION = 3;
|
|
|
|
static constexpr const char ELF_NOTE_GNU[] = "GNU";
|
|
|
|
// Creates symbol info from the given STB and STT values.
|
|
constexpr decltype(Symbol::info) SymbolInfo(intptr_t binding, intptr_t type) {
|
|
// Take the low nibble of each value in case, though the upper bits should
|
|
// all be zero as long as STB/STT constants are used.
|
|
return (binding & 0xf) << 4 | (type & 0xf);
|
|
}
|
|
|
|
// Retrieves the STB binding value for the given symbol info.
|
|
constexpr intptr_t SymbolBinding(const decltype(Symbol::info) info) {
|
|
return (info >> 4) & 0xf;
|
|
}
|
|
|
|
// Retrieves the STT type value for the given symbol info.
|
|
constexpr intptr_t SymbolType(const decltype(Symbol::info) info) {
|
|
return info & 0xf;
|
|
}
|
|
|
|
} // namespace elf
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_PLATFORM_ELF_H_
|