mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:09:48 +00:00
[vm] Direct generation of ELF shared libraries.
Change-Id: I41c9911f33490e504f4852f15695ca4c3f32a77f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/81323 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
834587a78c
commit
af93ebcf4c
|
@ -260,6 +260,7 @@ class Configuration {
|
|||
useAnalyzerCfe: boolOption("use-cfe"),
|
||||
useAnalyzerFastaParser: boolOption("analyzer-use-fasta-parser"),
|
||||
useBlobs: boolOption("use-blobs"),
|
||||
useElf: boolOption("use-elf"),
|
||||
useHotReload: boolOption("hot-reload"),
|
||||
useHotReloadRollback: boolOption("hot-reload-rollback"),
|
||||
useSdk: boolOption("use-sdk"));
|
||||
|
@ -313,6 +314,7 @@ class Configuration {
|
|||
|
||||
// TODO(rnystrom): What is this?
|
||||
final bool useBlobs;
|
||||
final bool useElf;
|
||||
|
||||
final bool useHotReload;
|
||||
final bool useHotReloadRollback;
|
||||
|
@ -334,6 +336,7 @@ class Configuration {
|
|||
bool useAnalyzerCfe,
|
||||
bool useAnalyzerFastaParser,
|
||||
bool useBlobs,
|
||||
bool useElf,
|
||||
bool useHotReload,
|
||||
bool useHotReloadRollback,
|
||||
bool useSdk})
|
||||
|
@ -350,6 +353,7 @@ class Configuration {
|
|||
useAnalyzerCfe = useAnalyzerCfe ?? false,
|
||||
useAnalyzerFastaParser = useAnalyzerFastaParser ?? false,
|
||||
useBlobs = useBlobs ?? false,
|
||||
useElf = useElf ?? false,
|
||||
useHotReload = useHotReload ?? false,
|
||||
useHotReloadRollback = useHotReloadRollback ?? false,
|
||||
useSdk = useSdk ?? false;
|
||||
|
@ -375,6 +379,7 @@ class Configuration {
|
|||
useAnalyzerCfe == other.useAnalyzerCfe &&
|
||||
useAnalyzerFastaParser == other.useAnalyzerFastaParser &&
|
||||
useBlobs == other.useBlobs &&
|
||||
useElf == other.useElf &&
|
||||
useHotReload == other.useHotReload &&
|
||||
useHotReloadRollback == other.useHotReloadRollback &&
|
||||
useSdk == other.useSdk;
|
||||
|
@ -406,6 +411,7 @@ class Configuration {
|
|||
useAnalyzerCfe,
|
||||
useAnalyzerFastaParser,
|
||||
useBlobs,
|
||||
useElf,
|
||||
useHotReload,
|
||||
useHotReloadRollback,
|
||||
useSdk
|
||||
|
|
|
@ -83,6 +83,7 @@ enum SnapshotKind {
|
|||
kAppJIT,
|
||||
kAppAOTBlobs,
|
||||
kAppAOTAssembly,
|
||||
kAppAOTElf,
|
||||
kVMAOTAssembly,
|
||||
};
|
||||
static SnapshotKind snapshot_kind = kCore;
|
||||
|
@ -96,6 +97,7 @@ static const char* kSnapshotKindNames[] = {
|
|||
"app-jit",
|
||||
"app-aot-blobs",
|
||||
"app-aot-assembly",
|
||||
"app-aot-elf",
|
||||
"vm-aot-assembly",
|
||||
NULL,
|
||||
// clang-format on
|
||||
|
@ -146,7 +148,7 @@ DEFINE_CB_OPTION(ProcessEnvironmentOption);
|
|||
|
||||
static bool IsSnapshottingForPrecompilation() {
|
||||
return (snapshot_kind == kAppAOTBlobs) ||
|
||||
(snapshot_kind == kAppAOTAssembly) ||
|
||||
(snapshot_kind == kAppAOTAssembly) || (snapshot_kind == kAppAOTElf) ||
|
||||
(snapshot_kind == kVMAOTAssembly);
|
||||
}
|
||||
|
||||
|
@ -315,7 +317,8 @@ static int ParseArguments(int argc,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case kAppAOTAssembly: {
|
||||
case kAppAOTAssembly:
|
||||
case kAppAOTElf: {
|
||||
if (assembly_filename == NULL) {
|
||||
Syslog::PrintErr(
|
||||
"Building an AOT snapshot as assembly requires specifying "
|
||||
|
@ -621,16 +624,17 @@ static void CreateAndWritePrecompiledSnapshot() {
|
|||
CHECK_RESULT(result);
|
||||
|
||||
// Create a precompiled snapshot.
|
||||
bool as_assembly = assembly_filename != NULL;
|
||||
if (as_assembly) {
|
||||
ASSERT(snapshot_kind == kAppAOTAssembly);
|
||||
if (snapshot_kind == kAppAOTAssembly) {
|
||||
File* file = OpenFile(assembly_filename);
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
result = Dart_CreateAppAOTSnapshotAsAssembly(StreamingWriteCallback, file);
|
||||
CHECK_RESULT(result);
|
||||
} else {
|
||||
ASSERT(snapshot_kind == kAppAOTBlobs);
|
||||
|
||||
} else if (snapshot_kind == kAppAOTElf) {
|
||||
File* file = OpenFile(assembly_filename);
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
result = Dart_CreateAppAOTSnapshotAsElf(StreamingWriteCallback, file);
|
||||
CHECK_RESULT(result);
|
||||
} else if (snapshot_kind == kAppAOTBlobs) {
|
||||
const uint8_t* shared_data = NULL;
|
||||
const uint8_t* shared_instructions = NULL;
|
||||
std::unique_ptr<MappedMemory> mapped_shared_data;
|
||||
|
@ -694,6 +698,8 @@ static void CreateAndWritePrecompiledSnapshot() {
|
|||
isolate_snapshot_instructions_buffer,
|
||||
isolate_snapshot_instructions_size);
|
||||
}
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Serialize obfuscation map if requested.
|
||||
|
@ -785,8 +791,9 @@ static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) {
|
|||
case kAppJIT:
|
||||
CreateAndWriteAppJITSnapshot();
|
||||
break;
|
||||
case kAppAOTBlobs:
|
||||
case kAppAOTAssembly:
|
||||
case kAppAOTBlobs:
|
||||
case kAppAOTElf:
|
||||
CreateAndWritePrecompiledSnapshot();
|
||||
break;
|
||||
case kVMAOTAssembly: {
|
||||
|
|
|
@ -3266,7 +3266,7 @@ typedef void (*Dart_StreamingWriteCallback)(void* callback_data,
|
|||
* Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT.
|
||||
* The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to
|
||||
* Dart_Initialize. The kDartIsolateSnapshotData and
|
||||
* kDartIsoalteSnapshotInstructions should be passed to Dart_CreateIsolate.
|
||||
* kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate.
|
||||
*
|
||||
* The callback will be invoked one or more times to provide the assembly code.
|
||||
*
|
||||
|
@ -3276,6 +3276,31 @@ DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
|
|||
Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
|
||||
void* callback_data);
|
||||
|
||||
/**
|
||||
* Creates a precompiled snapshot.
|
||||
* - A root library must have been loaded.
|
||||
* - Dart_Precompile must have been called.
|
||||
*
|
||||
* Outputs an ELF shared library defining the symbols
|
||||
* - kDartVmSnapshotData
|
||||
* - kDartVmSnapshotInstructions
|
||||
* - kDartIsolateSnapshotData
|
||||
* - kDartIsolateSnapshotInstructions
|
||||
*
|
||||
* The shared library should be dynamically loaded by the embedder.
|
||||
* Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT.
|
||||
* The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to
|
||||
* Dart_Initialize. The kDartIsolateSnapshotData and
|
||||
* kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate.
|
||||
*
|
||||
* The callback will be invoked one or more times to provide the binary output.
|
||||
*
|
||||
* \return A valid handle if no error occurs during the operation.
|
||||
*/
|
||||
DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
|
||||
Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
||||
void* callback_data);
|
||||
|
||||
/**
|
||||
* Like Dart_CreateAppAOTSnapshotAsAssembly, but only includes
|
||||
* kDartVmSnapshotData and kDartVmSnapshotInstructions.
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "vm/dart_api_state.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/debugger.h"
|
||||
#include "vm/dwarf.h"
|
||||
#include "vm/elf.h"
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#include "vm/kernel_loader.h"
|
||||
#endif
|
||||
|
@ -6061,10 +6063,11 @@ Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
|
|||
CHECK_NULL(callback);
|
||||
|
||||
TIMELINE_DURATION(T, Isolate, "WriteVMAOTSnapshot");
|
||||
AssemblyImageWriter image_writer(T, callback, callback_data, NULL, NULL);
|
||||
uint8_t* vm_snapshot_data_buffer = NULL;
|
||||
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data_buffer, NULL,
|
||||
ApiReallocate, &image_writer, NULL);
|
||||
AssemblyImageWriter image_writer(T, callback, callback_data, nullptr,
|
||||
nullptr);
|
||||
uint8_t* vm_snapshot_data_buffer = nullptr;
|
||||
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data_buffer,
|
||||
nullptr, ApiReallocate, &image_writer, nullptr);
|
||||
|
||||
writer.WriteFullSnapshot();
|
||||
|
||||
|
@ -6072,6 +6075,60 @@ Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
|
|||
#endif
|
||||
}
|
||||
|
||||
DART_EXPORT Dart_Handle
|
||||
Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
||||
void* callback_data) {
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
return Api::NewError("AOT compilation is not supported on IA32.");
|
||||
#elif defined(TARGET_ARCH_DBC)
|
||||
return Api::NewError("AOT compilation is not supported on DBC.");
|
||||
#elif defined(TARGET_OS_WINDOWS)
|
||||
return Api::NewError("Windows cannot load ELF.");
|
||||
#elif defined(TARGET_OS_MACOS)
|
||||
return Api::NewError("macOS/iOS cannot load ELF.");
|
||||
#elif !defined(DART_PRECOMPILER)
|
||||
return Api::NewError(
|
||||
"This VM was built without support for AOT compilation.");
|
||||
#else
|
||||
DARTSCOPE(Thread::Current());
|
||||
API_TIMELINE_DURATION(T);
|
||||
|
||||
uint8_t* vm_snapshot_data_buffer = nullptr;
|
||||
uint8_t* vm_snapshot_instructions_buffer = nullptr;
|
||||
uint8_t* isolate_snapshot_data_buffer = nullptr;
|
||||
uint8_t* isolate_snapshot_instructions_buffer = nullptr;
|
||||
|
||||
NOT_IN_PRODUCT(TimelineDurationScope tds2(T, Timeline::GetIsolateStream(),
|
||||
"WriteAppAOTSnapshot"));
|
||||
StreamingWriteStream elf_stream(2 * MB, callback, callback_data);
|
||||
Elf* elf = new (Z) Elf(Z, &elf_stream);
|
||||
Dwarf* dwarf = new (Z) Dwarf(Z, nullptr, elf);
|
||||
|
||||
BlobImageWriter vm_image_writer(T, &vm_snapshot_instructions_buffer,
|
||||
ApiReallocate, /* initial_size= */ 2 * MB,
|
||||
nullptr, nullptr, nullptr, elf, dwarf);
|
||||
BlobImageWriter isolate_image_writer(
|
||||
T, &isolate_snapshot_instructions_buffer, ApiReallocate,
|
||||
/* initial_size= */ 2 * MB, /* shared_data_image= */ nullptr,
|
||||
/* shared_instructions_image= */ nullptr, nullptr, elf, dwarf);
|
||||
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data_buffer,
|
||||
&isolate_snapshot_data_buffer, ApiReallocate,
|
||||
&vm_image_writer, &isolate_image_writer);
|
||||
|
||||
writer.WriteFullSnapshot();
|
||||
elf->AddROData("_kDartVmSnapshotData", vm_snapshot_data_buffer,
|
||||
writer.VmIsolateSnapshotSize());
|
||||
elf->AddROData("_kDartIsolateSnapshotData", isolate_snapshot_data_buffer,
|
||||
writer.IsolateSnapshotSize());
|
||||
// TODO(rmacnak): Generate .debug_frame / .eh_frame / .arm.exidx to
|
||||
// providing unwinding information.
|
||||
dwarf->Write();
|
||||
elf->Finalize();
|
||||
|
||||
return Api::Success();
|
||||
#endif
|
||||
}
|
||||
|
||||
DART_EXPORT Dart_Handle
|
||||
Dart_CreateAppAOTSnapshotAsBlobs(uint8_t** vm_snapshot_data_buffer,
|
||||
intptr_t* vm_snapshot_data_size,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "vm/dwarf.h"
|
||||
|
||||
#include "vm/code_descriptors.h"
|
||||
#include "vm/elf.h"
|
||||
#include "vm/object_store.h"
|
||||
|
||||
namespace dart {
|
||||
|
@ -51,26 +52,37 @@ class InliningNode : public ZoneAllocated {
|
|||
InliningNode* children_next;
|
||||
};
|
||||
|
||||
Dwarf::Dwarf(Zone* zone, StreamingWriteStream* stream)
|
||||
Dwarf::Dwarf(Zone* zone, StreamingWriteStream* stream, Elf* elf)
|
||||
: zone_(zone),
|
||||
stream_(stream),
|
||||
elf_(elf),
|
||||
asm_stream_(stream),
|
||||
bin_stream_(nullptr),
|
||||
codes_(zone, 1024),
|
||||
code_to_index_(zone),
|
||||
functions_(zone, 1024),
|
||||
function_to_index_(zone),
|
||||
scripts_(zone, 1024),
|
||||
script_to_index_(zone),
|
||||
temp_(0) {}
|
||||
abstract_origins_(nullptr),
|
||||
temp_(0) {
|
||||
// Either assembler or direct to ELF.
|
||||
RELEASE_ASSERT((stream == nullptr) != (elf == nullptr));
|
||||
}
|
||||
|
||||
intptr_t Dwarf::AddCode(const Code& code) {
|
||||
intptr_t index = codes_.length();
|
||||
AddCode(code, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
void Dwarf::AddCode(const Code& code, intptr_t offset) {
|
||||
RELEASE_ASSERT(!code.IsNull());
|
||||
CodeIndexPair* pair = code_to_index_.Lookup(&code);
|
||||
if (pair != NULL) {
|
||||
return pair->index_;
|
||||
return; // UNREACHABLE?
|
||||
}
|
||||
intptr_t index = codes_.length();
|
||||
const Code& zone_code = Code::ZoneHandle(zone_, code.raw());
|
||||
code_to_index_.Insert(CodeIndexPair(&zone_code, index));
|
||||
code_to_index_.Insert(CodeIndexPair(&zone_code, offset));
|
||||
codes_.Add(&zone_code);
|
||||
if (code.IsFunctionCode()) {
|
||||
const Function& function = Function::Handle(zone_, code.function());
|
||||
|
@ -85,7 +97,6 @@ intptr_t Dwarf::AddCode(const Code& code) {
|
|||
AddFunction(function);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
intptr_t Dwarf::AddFunction(const Function& function) {
|
||||
|
@ -140,15 +151,25 @@ intptr_t Dwarf::LookupScript(const Script& script) {
|
|||
void Dwarf::Print(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
stream_->VPrint(format, args);
|
||||
asm_stream_->VPrint(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void Dwarf::WriteAbbreviations() {
|
||||
// Dwarf data mostly takes the form of a tree, whose nodes are called
|
||||
// DIEs. Each DIE begins with an abbreviation code, and the abbreviation
|
||||
// describes the attributes of that DIE and their representation.
|
||||
static uint8_t* ZoneReallocate(uint8_t* ptr,
|
||||
intptr_t old_size,
|
||||
intptr_t new_size) {
|
||||
return Thread::Current()->zone()->Realloc<uint8_t>(ptr, old_size, new_size);
|
||||
}
|
||||
|
||||
void Dwarf::WriteAbbreviations() {
|
||||
// Dwarf data mostly takes the form of a tree, whose nodes are called
|
||||
// DIEs. Each DIE begins with an abbreviation code, and the abbreviation
|
||||
// describes the attributes of that DIE and their representation.
|
||||
|
||||
uint8_t* buffer = nullptr;
|
||||
WriteStream stream(&buffer, ZoneReallocate, 64 * KB);
|
||||
|
||||
if (asm_stream_) {
|
||||
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
||||
Print(".section __DWARF,__debug_abbrev,regular,debug\n");
|
||||
#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
|
||||
|
@ -157,6 +178,9 @@ void Dwarf::WriteAbbreviations() {
|
|||
#else
|
||||
UNIMPLEMENTED();
|
||||
#endif
|
||||
} else {
|
||||
bin_stream_ = &stream;
|
||||
}
|
||||
|
||||
uleb128(kCompilationUnit); // Abbrev code.
|
||||
uleb128(DW_TAG_compile_unit); // Type.
|
||||
|
@ -219,11 +243,18 @@ void Dwarf::WriteAbbreviations() {
|
|||
uleb128(0); // End of attributes.
|
||||
|
||||
uleb128(0); // End of abbreviations.
|
||||
|
||||
if (elf_) {
|
||||
elf_->AddDebug(".debug_abbrev", buffer, stream.bytes_written());
|
||||
bin_stream_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Dwarf::WriteCompilationUnit() {
|
||||
// 7.5.1.1 Compilation Unit Header
|
||||
uint8_t* buffer = nullptr;
|
||||
WriteStream stream(&buffer, ZoneReallocate, 64 * KB);
|
||||
|
||||
if (asm_stream_) {
|
||||
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
||||
Print(".section __DWARF,__debug_info,regular,debug\n");
|
||||
#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
|
||||
|
@ -233,11 +264,23 @@ void Dwarf::WriteCompilationUnit() {
|
|||
UNIMPLEMENTED();
|
||||
#endif
|
||||
Print(".Ldebug_info:\n");
|
||||
} else {
|
||||
bin_stream_ = &stream;
|
||||
}
|
||||
|
||||
// 7.5.1.1 Compilation Unit Header
|
||||
|
||||
// Unit length. Assignment to temp works around buggy Mac assembler.
|
||||
intptr_t cu_size_fixup = 0;
|
||||
intptr_t cu_start = 0;
|
||||
if (asm_stream_) {
|
||||
Print("Lcu_size = .Lcu_end - .Lcu_start\n");
|
||||
Print(".4byte Lcu_size\n");
|
||||
Print(".Lcu_start:\n");
|
||||
} else {
|
||||
cu_size_fixup = u4(0);
|
||||
cu_start = position();
|
||||
}
|
||||
|
||||
u2(2); // DWARF version 2
|
||||
u4(0); // debug_abbrev_offset
|
||||
|
@ -250,24 +293,32 @@ void Dwarf::WriteCompilationUnit() {
|
|||
const Library& root_library = Library::Handle(
|
||||
zone_, Isolate::Current()->object_store()->root_library());
|
||||
const String& root_uri = String::Handle(zone_, root_library.url());
|
||||
Print(".string \"%s\"\n", root_uri.ToCString()); // DW_AT_name
|
||||
Print(".string \"Dart VM\"\n"); // DW_AT_producer
|
||||
Print(".string \"\"\n"); // DW_AT_comp_dir
|
||||
string(root_uri.ToCString()); // DW_AT_name
|
||||
string("Dart VM"); // DW_AT_producer
|
||||
string(""); // DW_AT_comp_dir
|
||||
|
||||
// DW_AT_low_pc
|
||||
// The lowest instruction address in this object file that is part of our
|
||||
// compilation unit. Dwarf consumers use this to quickly decide which
|
||||
// compilation unit DIE to consult for a given pc.
|
||||
if (asm_stream_) {
|
||||
Print(FORM_ADDR " _kDartIsolateSnapshotInstructions\n");
|
||||
} else {
|
||||
addr(0);
|
||||
}
|
||||
|
||||
// DW_AT_high_pc
|
||||
// The highest instruction address in this object file that is part of our
|
||||
// compilation unit. Dwarf consumers use this to quickly decide which
|
||||
// compilation unit DIE to consult for a given pc.
|
||||
if (asm_stream_) {
|
||||
intptr_t last_code_index = codes_.length() - 1;
|
||||
const Code& last_code = *(codes_[last_code_index]);
|
||||
Print(FORM_ADDR " .Lcode%" Pd " + %" Pd "\n", last_code_index,
|
||||
last_code.Size());
|
||||
} else {
|
||||
addr(elf_->NextMemoryOffset());
|
||||
}
|
||||
|
||||
// DW_AT_stmt_list (offset into .debug_line)
|
||||
// Indicates which line number program is associated with this compilation
|
||||
|
@ -280,12 +331,23 @@ void Dwarf::WriteCompilationUnit() {
|
|||
uleb128(0); // End of children.
|
||||
|
||||
uleb128(0); // End of entries.
|
||||
|
||||
if (asm_stream_) {
|
||||
Print(".Lcu_end:\n");
|
||||
} else {
|
||||
fixup_u4(cu_size_fixup, position() - cu_start);
|
||||
|
||||
elf_->AddDebug(".debug_info", buffer, stream.bytes_written());
|
||||
bin_stream_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Dwarf::WriteAbstractFunctions() {
|
||||
Script& script = Script::Handle(zone_);
|
||||
String& name = String::Handle(zone_);
|
||||
if (!asm_stream_) {
|
||||
abstract_origins_ = zone_->Alloc<uint32_t>(functions_.length());
|
||||
}
|
||||
for (intptr_t i = 0; i < functions_.length(); i++) {
|
||||
const Function& function = *(functions_[i]);
|
||||
name = function.QualifiedUserVisibleName();
|
||||
|
@ -293,9 +355,14 @@ void Dwarf::WriteAbstractFunctions() {
|
|||
intptr_t file = LookupScript(script);
|
||||
intptr_t line = 0; // Not known. Script has already lost its token stream.
|
||||
|
||||
Print(".Lfunc%" Pd ":\n", i); // Label for DW_AT_abstract_origin references
|
||||
if (asm_stream_) {
|
||||
Print(".Lfunc%" Pd ":\n",
|
||||
i); // Label for DW_AT_abstract_origin references
|
||||
} else {
|
||||
abstract_origins_[i] = position();
|
||||
}
|
||||
uleb128(kAbstractFunction);
|
||||
Print(".string \"%s\"\n", name.ToCString()); // DW_AT_name
|
||||
string(name.ToCString()); // DW_AT_name
|
||||
uleb128(file); // DW_AT_decl_file
|
||||
uleb128(line); // DW_AT_decl_line
|
||||
uleb128(DW_INL_inlined); // DW_AT_inline
|
||||
|
@ -312,6 +379,11 @@ void Dwarf::WriteConcreteFunctions() {
|
|||
if (!code.IsFunctionCode()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CodeIndexPair* pair = code_to_index_.Lookup(&code);
|
||||
RELEASE_ASSERT(pair != NULL);
|
||||
intptr_t code_offset = pair->index_;
|
||||
|
||||
function = code.function();
|
||||
intptr_t function_index = LookupFunction(function);
|
||||
script = function.script();
|
||||
|
@ -320,15 +392,27 @@ void Dwarf::WriteConcreteFunctions() {
|
|||
// DW_AT_abstract_origin
|
||||
// References a node written above in WriteAbstractFunctions.
|
||||
// Assignment to temp works around buggy Mac assembler.
|
||||
if (asm_stream_) {
|
||||
intptr_t temp = temp_++;
|
||||
Print("Ltemp%" Pd " = .Lfunc%" Pd " - .Ldebug_info\n", temp,
|
||||
function_index);
|
||||
Print(".4byte Ltemp%" Pd "\n", temp);
|
||||
} else {
|
||||
u4(abstract_origins_[function_index]);
|
||||
}
|
||||
|
||||
// DW_AT_low_pc
|
||||
if (asm_stream_) {
|
||||
Print(FORM_ADDR " .Lcode%" Pd "\n", i);
|
||||
} else {
|
||||
addr(code_offset);
|
||||
}
|
||||
// DW_AT_high_pc
|
||||
if (asm_stream_) {
|
||||
Print(FORM_ADDR " .Lcode%" Pd " + %" Pd "\n", i, code.Size());
|
||||
} else {
|
||||
addr(code_offset + code.Size());
|
||||
}
|
||||
|
||||
InliningNode* node = ExpandInliningTree(code);
|
||||
if (node != NULL) {
|
||||
|
@ -435,15 +519,28 @@ void Dwarf::WriteInliningNode(InliningNode* node,
|
|||
// DW_AT_abstract_origin
|
||||
// References a node written above in WriteAbstractFunctions.
|
||||
// Assignment to temp works around buggy Mac assembler.
|
||||
if (asm_stream_) {
|
||||
intptr_t temp = temp_++;
|
||||
Print("Ltemp%" Pd " = .Lfunc%" Pd " - .Ldebug_info\n", temp, function_index);
|
||||
Print("Ltemp%" Pd " = .Lfunc%" Pd " - .Ldebug_info\n", temp,
|
||||
function_index);
|
||||
Print(".4byte Ltemp%" Pd "\n", temp);
|
||||
} else {
|
||||
u4(abstract_origins_[function_index]);
|
||||
}
|
||||
// DW_AT_low_pc
|
||||
if (asm_stream_) {
|
||||
Print(FORM_ADDR " .Lcode%" Pd " + %d\n", root_code_index,
|
||||
node->start_pc_offset);
|
||||
} else {
|
||||
addr(root_code_index + node->start_pc_offset);
|
||||
}
|
||||
// DW_AT_high_pc
|
||||
if (asm_stream_) {
|
||||
Print(FORM_ADDR " .Lcode%" Pd " + %d\n", root_code_index,
|
||||
node->end_pc_offset);
|
||||
} else {
|
||||
addr(root_code_index + node->end_pc_offset);
|
||||
}
|
||||
// DW_AT_call_file
|
||||
uleb128(file);
|
||||
// DW_AT_call_line
|
||||
|
@ -458,6 +555,10 @@ void Dwarf::WriteInliningNode(InliningNode* node,
|
|||
}
|
||||
|
||||
void Dwarf::WriteLines() {
|
||||
uint8_t* buffer = nullptr;
|
||||
WriteStream stream(&buffer, ZoneReallocate, 64 * KB);
|
||||
|
||||
if (asm_stream_) {
|
||||
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
||||
Print(".section __DWARF,__debug_line,regular,debug\n");
|
||||
#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
|
||||
|
@ -466,22 +567,38 @@ void Dwarf::WriteLines() {
|
|||
#else
|
||||
UNIMPLEMENTED();
|
||||
#endif
|
||||
} else {
|
||||
bin_stream_ = &stream;
|
||||
}
|
||||
|
||||
// 6.2.4 The Line Number Program Header
|
||||
|
||||
// 1. unit_length. This encoding implies 32-bit DWARF.
|
||||
intptr_t line_size_fixup = 0;
|
||||
intptr_t line_start = 0;
|
||||
if (asm_stream_) {
|
||||
Print("Lline_size = .Lline_end - .Lline_start\n");
|
||||
Print(".4byte Lline_size\n");
|
||||
|
||||
Print(".Lline_start:\n");
|
||||
} else {
|
||||
line_size_fixup = u4(0);
|
||||
line_start = position();
|
||||
}
|
||||
|
||||
u2(2); // 2. DWARF version 2
|
||||
|
||||
// 3. header_length
|
||||
// Assignment to temp works around buggy Mac assembler.
|
||||
intptr_t lineheader_size_fixup = 0;
|
||||
intptr_t lineheader_start = 0;
|
||||
if (asm_stream_) {
|
||||
Print("Llineheader_size = .Llineheader_end - .Llineheader_start\n");
|
||||
Print(".4byte Llineheader_size\n");
|
||||
Print(".Llineheader_start:\n");
|
||||
} else {
|
||||
lineheader_size_fixup = u4(0);
|
||||
lineheader_start = position();
|
||||
}
|
||||
|
||||
u1(1); // 4. minimum_instruction_length
|
||||
u1(1); // 5. default_is_stmt (true for compatibility with dsymutil).
|
||||
|
@ -512,20 +629,24 @@ void Dwarf::WriteLines() {
|
|||
for (intptr_t i = 0; i < scripts_.length(); i++) {
|
||||
const Script& script = *(scripts_[i]);
|
||||
uri = script.url();
|
||||
Print(".string \"%s\"\n", uri.ToCString());
|
||||
string(uri.ToCString()); // NOLINT
|
||||
uleb128(0); // Include directory index.
|
||||
uleb128(0); // File modification time.
|
||||
uleb128(0); // File length.
|
||||
}
|
||||
u1(0); // End of file names.
|
||||
|
||||
if (asm_stream_) {
|
||||
Print(".Llineheader_end:\n");
|
||||
} else {
|
||||
fixup_u4(lineheader_size_fixup, position() - lineheader_start);
|
||||
}
|
||||
|
||||
// 6.2.5 The Line Number Program
|
||||
|
||||
intptr_t previous_file = 1;
|
||||
intptr_t previous_line = 1;
|
||||
intptr_t previous_code_index = -1;
|
||||
intptr_t previous_code_offset = -1;
|
||||
intptr_t previous_pc_offset = 0;
|
||||
|
||||
Function& root_function = Function::Handle(zone_);
|
||||
|
@ -537,6 +658,11 @@ void Dwarf::WriteLines() {
|
|||
|
||||
for (intptr_t i = 0; i < codes_.length(); i++) {
|
||||
const Code& code = *(codes_[i]);
|
||||
|
||||
CodeIndexPair* pair = code_to_index_.Lookup(&code);
|
||||
RELEASE_ASSERT(pair != NULL);
|
||||
intptr_t current_code_offset = pair->index_;
|
||||
|
||||
map = code.code_source_map();
|
||||
if (map.IsNull()) {
|
||||
continue;
|
||||
|
@ -591,18 +717,30 @@ void Dwarf::WriteLines() {
|
|||
u1(DW_LNS_copy);
|
||||
|
||||
// 4. Update LNP pc.
|
||||
if (previous_code_index == -1) {
|
||||
if (previous_code_offset == -1) {
|
||||
// This variant is relocatable.
|
||||
u1(0); // This is an extended opcode
|
||||
u1(1 + sizeof(void*)); // that is 5 or 9 bytes long
|
||||
u1(DW_LNE_set_address);
|
||||
if (asm_stream_) {
|
||||
Print(FORM_ADDR " .Lcode%" Pd " + %d\n", i, current_pc_offset);
|
||||
} else {
|
||||
u1(DW_LNS_advance_pc);
|
||||
Print(".uleb128 .Lcode%" Pd " - .Lcode%" Pd " + %" Pd "\n", i,
|
||||
previous_code_index, current_pc_offset - previous_pc_offset);
|
||||
addr(current_code_offset + current_pc_offset);
|
||||
}
|
||||
previous_code_index = i;
|
||||
} else {
|
||||
u1(DW_LNS_advance_pc);
|
||||
if (asm_stream_) {
|
||||
Print(".uleb128 .Lcode%" Pd " - .Lcode%" Pd " + %" Pd "\n", i,
|
||||
previous_code_offset,
|
||||
current_pc_offset - previous_pc_offset);
|
||||
} else {
|
||||
intptr_t delta = current_code_offset - previous_code_offset +
|
||||
current_pc_offset - previous_pc_offset;
|
||||
RELEASE_ASSERT(delta > 0);
|
||||
uleb128(delta);
|
||||
}
|
||||
}
|
||||
previous_code_offset = current_code_offset;
|
||||
previous_pc_offset = current_pc_offset;
|
||||
break;
|
||||
}
|
||||
|
@ -635,16 +773,35 @@ void Dwarf::WriteLines() {
|
|||
// Advance pc to end of the compilation unit.
|
||||
intptr_t last_code_index = codes_.length() - 1;
|
||||
const Code& last_code = *(codes_[last_code_index]);
|
||||
|
||||
CodeIndexPair* pair = code_to_index_.Lookup(&last_code);
|
||||
RELEASE_ASSERT(pair != NULL);
|
||||
intptr_t last_code_offset = pair->index_;
|
||||
|
||||
u1(DW_LNS_advance_pc);
|
||||
if (asm_stream_) {
|
||||
Print(".uleb128 .Lcode%" Pd " - .Lcode%" Pd " + %" Pd "\n", last_code_index,
|
||||
previous_code_index, last_code.Size() - previous_pc_offset);
|
||||
previous_code_offset, last_code.Size() - previous_pc_offset);
|
||||
} else {
|
||||
intptr_t delta = last_code_offset - previous_code_offset +
|
||||
last_code.Size() - previous_pc_offset;
|
||||
RELEASE_ASSERT(delta >= 0);
|
||||
uleb128(delta);
|
||||
}
|
||||
|
||||
// End of contiguous machine code.
|
||||
u1(0); // This is an extended opcode
|
||||
u1(1); // that is 1 byte long
|
||||
u1(DW_LNE_end_sequence);
|
||||
|
||||
if (asm_stream_) {
|
||||
Print(".Lline_end:\n");
|
||||
} else {
|
||||
fixup_u4(line_size_fixup, position() - line_start);
|
||||
|
||||
elf_->AddDebug(".debug_line", buffer, stream.bytes_written());
|
||||
bin_stream_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DART_PRECOMPILER
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace dart {
|
|||
|
||||
#ifdef DART_PRECOMPILER
|
||||
|
||||
class Elf;
|
||||
class InliningNode;
|
||||
|
||||
struct ScriptIndexPair {
|
||||
|
@ -117,8 +118,9 @@ typedef DirectChainedHashMap<CodeIndexPair> CodeIndexMap;
|
|||
|
||||
class Dwarf : public ZoneAllocated {
|
||||
public:
|
||||
Dwarf(Zone* zone, StreamingWriteStream* stream);
|
||||
Dwarf(Zone* zone, StreamingWriteStream* stream, Elf* elf);
|
||||
|
||||
void AddCode(const Code& code, intptr_t offset);
|
||||
intptr_t AddCode(const Code& code);
|
||||
intptr_t AddFunction(const Function& function);
|
||||
intptr_t AddScript(const Script& script);
|
||||
|
@ -181,11 +183,100 @@ class Dwarf : public ZoneAllocated {
|
|||
};
|
||||
|
||||
void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
|
||||
void sleb128(intptr_t value) { Print(".sleb128 %" Pd "\n", value); }
|
||||
void uleb128(uintptr_t value) { Print(".uleb128 %" Pd "\n", value); }
|
||||
void u1(uint8_t value) { Print(".byte %d\n", value); }
|
||||
void u2(uint16_t value) { Print(".2byte %d\n", value); }
|
||||
void u4(uint32_t value) { Print(".4byte %d\n", value); }
|
||||
void sleb128(intptr_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".sleb128 %" Pd "\n", value);
|
||||
} else {
|
||||
bool is_last_part = false;
|
||||
while (!is_last_part) {
|
||||
uint8_t part = value & 0x7F;
|
||||
value >>= 7;
|
||||
if ((value == 0 && (part & 0x40) == 0) ||
|
||||
(value == static_cast<intptr_t>(-1) && (part & 0x40) != 0)) {
|
||||
is_last_part = true;
|
||||
} else {
|
||||
part |= 0x80;
|
||||
}
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&part),
|
||||
sizeof(part));
|
||||
}
|
||||
}
|
||||
}
|
||||
void uleb128(uintptr_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".uleb128 %" Pd "\n", value);
|
||||
} else {
|
||||
bool is_last_part = false;
|
||||
while (!is_last_part) {
|
||||
uint8_t part = value & 0x7F;
|
||||
value >>= 7;
|
||||
if (value == 0) {
|
||||
is_last_part = true;
|
||||
} else {
|
||||
part |= 0x80;
|
||||
}
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&part),
|
||||
sizeof(part));
|
||||
}
|
||||
}
|
||||
}
|
||||
void u1(uint8_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".byte %d\n", value);
|
||||
} else {
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
|
||||
sizeof(value));
|
||||
}
|
||||
}
|
||||
void u2(uint16_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".2byte %d\n", value);
|
||||
} else {
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
|
||||
sizeof(value));
|
||||
}
|
||||
}
|
||||
intptr_t u4(uint32_t value) {
|
||||
if (asm_stream_) {
|
||||
Print(".4byte %d\n", value);
|
||||
return -1;
|
||||
} else {
|
||||
intptr_t fixup = position();
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
|
||||
sizeof(value));
|
||||
return fixup;
|
||||
}
|
||||
}
|
||||
void fixup_u4(intptr_t position, uint32_t value) {
|
||||
if (asm_stream_) {
|
||||
UNREACHABLE();
|
||||
} else {
|
||||
memmove(bin_stream_->buffer() + position, &value, sizeof(value));
|
||||
}
|
||||
}
|
||||
void addr(uword value) {
|
||||
if (asm_stream_) {
|
||||
UNREACHABLE();
|
||||
} else {
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(&value),
|
||||
sizeof(value));
|
||||
}
|
||||
}
|
||||
void string(const char* cstr) { // NOLINT
|
||||
if (asm_stream_) {
|
||||
Print(".string \"%s\"\n", cstr); // NOLINT
|
||||
} else {
|
||||
bin_stream_->WriteBytes(reinterpret_cast<const uint8_t*>(cstr),
|
||||
strlen(cstr) + 1);
|
||||
}
|
||||
}
|
||||
intptr_t position() {
|
||||
if (asm_stream_) {
|
||||
UNREACHABLE();
|
||||
} else {
|
||||
return bin_stream_->Position();
|
||||
}
|
||||
}
|
||||
|
||||
void WriteAbbreviations();
|
||||
void WriteCompilationUnit();
|
||||
|
@ -198,13 +289,16 @@ class Dwarf : public ZoneAllocated {
|
|||
void WriteLines();
|
||||
|
||||
Zone* const zone_;
|
||||
StreamingWriteStream* stream_;
|
||||
Elf* const elf_;
|
||||
StreamingWriteStream* asm_stream_;
|
||||
WriteStream* bin_stream_;
|
||||
ZoneGrowableArray<const Code*> codes_;
|
||||
CodeIndexMap code_to_index_;
|
||||
ZoneGrowableArray<const Function*> functions_;
|
||||
FunctionIndexMap function_to_index_;
|
||||
ZoneGrowableArray<const Script*> scripts_;
|
||||
ScriptIndexMap script_to_index_;
|
||||
uint32_t* abstract_origins_;
|
||||
intptr_t temp_;
|
||||
};
|
||||
|
||||
|
|
754
runtime/vm/elf.cc
Normal file
754
runtime/vm/elf.cc
Normal file
|
@ -0,0 +1,754 @@
|
|||
// 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.
|
||||
|
||||
#include "vm/elf.h"
|
||||
|
||||
#include "platform/text_buffer.h"
|
||||
#include "vm/cpu.h"
|
||||
#include "vm/thread.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
#define ELFCLASS32 1
|
||||
#define ELFCLASS64 2
|
||||
|
||||
static const intptr_t ELFDATA2LSB = 1;
|
||||
|
||||
static const intptr_t ELFOSABI_SYSV = 0;
|
||||
|
||||
#define EF_ARM_ABI_FLOAT_HARD 0x00000400
|
||||
#define EF_ARM_ABI_FLOAT_SOFT 0x00000200
|
||||
#define EF_ARM_ABI 0x05000000
|
||||
|
||||
static const intptr_t ET_DYN = 3;
|
||||
|
||||
#define EM_386 3
|
||||
#define EM_ARM 40
|
||||
#define EM_X86_64 62
|
||||
#define EM_AARCH64 183
|
||||
|
||||
static const intptr_t EV_CURRENT = 1;
|
||||
|
||||
static const intptr_t SHT_PROGBITS = 1;
|
||||
static const intptr_t SHT_STRTAB = 3;
|
||||
static const intptr_t SHT_HASH = 5;
|
||||
static const intptr_t SHT_DYNSYM = 11;
|
||||
static const intptr_t SHT_DYNAMIC = 6;
|
||||
|
||||
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 PT_LOAD = 1;
|
||||
static const intptr_t PT_DYNAMIC = 2;
|
||||
static const intptr_t PT_PHDR = 6;
|
||||
|
||||
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 STB_GLOBAL = 1;
|
||||
|
||||
static const intptr_t STT_OBJECT = 1; // I.e., data.
|
||||
static const intptr_t STT_FUNC = 2;
|
||||
|
||||
static const intptr_t DT_HASH = 4;
|
||||
static const intptr_t DT_STRTAB = 5;
|
||||
static const intptr_t DT_SYMTAB = 6;
|
||||
static const intptr_t DT_STRSZ = 10;
|
||||
static const intptr_t DT_SYMENT = 11;
|
||||
|
||||
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM)
|
||||
#define TARGET_ARCH_IS_32_BIT 1
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
static const intptr_t kElfHeaderSize = 52;
|
||||
static const intptr_t kElfSectionTableEntrySize = 40;
|
||||
static const intptr_t kElfProgramTableEntrySize = 32;
|
||||
static const intptr_t kElfSymbolTableEntrySize = 16;
|
||||
static const intptr_t kElfDynamicTableEntrySize = 8;
|
||||
#else
|
||||
static const intptr_t kElfHeaderSize = 64;
|
||||
static const intptr_t kElfSectionTableEntrySize = 64;
|
||||
static const intptr_t kElfProgramTableEntrySize = 56;
|
||||
static const intptr_t kElfSymbolTableEntrySize = 24;
|
||||
static const intptr_t kElfDynamicTableEntrySize = 16;
|
||||
#endif
|
||||
|
||||
static const intptr_t kPageSize = 4096;
|
||||
|
||||
class Section : public ZoneAllocated {
|
||||
public:
|
||||
Section() {}
|
||||
|
||||
virtual ~Section() {}
|
||||
virtual void Write(Elf* stream) = 0;
|
||||
|
||||
// Linker view.
|
||||
intptr_t section_name = 0; // Index into string table.
|
||||
intptr_t section_type = 0;
|
||||
intptr_t section_flags = 0;
|
||||
intptr_t section_index = -1;
|
||||
intptr_t section_link = SHN_UNDEF;
|
||||
intptr_t section_info = 0;
|
||||
intptr_t section_entry_size = 0;
|
||||
intptr_t file_size = 0;
|
||||
intptr_t file_offset = -1;
|
||||
|
||||
intptr_t alignment = 1;
|
||||
|
||||
// Loader view.
|
||||
intptr_t segment_type = -1;
|
||||
intptr_t segment_flags = 0;
|
||||
intptr_t memory_size = 0;
|
||||
intptr_t memory_offset = -1;
|
||||
};
|
||||
|
||||
class ProgramBits : public Section {
|
||||
public:
|
||||
ProgramBits(bool allocate,
|
||||
bool executable,
|
||||
const uint8_t* bytes,
|
||||
intptr_t size) {
|
||||
section_type = SHT_PROGBITS;
|
||||
if (allocate) {
|
||||
section_flags = SHF_ALLOC;
|
||||
if (executable) section_flags |= SHF_EXECINSTR;
|
||||
|
||||
segment_type = PT_LOAD;
|
||||
segment_flags = PF_R;
|
||||
if (executable) segment_flags |= PF_X;
|
||||
}
|
||||
|
||||
bytes_ = bytes;
|
||||
file_size = memory_size = size;
|
||||
}
|
||||
|
||||
void Write(Elf* stream) { stream->WriteBytes(bytes_, memory_size); }
|
||||
|
||||
const uint8_t* bytes_;
|
||||
};
|
||||
|
||||
class StringTable : public Section {
|
||||
public:
|
||||
StringTable() : text_(128) {
|
||||
section_type = SHT_STRTAB;
|
||||
section_flags = SHF_ALLOC;
|
||||
segment_type = PT_LOAD;
|
||||
segment_flags = PF_R;
|
||||
|
||||
text_.AddChar('\0');
|
||||
memory_size = file_size = text_.length();
|
||||
}
|
||||
|
||||
intptr_t AddString(const char* str) {
|
||||
intptr_t offset = text_.length();
|
||||
text_.AddString(str);
|
||||
text_.AddChar('\0');
|
||||
memory_size = file_size = text_.length();
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Write(Elf* stream) {
|
||||
stream->WriteBytes(reinterpret_cast<const uint8_t*>(text_.buf()),
|
||||
text_.length());
|
||||
}
|
||||
|
||||
TextBuffer text_;
|
||||
};
|
||||
|
||||
class Symbol : public ZoneAllocated {
|
||||
public:
|
||||
const char* cstr;
|
||||
intptr_t name;
|
||||
intptr_t info;
|
||||
intptr_t section;
|
||||
intptr_t offset;
|
||||
intptr_t size;
|
||||
};
|
||||
|
||||
class SymbolTable : public Section {
|
||||
public:
|
||||
SymbolTable() {
|
||||
section_type = SHT_DYNSYM;
|
||||
section_flags = SHF_ALLOC;
|
||||
segment_type = PT_LOAD;
|
||||
segment_flags = PF_R;
|
||||
|
||||
section_entry_size = kElfSymbolTableEntrySize;
|
||||
AddSymbol(NULL);
|
||||
section_info = 1; // One "local" symbol, the reserved first entry.
|
||||
}
|
||||
|
||||
void AddSymbol(Symbol* symbol) {
|
||||
symbols_.Add(symbol);
|
||||
memory_size += kElfSymbolTableEntrySize;
|
||||
file_size += kElfSymbolTableEntrySize;
|
||||
}
|
||||
|
||||
void Write(Elf* stream) {
|
||||
// The first symbol table entry is reserved and must be all zeros.
|
||||
{
|
||||
const intptr_t start = stream->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
stream->WriteWord(0);
|
||||
stream->WriteAddr(0);
|
||||
stream->WriteWord(0);
|
||||
stream->WriteByte(0);
|
||||
stream->WriteByte(0);
|
||||
stream->WriteHalf(0);
|
||||
#else
|
||||
stream->WriteWord(0);
|
||||
stream->WriteByte(0);
|
||||
stream->WriteByte(0);
|
||||
stream->WriteHalf(0);
|
||||
stream->WriteAddr(0);
|
||||
stream->WriteXWord(0);
|
||||
#endif
|
||||
const intptr_t end = stream->position();
|
||||
ASSERT((end - start) == kElfSymbolTableEntrySize);
|
||||
}
|
||||
|
||||
for (intptr_t i = 1; i < symbols_.length(); i++) {
|
||||
Symbol* symbol = symbols_[i];
|
||||
const intptr_t start = stream->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
stream->WriteWord(symbol->name);
|
||||
stream->WriteAddr(symbol->offset);
|
||||
stream->WriteWord(symbol->size);
|
||||
stream->WriteByte(symbol->info);
|
||||
stream->WriteByte(0);
|
||||
stream->WriteHalf(symbol->section);
|
||||
#else
|
||||
stream->WriteWord(symbol->name);
|
||||
stream->WriteByte(symbol->info);
|
||||
stream->WriteByte(0);
|
||||
stream->WriteHalf(symbol->section);
|
||||
stream->WriteAddr(symbol->offset);
|
||||
stream->WriteXWord(symbol->size);
|
||||
#endif
|
||||
const intptr_t end = stream->position();
|
||||
ASSERT((end - start) == kElfSymbolTableEntrySize);
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t length() const { return symbols_.length(); }
|
||||
Symbol* at(intptr_t i) const { return symbols_[i]; }
|
||||
|
||||
GrowableArray<Symbol*> symbols_;
|
||||
};
|
||||
|
||||
static uint32_t ElfHash(const unsigned char* name) {
|
||||
uint32_t h = 0;
|
||||
while (*name) {
|
||||
h = (h << 4) + *name++;
|
||||
uint32_t g = h & 0xf0000000;
|
||||
h ^= g;
|
||||
h ^= g >> 24;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
class SymbolHashTable : public Section {
|
||||
public:
|
||||
SymbolHashTable(StringTable* strtab, SymbolTable* symtab) {
|
||||
section_type = SHT_HASH;
|
||||
section_flags = SHF_ALLOC;
|
||||
section_link = symtab->section_index;
|
||||
segment_type = PT_LOAD;
|
||||
segment_flags = PF_R;
|
||||
|
||||
nchain_ = symtab->length();
|
||||
nbucket_ = symtab->length();
|
||||
|
||||
bucket_ = Thread::Current()->zone()->Alloc<int32_t>(nbucket_);
|
||||
for (intptr_t i = 0; i < nbucket_; i++) {
|
||||
bucket_[i] = STN_UNDEF;
|
||||
}
|
||||
|
||||
chain_ = Thread::Current()->zone()->Alloc<int32_t>(nchain_);
|
||||
for (intptr_t i = 0; i < nchain_; i++) {
|
||||
chain_[i] = STN_UNDEF;
|
||||
}
|
||||
|
||||
for (intptr_t i = 1; i < symtab->length(); i++) {
|
||||
Symbol* symbol = symtab->at(i);
|
||||
uint32_t hash = ElfHash((const unsigned char*)symbol->cstr);
|
||||
uint32_t probe = hash % nbucket_;
|
||||
chain_[i] = bucket_[probe]; // next = head
|
||||
bucket_[probe] = i; // head = symbol
|
||||
}
|
||||
|
||||
memory_size = file_size = 4 * (nbucket_ + nchain_ + 2);
|
||||
}
|
||||
|
||||
void Write(Elf* stream) {
|
||||
stream->WriteWord(nbucket_);
|
||||
stream->WriteWord(nchain_);
|
||||
for (intptr_t i = 0; i < nbucket_; i++) {
|
||||
stream->WriteWord(bucket_[i]);
|
||||
}
|
||||
for (intptr_t i = 0; i < nchain_; i++) {
|
||||
stream->WriteWord(chain_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t nbucket_;
|
||||
int32_t nchain_;
|
||||
int32_t* bucket_; // "Head"
|
||||
int32_t* chain_; // "Next"
|
||||
};
|
||||
|
||||
class DynamicTable : public Section {
|
||||
public:
|
||||
DynamicTable(StringTable* strtab,
|
||||
SymbolTable* symtab,
|
||||
SymbolHashTable* hash) {
|
||||
section_type = SHT_DYNAMIC;
|
||||
section_link = strtab->section_index;
|
||||
section_flags = SHF_ALLOC | SHF_WRITE;
|
||||
section_entry_size = kElfDynamicTableEntrySize;
|
||||
|
||||
segment_type = PT_LOAD;
|
||||
segment_flags = PF_R | PF_W;
|
||||
|
||||
AddEntry(DT_HASH, hash->memory_offset);
|
||||
AddEntry(DT_STRTAB, strtab->memory_offset);
|
||||
AddEntry(DT_STRSZ, strtab->memory_size);
|
||||
AddEntry(DT_SYMTAB, symtab->memory_offset);
|
||||
AddEntry(DT_SYMENT, kElfSymbolTableEntrySize);
|
||||
}
|
||||
|
||||
void Write(Elf* stream) {
|
||||
for (intptr_t i = 0; i < entries_.length(); i++) {
|
||||
const intptr_t start = stream->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
stream->WriteWord(entries_[i]->tag);
|
||||
stream->WriteAddr(entries_[i]->value);
|
||||
#else
|
||||
stream->WriteXWord(entries_[i]->tag);
|
||||
stream->WriteAddr(entries_[i]->value);
|
||||
#endif
|
||||
const intptr_t end = stream->position();
|
||||
ASSERT((end - start) == kElfDynamicTableEntrySize);
|
||||
}
|
||||
}
|
||||
|
||||
class Entry {
|
||||
public:
|
||||
intptr_t tag;
|
||||
intptr_t value;
|
||||
};
|
||||
|
||||
void AddEntry(intptr_t tag, intptr_t value) {
|
||||
Entry* entry = new Entry();
|
||||
entry->tag = tag;
|
||||
entry->value = value;
|
||||
entries_.Add(entry);
|
||||
|
||||
memory_size += kElfDynamicTableEntrySize;
|
||||
file_size += kElfDynamicTableEntrySize;
|
||||
}
|
||||
|
||||
private:
|
||||
GrowableArray<Entry*> entries_;
|
||||
};
|
||||
|
||||
static uint8_t kNothing = 0;
|
||||
|
||||
// The first section must be written out and contains only zeros.
|
||||
static const intptr_t kNumInvalidSections = 1;
|
||||
|
||||
// Extra segments put in the program table that aren't reified in
|
||||
// Elf::segments_.
|
||||
static const intptr_t kNumImplicitSegments = 3;
|
||||
|
||||
Elf::Elf(Zone* zone, StreamingWriteStream* stream)
|
||||
: zone_(zone), stream_(stream), memory_offset_(0) {
|
||||
// Assumed by various offset logic in this file.
|
||||
ASSERT(stream_->position() == 0);
|
||||
|
||||
// We don't bother with a separate .shstrtab since all our strings will fit
|
||||
// in a single page.
|
||||
strtab_ = new (zone_) StringTable();
|
||||
strtab_->section_name = strtab_->AddString(".dynstr");
|
||||
AddSection(strtab_);
|
||||
|
||||
symtab_ = new (zone_) SymbolTable();
|
||||
symtab_->section_name = strtab_->AddString(".dynsym");
|
||||
symtab_->section_link = strtab_->section_index;
|
||||
AddSection(symtab_);
|
||||
|
||||
// dlsym gets confused if a symbol's value is dso offset 0, treating this as a
|
||||
// failed lookup instead of answering dso base + 0. dladdr answers the wrong
|
||||
// dso base if we don't start allocating from 0 (answering the address of
|
||||
// either the first or lowest PT_LOAD). Sacrifice the first page to work
|
||||
// around these issues. (gcc places build metadata in the first page.)
|
||||
AddROData("nothing", &kNothing, sizeof(kNothing));
|
||||
}
|
||||
|
||||
void Elf::AddSection(Section* section) {
|
||||
section->section_index = sections_.length() + kNumInvalidSections;
|
||||
sections_.Add(section);
|
||||
}
|
||||
|
||||
void Elf::AddSegment(Section* section) {
|
||||
if (section->alignment < kPageSize) {
|
||||
section->alignment = kPageSize;
|
||||
}
|
||||
|
||||
memory_offset_ = Utils::RoundUp(memory_offset_, section->alignment);
|
||||
section->memory_offset = memory_offset_;
|
||||
memory_offset_ += section->memory_size;
|
||||
segments_.Add(section);
|
||||
memory_offset_ = Utils::RoundUp(memory_offset_, kPageSize);
|
||||
}
|
||||
|
||||
intptr_t Elf::NextMemoryOffset() {
|
||||
return memory_offset_;
|
||||
}
|
||||
|
||||
intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) {
|
||||
ProgramBits* image = new (zone_) ProgramBits(true, true, bytes, size);
|
||||
image->section_name = strtab_->AddString(".text");
|
||||
AddSection(image);
|
||||
AddSegment(image);
|
||||
|
||||
Symbol* symbol = new (zone_) Symbol();
|
||||
symbol->cstr = name;
|
||||
symbol->name = strtab_->AddString(name);
|
||||
symbol->info = (STB_GLOBAL << 4) | STT_FUNC;
|
||||
symbol->section = image->section_index;
|
||||
// For shared libraries, this is the offset from the DSO base. For static
|
||||
// libraries, this is section relative.
|
||||
symbol->offset = image->memory_offset;
|
||||
symbol->size = size;
|
||||
symtab_->AddSymbol(symbol);
|
||||
|
||||
return symbol->offset;
|
||||
}
|
||||
|
||||
intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) {
|
||||
ProgramBits* image = new (zone_) ProgramBits(true, false, bytes, size);
|
||||
image->section_name = strtab_->AddString(".rodata");
|
||||
AddSection(image);
|
||||
AddSegment(image);
|
||||
|
||||
Symbol* symbol = new (zone_) Symbol();
|
||||
symbol->cstr = name;
|
||||
symbol->name = strtab_->AddString(name);
|
||||
symbol->info = (STB_GLOBAL << 4) | STT_OBJECT;
|
||||
symbol->section = image->section_index;
|
||||
// For shared libraries, this is the offset from the DSO base. For static
|
||||
// libraries, this is section relative.
|
||||
symbol->offset = image->memory_offset;
|
||||
symbol->size = size;
|
||||
symtab_->AddSymbol(symbol);
|
||||
|
||||
return symbol->offset;
|
||||
}
|
||||
|
||||
void Elf::AddDebug(const char* name, const uint8_t* bytes, intptr_t size) {
|
||||
ProgramBits* image = new (zone_) ProgramBits(false, false, bytes, size);
|
||||
image->section_name = strtab_->AddString(name);
|
||||
AddSection(image);
|
||||
}
|
||||
|
||||
void Elf::Finalize() {
|
||||
SymbolHashTable* hash = new (zone_) SymbolHashTable(strtab_, symtab_);
|
||||
hash->section_name = strtab_->AddString(".hash");
|
||||
AddSection(hash);
|
||||
AddSegment(hash);
|
||||
|
||||
// Before finalizing the string table's memory size:
|
||||
intptr_t name_dynamic = strtab_->AddString(".dynamic");
|
||||
|
||||
// Finalizes memory size of string and symbol tables.
|
||||
AddSegment(strtab_);
|
||||
AddSegment(symtab_);
|
||||
|
||||
dynamic_ = new (zone_) DynamicTable(strtab_, symtab_, hash);
|
||||
dynamic_->section_name = name_dynamic;
|
||||
AddSection(dynamic_);
|
||||
AddSegment(dynamic_);
|
||||
|
||||
ComputeFileOffsets();
|
||||
|
||||
WriteHeader();
|
||||
WriteProgramTable();
|
||||
WriteSectionTable();
|
||||
WriteSections();
|
||||
}
|
||||
|
||||
void Elf::ComputeFileOffsets() {
|
||||
intptr_t file_offset = kElfHeaderSize;
|
||||
|
||||
file_offset = Utils::RoundUp(file_offset, kPageSize);
|
||||
program_table_file_offset_ = file_offset;
|
||||
program_table_file_size_ =
|
||||
(segments_.length() + kNumImplicitSegments) * kElfProgramTableEntrySize;
|
||||
file_offset += program_table_file_size_;
|
||||
|
||||
section_table_file_offset_ = file_offset;
|
||||
section_table_file_size_ =
|
||||
(sections_.length() + kNumInvalidSections) * kElfSectionTableEntrySize;
|
||||
file_offset += section_table_file_size_;
|
||||
|
||||
for (intptr_t i = 0; i < sections_.length(); i++) {
|
||||
Section* section = sections_[i];
|
||||
file_offset = Utils::RoundUp(file_offset, section->alignment);
|
||||
section->file_offset = file_offset;
|
||||
file_offset += section->file_size;
|
||||
file_offset = Utils::RoundUp(file_offset, section->alignment);
|
||||
}
|
||||
}
|
||||
|
||||
void Elf::WriteHeader() {
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
uint8_t size = ELFCLASS32;
|
||||
#else
|
||||
uint8_t size = ELFCLASS64;
|
||||
#endif
|
||||
uint8_t e_ident[16] = {
|
||||
0x7f, 'E', 'L', 'F', size, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
|
||||
0, 0, 0, 0, 0, 0, 0, 0};
|
||||
stream_->WriteBytes(e_ident, 16);
|
||||
|
||||
WriteHalf(ET_DYN); // Shared library.
|
||||
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
WriteHalf(EM_386);
|
||||
#elif defined(TARGET_ARCH_X64)
|
||||
WriteHalf(EM_X86_64);
|
||||
#elif defined(TARGET_ARCH_ARM)
|
||||
WriteHalf(EM_ARM);
|
||||
#elif defined(TARGET_ARCH_ARM64)
|
||||
WriteHalf(EM_AARCH64);
|
||||
#else
|
||||
// E.g., DBC.
|
||||
FATAL("Unknown ELF architecture");
|
||||
#endif
|
||||
|
||||
WriteWord(EV_CURRENT); // Version
|
||||
WriteAddr(0); // "Entry point"
|
||||
WriteOff(program_table_file_offset_);
|
||||
WriteOff(section_table_file_offset_);
|
||||
|
||||
#if defined(TARGET_ARCH_ARM)
|
||||
uword flags = EF_ARM_ABI |
|
||||
(TargetCPUFeatures::hardfp_supported() ? EF_ARM_ABI_FLOAT_HARD
|
||||
: EF_ARM_ABI_FLOAT_SOFT);
|
||||
#else
|
||||
uword flags = 0;
|
||||
#endif
|
||||
WriteWord(flags);
|
||||
|
||||
WriteHalf(kElfHeaderSize);
|
||||
WriteHalf(kElfProgramTableEntrySize);
|
||||
WriteHalf(segments_.length() + kNumImplicitSegments);
|
||||
WriteHalf(kElfSectionTableEntrySize);
|
||||
WriteHalf(sections_.length() + kNumInvalidSections);
|
||||
WriteHalf(strtab_->section_index);
|
||||
|
||||
ASSERT(stream_->position() == kElfHeaderSize);
|
||||
}
|
||||
|
||||
void Elf::WriteProgramTable() {
|
||||
stream_->Align(kPageSize);
|
||||
|
||||
ASSERT(stream_->position() == program_table_file_offset_);
|
||||
|
||||
// Self-reference to program header table that Android wants for some reason.
|
||||
// Must appear before any PT_LOAD entries.
|
||||
{
|
||||
ASSERT(kNumImplicitSegments == 3);
|
||||
const intptr_t start = stream_->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
WriteWord(PT_PHDR);
|
||||
WriteOff(program_table_file_offset_);
|
||||
WriteAddr(memory_offset_);
|
||||
WriteAddr(0); // Physical address, not used.
|
||||
WriteWord(program_table_file_size_);
|
||||
WriteWord(program_table_file_size_);
|
||||
WriteWord(PF_R);
|
||||
WriteWord(kPageSize);
|
||||
#else
|
||||
WriteWord(PT_PHDR);
|
||||
WriteWord(PF_R);
|
||||
WriteOff(program_table_file_offset_);
|
||||
WriteAddr(memory_offset_);
|
||||
WriteAddr(0); // Physical address, not used.
|
||||
WriteXWord(program_table_file_size_);
|
||||
WriteXWord(program_table_file_size_);
|
||||
WriteXWord(kPageSize);
|
||||
#endif
|
||||
const intptr_t end = stream_->position();
|
||||
ASSERT((end - start) == kElfProgramTableEntrySize);
|
||||
}
|
||||
|
||||
for (intptr_t i = 0; i < segments_.length(); i++) {
|
||||
Section* section = segments_[i];
|
||||
const intptr_t start = stream_->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
WriteWord(section->segment_type);
|
||||
WriteOff(section->file_offset);
|
||||
WriteAddr(section->memory_offset);
|
||||
WriteAddr(0); // Physical address, not used.
|
||||
WriteWord(section->file_size);
|
||||
WriteWord(section->memory_size);
|
||||
WriteWord(section->segment_flags);
|
||||
WriteWord(section->alignment);
|
||||
#else
|
||||
WriteWord(section->segment_type);
|
||||
WriteWord(section->segment_flags);
|
||||
WriteOff(section->file_offset);
|
||||
WriteAddr(section->memory_offset);
|
||||
WriteAddr(0); // Physical address, not used.
|
||||
WriteXWord(section->file_size);
|
||||
WriteXWord(section->memory_size);
|
||||
WriteXWord(section->alignment);
|
||||
#endif
|
||||
const intptr_t end = stream_->position();
|
||||
ASSERT((end - start) == kElfProgramTableEntrySize);
|
||||
}
|
||||
|
||||
// Special case: the dynamic section requires both LOAD and DYNAMIC program
|
||||
// header table entries.
|
||||
{
|
||||
ASSERT(kNumImplicitSegments == 3);
|
||||
const intptr_t start = stream_->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
WriteWord(PT_DYNAMIC);
|
||||
WriteOff(dynamic_->file_offset);
|
||||
WriteAddr(dynamic_->memory_offset);
|
||||
WriteAddr(0); // Physical address, not used.
|
||||
WriteWord(dynamic_->file_size);
|
||||
WriteWord(dynamic_->memory_size);
|
||||
WriteWord(dynamic_->segment_flags);
|
||||
WriteWord(dynamic_->alignment);
|
||||
#else
|
||||
WriteWord(PT_DYNAMIC);
|
||||
WriteWord(dynamic_->segment_flags);
|
||||
WriteOff(dynamic_->file_offset);
|
||||
WriteAddr(dynamic_->memory_offset);
|
||||
WriteAddr(0); // Physical address, not used.
|
||||
WriteXWord(dynamic_->file_size);
|
||||
WriteXWord(dynamic_->memory_size);
|
||||
WriteXWord(dynamic_->alignment);
|
||||
#endif
|
||||
const intptr_t end = stream_->position();
|
||||
ASSERT((end - start) == kElfProgramTableEntrySize);
|
||||
}
|
||||
|
||||
// Self-reference to program header table that Android wants for some reason.
|
||||
{
|
||||
ASSERT(kNumImplicitSegments == 3);
|
||||
const intptr_t start = stream_->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
WriteWord(PT_LOAD);
|
||||
WriteOff(program_table_file_offset_);
|
||||
WriteAddr(memory_offset_);
|
||||
WriteAddr(0); // Physical address, not used.
|
||||
WriteWord(program_table_file_size_);
|
||||
WriteWord(program_table_file_size_);
|
||||
WriteWord(PF_R);
|
||||
WriteWord(kPageSize);
|
||||
#else
|
||||
WriteWord(PT_LOAD);
|
||||
WriteWord(PF_R);
|
||||
WriteOff(program_table_file_offset_);
|
||||
WriteAddr(memory_offset_);
|
||||
WriteAddr(0); // Physical address, not used.
|
||||
WriteXWord(program_table_file_size_);
|
||||
WriteXWord(program_table_file_size_);
|
||||
WriteXWord(kPageSize);
|
||||
#endif
|
||||
const intptr_t end = stream_->position();
|
||||
ASSERT((end - start) == kElfProgramTableEntrySize);
|
||||
}
|
||||
}
|
||||
|
||||
void Elf::WriteSectionTable() {
|
||||
ASSERT(stream_->position() == section_table_file_offset_);
|
||||
|
||||
{
|
||||
// The first entry in the section table is reserved and must be all zeros.
|
||||
ASSERT(kNumInvalidSections == 1);
|
||||
const intptr_t start = stream_->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteAddr(0);
|
||||
WriteOff(0);
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
#else
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteXWord(0);
|
||||
WriteAddr(0);
|
||||
WriteOff(0);
|
||||
WriteXWord(0);
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteXWord(0);
|
||||
WriteXWord(0);
|
||||
#endif
|
||||
const intptr_t end = stream_->position();
|
||||
ASSERT((end - start) == kElfSectionTableEntrySize);
|
||||
}
|
||||
|
||||
for (intptr_t i = 0; i < sections_.length(); i++) {
|
||||
Section* section = sections_[i];
|
||||
const intptr_t start = stream_->position();
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
WriteWord(section->section_name);
|
||||
WriteWord(section->section_type);
|
||||
WriteWord(section->section_flags);
|
||||
WriteAddr(section->memory_offset);
|
||||
WriteOff(section->file_offset);
|
||||
WriteWord(section->file_size); // Has different meaning for BSS.
|
||||
WriteWord(section->section_link);
|
||||
WriteWord(section->section_info);
|
||||
WriteWord(section->alignment);
|
||||
WriteWord(section->section_entry_size);
|
||||
#else
|
||||
WriteWord(section->section_name);
|
||||
WriteWord(section->section_type);
|
||||
WriteXWord(section->section_flags);
|
||||
WriteAddr(section->memory_offset);
|
||||
WriteOff(section->file_offset);
|
||||
WriteXWord(section->file_size); // Has different meaning for BSS.
|
||||
WriteWord(section->section_link);
|
||||
WriteWord(section->section_info);
|
||||
WriteXWord(section->alignment);
|
||||
WriteXWord(section->section_entry_size);
|
||||
#endif
|
||||
const intptr_t end = stream_->position();
|
||||
ASSERT((end - start) == kElfSectionTableEntrySize);
|
||||
}
|
||||
}
|
||||
|
||||
void Elf::WriteSections() {
|
||||
for (intptr_t i = 0; i < sections_.length(); i++) {
|
||||
Section* section = sections_[i];
|
||||
stream_->Align(section->alignment);
|
||||
ASSERT(stream_->position() == section->file_offset);
|
||||
section->Write(this);
|
||||
ASSERT(stream_->position() == section->file_offset + section->file_size);
|
||||
stream_->Align(section->alignment);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dart
|
84
runtime/vm/elf.h
Normal file
84
runtime/vm/elf.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
// 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 "vm/allocation.h"
|
||||
#include "vm/datastream.h"
|
||||
#include "vm/growable_array.h"
|
||||
#include "vm/zone.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
class DynamicTable;
|
||||
class Section;
|
||||
class StringTable;
|
||||
class Symbol;
|
||||
class SymbolTable;
|
||||
|
||||
class Elf : public ZoneAllocated {
|
||||
public:
|
||||
Elf(Zone* zone, StreamingWriteStream* stream);
|
||||
|
||||
intptr_t NextMemoryOffset();
|
||||
intptr_t AddText(const char* name, const uint8_t* bytes, intptr_t size);
|
||||
intptr_t AddROData(const char* name, const uint8_t* bytes, intptr_t size);
|
||||
void AddDebug(const char* name, const uint8_t* bytes, intptr_t size);
|
||||
|
||||
void Finalize();
|
||||
|
||||
intptr_t position() const { return stream_->position(); }
|
||||
void WriteBytes(const uint8_t* b, intptr_t size) {
|
||||
stream_->WriteBytes(b, size);
|
||||
}
|
||||
void WriteByte(uint8_t value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
void WriteHalf(uint16_t value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
void WriteWord(uint32_t value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
void WriteAddr(uword value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
void WriteOff(uword value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
#if defined(ARCH_IS_64_BIT)
|
||||
void WriteXWord(uint64_t value) {
|
||||
stream_->WriteBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
void AddSection(Section* section);
|
||||
void AddSegment(Section* section);
|
||||
|
||||
void ComputeFileOffsets();
|
||||
void WriteHeader();
|
||||
void WriteSectionTable();
|
||||
void WriteProgramTable();
|
||||
void WriteSections();
|
||||
|
||||
Zone* const zone_;
|
||||
StreamingWriteStream* stream_;
|
||||
GrowableArray<Section*> sections_;
|
||||
GrowableArray<Section*> segments_;
|
||||
|
||||
intptr_t memory_offset_;
|
||||
intptr_t section_table_file_offset_;
|
||||
intptr_t section_table_file_size_;
|
||||
intptr_t program_table_file_offset_;
|
||||
intptr_t program_table_file_size_;
|
||||
StringTable* strtab_;
|
||||
SymbolTable* symtab_;
|
||||
DynamicTable* dynamic_;
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_ELF_H_
|
|
@ -7,6 +7,7 @@
|
|||
#include "platform/assert.h"
|
||||
#include "vm/compiler/backend/code_statistics.h"
|
||||
#include "vm/dwarf.h"
|
||||
#include "vm/elf.h"
|
||||
#include "vm/hash.h"
|
||||
#include "vm/hash_map.h"
|
||||
#include "vm/heap/heap.h"
|
||||
|
@ -359,10 +360,10 @@ AssemblyImageWriter::AssemblyImageWriter(Thread* thread,
|
|||
const void* shared_instructions)
|
||||
: ImageWriter(thread->heap(), shared_objects, shared_instructions, nullptr),
|
||||
assembly_stream_(512 * KB, callback, callback_data),
|
||||
dwarf_(NULL) {
|
||||
dwarf_(nullptr) {
|
||||
#if defined(DART_PRECOMPILER)
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
dwarf_ = new (zone) Dwarf(zone, &assembly_stream_);
|
||||
dwarf_ = new (zone) Dwarf(zone, &assembly_stream_, /* elf= */ nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -676,12 +677,20 @@ BlobImageWriter::BlobImageWriter(Thread* thread,
|
|||
intptr_t initial_size,
|
||||
const void* shared_objects,
|
||||
const void* shared_instructions,
|
||||
const void* reused_instructions)
|
||||
const void* reused_instructions,
|
||||
Elf* elf,
|
||||
Dwarf* dwarf)
|
||||
: ImageWriter(thread->heap(),
|
||||
shared_objects,
|
||||
shared_instructions,
|
||||
reused_instructions),
|
||||
instructions_blob_stream_(instructions_blob_buffer, alloc, initial_size) {
|
||||
instructions_blob_stream_(instructions_blob_buffer, alloc, initial_size),
|
||||
elf_(elf),
|
||||
dwarf_(dwarf) {
|
||||
#ifndef DART_PRECOMPILER
|
||||
RELEASE_ASSERT(elf_ == nullptr);
|
||||
RELEASE_ASSERT(dwarf_ == nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
intptr_t BlobImageWriter::WriteByteSequence(uword start, uword end) {
|
||||
|
@ -693,6 +702,13 @@ intptr_t BlobImageWriter::WriteByteSequence(uword start, uword end) {
|
|||
}
|
||||
|
||||
void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||
#ifdef DART_PRECOMPILER
|
||||
intptr_t segment_base = 0;
|
||||
if (elf_ != nullptr) {
|
||||
segment_base = elf_->NextMemoryOffset();
|
||||
}
|
||||
#endif
|
||||
|
||||
// This header provides the gap to make the instructions snapshot look like a
|
||||
// HeapPage.
|
||||
intptr_t instructions_length = next_text_offset_;
|
||||
|
@ -733,6 +749,15 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
|||
ASSERT(Utils::IsAligned(beginning, sizeof(uword)));
|
||||
ASSERT(Utils::IsAligned(entry, sizeof(uword)));
|
||||
|
||||
#ifdef DART_PRECOMPILER
|
||||
const Code& code = *instructions_[i].code_;
|
||||
if ((elf_ != nullptr) && !code.IsNull()) {
|
||||
intptr_t segment_offset = instructions_blob_stream_.bytes_written() +
|
||||
Instructions::HeaderSize();
|
||||
dwarf_->AddCode(code, segment_base + segment_offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write Instructions with the mark and read-only bits set.
|
||||
uword marked_tags = insns.raw_ptr()->tags_;
|
||||
marked_tags = RawObject::OldBit::update(true, marked_tags);
|
||||
|
@ -752,6 +777,17 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
|||
|
||||
ASSERT((text_offset - instr_start) == insns.raw()->HeapSize());
|
||||
}
|
||||
|
||||
#ifdef DART_PRECOMPILER
|
||||
if (elf_ != nullptr) {
|
||||
const char* instructions_symbol = vm ? "_kDartVmSnapshotInstructions"
|
||||
: "_kDartIsolateSnapshotInstructions";
|
||||
intptr_t segment_base2 =
|
||||
elf_->AddText(instructions_symbol, instructions_blob_stream_.buffer(),
|
||||
instructions_blob_stream_.bytes_written());
|
||||
ASSERT(segment_base == segment_base2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ImageReader::ImageReader(const uint8_t* data_image,
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace dart {
|
|||
// Forward declarations.
|
||||
class Code;
|
||||
class Dwarf;
|
||||
class Elf;
|
||||
class Instructions;
|
||||
class Object;
|
||||
class RawApiError;
|
||||
|
@ -333,7 +334,9 @@ class BlobImageWriter : public ImageWriter {
|
|||
intptr_t initial_size,
|
||||
const void* shared_objects,
|
||||
const void* shared_instructions,
|
||||
const void* reused_instructions);
|
||||
const void* reused_instructions,
|
||||
Elf* elf = nullptr,
|
||||
Dwarf* dwarf = nullptr);
|
||||
|
||||
virtual void WriteText(WriteStream* clustered_stream, bool vm);
|
||||
|
||||
|
@ -345,6 +348,8 @@ class BlobImageWriter : public ImageWriter {
|
|||
intptr_t WriteByteSequence(uword start, uword end);
|
||||
|
||||
WriteStream instructions_blob_stream_;
|
||||
Elf* elf_;
|
||||
Dwarf* dwarf_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BlobImageWriter);
|
||||
};
|
||||
|
|
|
@ -95,6 +95,8 @@ vm_sources = [
|
|||
"double_internals.h",
|
||||
"dwarf.cc",
|
||||
"dwarf.h",
|
||||
"elf.cc",
|
||||
"elf.h",
|
||||
"exceptions.cc",
|
||||
"exceptions.h",
|
||||
"finalizable_data.h",
|
||||
|
|
|
@ -274,9 +274,9 @@
|
|||
"options": {
|
||||
"host-checked": true
|
||||
}},
|
||||
"dartkp-android-(debug|product|release)-arm": {
|
||||
"dartkp-android-(debug|product|release)-(arm|arm64)": {
|
||||
"options": {
|
||||
"use-blobs": true
|
||||
"use-elf": true
|
||||
}},
|
||||
"dartk-android-(debug|product|release)-(arm|arm64)": {},
|
||||
"dartkp-linux-(debug|product|release)-(simarm|simarm64)": {
|
||||
|
|
|
@ -76,10 +76,15 @@ class Command {
|
|||
checked: checked);
|
||||
}
|
||||
|
||||
static Command adbPrecompiled(String precompiledRunner, String processTest,
|
||||
String testDirectory, List<String> arguments, bool useBlobs) {
|
||||
return new AdbPrecompilationCommand._(
|
||||
precompiledRunner, processTest, testDirectory, arguments, useBlobs);
|
||||
static Command adbPrecompiled(
|
||||
String precompiledRunner,
|
||||
String processTest,
|
||||
String testDirectory,
|
||||
List<String> arguments,
|
||||
bool useBlobs,
|
||||
bool useElf) {
|
||||
return new AdbPrecompilationCommand._(precompiledRunner, processTest,
|
||||
testDirectory, arguments, useBlobs, useElf);
|
||||
}
|
||||
|
||||
static Command adbDartk(String precompiledRunner, String processTest,
|
||||
|
@ -589,6 +594,7 @@ class AdbPrecompilationCommand extends Command {
|
|||
final String precompiledTestDirectory;
|
||||
final List<String> arguments;
|
||||
final bool useBlobs;
|
||||
final bool useElf;
|
||||
|
||||
AdbPrecompilationCommand._(
|
||||
this.precompiledRunnerFilename,
|
||||
|
@ -596,6 +602,7 @@ class AdbPrecompilationCommand extends Command {
|
|||
this.precompiledTestDirectory,
|
||||
this.arguments,
|
||||
this.useBlobs,
|
||||
this.useElf,
|
||||
{int index = 0})
|
||||
: super._("adb_precompilation", index: index);
|
||||
|
||||
|
@ -605,6 +612,7 @@ class AdbPrecompilationCommand extends Command {
|
|||
precompiledTestDirectory,
|
||||
arguments,
|
||||
useBlobs,
|
||||
useElf,
|
||||
index: index);
|
||||
_buildHashCode(HashCodeBuilder builder) {
|
||||
super._buildHashCode(builder);
|
||||
|
@ -612,12 +620,14 @@ class AdbPrecompilationCommand extends Command {
|
|||
builder.add(precompiledTestDirectory);
|
||||
builder.add(arguments);
|
||||
builder.add(useBlobs);
|
||||
builder.add(useElf);
|
||||
}
|
||||
|
||||
bool _equal(AdbPrecompilationCommand other) =>
|
||||
super._equal(other) &&
|
||||
precompiledRunnerFilename == other.precompiledRunnerFilename &&
|
||||
useBlobs == other.useBlobs &&
|
||||
useElf == other.useElf &&
|
||||
arguments == other.arguments &&
|
||||
precompiledTestDirectory == other.precompiledTestDirectory;
|
||||
|
||||
|
|
|
@ -673,7 +673,7 @@ class PrecompilerCompilerConfiguration extends CompilerConfiguration
|
|||
tempDir, arguments, environmentOverrides));
|
||||
}
|
||||
|
||||
if (!_configuration.useBlobs) {
|
||||
if (!_configuration.useBlobs && !_configuration.useElf) {
|
||||
commands.add(
|
||||
computeAssembleCommand(tempDir, arguments, environmentOverrides));
|
||||
if (!_configuration.keepGeneratedFiles) {
|
||||
|
@ -729,6 +729,9 @@ class PrecompilerCompilerConfiguration extends CompilerConfiguration
|
|||
if (_configuration.useBlobs) {
|
||||
args.add("--snapshot-kind=app-aot-blobs");
|
||||
args.add("--blobs_container_filename=$tempDir/out.aotsnapshot");
|
||||
} else if (_configuration.useElf) {
|
||||
args.add("--snapshot-kind=app-aot-elf");
|
||||
args.add("--assembly=$tempDir/out.aotsnapshot");
|
||||
} else {
|
||||
args.add("--snapshot-kind=app-aot-assembly");
|
||||
args.add("--assembly=$tempDir/out.S");
|
||||
|
|
|
@ -115,6 +115,7 @@ class TestConfiguration {
|
|||
bool get useAnalyzerCfe => configuration.useAnalyzerCfe;
|
||||
bool get useAnalyzerFastaParser => configuration.useAnalyzerFastaParser;
|
||||
bool get useBlobs => configuration.useBlobs;
|
||||
bool get useElf => configuration.useElf;
|
||||
bool get useSdk => configuration.useSdk;
|
||||
bool get useEnableAsserts => configuration.enableAsserts;
|
||||
|
||||
|
|
|
@ -181,6 +181,9 @@ test options, specifying how tests should be run.''',
|
|||
new _Option.bool(
|
||||
'use_blobs', 'Use mmap instead of shared libraries for precompilation.',
|
||||
hide: true),
|
||||
new _Option.bool(
|
||||
'use_elf', 'Directly generate an ELF shared libraries for precompilation.',
|
||||
hide: true),
|
||||
new _Option.bool('keep_generated_files', 'Keep any generated files.',
|
||||
abbr: 'k'),
|
||||
new _Option.int('timeout', 'Timeout in seconds.', abbr: 't'),
|
||||
|
@ -688,6 +691,7 @@ compiler.''',
|
|||
useAnalyzerFastaParser:
|
||||
data["analyzer_use_fasta_parser"] as bool,
|
||||
useBlobs: data["use_blobs"] as bool,
|
||||
useElf: data["use_elf"] as bool,
|
||||
useSdk: data["use_sdk"] as bool,
|
||||
useHotReload: data["hot_reload"] as bool,
|
||||
useHotReloadRollback: data["hot_reload_rollback"] as bool,
|
||||
|
|
|
@ -50,10 +50,14 @@ abstract class RuntimeConfiguration {
|
|||
case Runtime.dartPrecompiled:
|
||||
if (configuration.system == System.android) {
|
||||
return new DartPrecompiledAdbRuntimeConfiguration(
|
||||
useBlobs: configuration.useBlobs);
|
||||
useBlobs: configuration.useBlobs,
|
||||
useElf: configuration.useElf,
|
||||
);
|
||||
} else {
|
||||
return new DartPrecompiledRuntimeConfiguration(
|
||||
useBlobs: configuration.useBlobs);
|
||||
useBlobs: configuration.useBlobs,
|
||||
useElf: configuration.useElf,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -238,7 +242,10 @@ class StandaloneDartRuntimeConfiguration extends DartVmRuntimeConfiguration {
|
|||
|
||||
class DartPrecompiledRuntimeConfiguration extends DartVmRuntimeConfiguration {
|
||||
final bool useBlobs;
|
||||
DartPrecompiledRuntimeConfiguration({bool useBlobs}) : useBlobs = useBlobs;
|
||||
final bool useElf;
|
||||
DartPrecompiledRuntimeConfiguration({bool useBlobs, bool useElf})
|
||||
: useBlobs = useBlobs,
|
||||
useElf = useElf;
|
||||
|
||||
List<Command> computeRuntimeCommands(
|
||||
TestSuite suite,
|
||||
|
@ -292,7 +299,10 @@ class DartPrecompiledAdbRuntimeConfiguration
|
|||
'/data/local/tmp/precompilation-testing/test';
|
||||
|
||||
final bool useBlobs;
|
||||
DartPrecompiledAdbRuntimeConfiguration({bool useBlobs}) : useBlobs = useBlobs;
|
||||
final bool useElf;
|
||||
DartPrecompiledAdbRuntimeConfiguration({bool useBlobs, bool useElf})
|
||||
: useBlobs = useBlobs,
|
||||
useElf = useElf;
|
||||
|
||||
List<Command> computeRuntimeCommands(
|
||||
TestSuite suite,
|
||||
|
@ -311,7 +321,7 @@ class DartPrecompiledAdbRuntimeConfiguration
|
|||
String processTest = suite.processTestBinaryFileName;
|
||||
return [
|
||||
Command.adbPrecompiled(
|
||||
precompiledRunner, processTest, script, arguments, useBlobs)
|
||||
precompiledRunner, processTest, script, arguments, useBlobs, useElf)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue