-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
229 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,144 @@ | ||
# V8-Bytecode-Disassemble | ||
Disassemble V8 Ignition bytecode. | ||
|
||
Test on linux. | ||
## 0.Credit | ||
Slightly modified from [v8dasm](https://github.com/noelex/v8dasm). | ||
|
||
## 1.Modify V8 Source Code | ||
In `code-serializer.cc`, insert the following lines after the serialization is completed (after maybe_result is successfully converted to result) in function `CodeSerializer::Deserialize`: | ||
|
||
```c++ | ||
result->GetBytecodeArray(isolate).Disassemble(std::cout); | ||
std::cout << std::flush; | ||
``` | ||
Modify function `SharedFunctionInfo::SharedFunctionInfoPrint` in `objects-printer.cc`: | ||
```diff | ||
diff --git a/src/diagnostics/objects-printer.cc b/src/diagnostics/objects-printer.cc | ||
index 2ec89d58962..7665668d8ae 100644 | ||
--- a/src/diagnostics/objects-printer.cc | ||
+++ b/src/diagnostics/objects-printer.cc | ||
@@ -1652,6 +1652,14 @@ void SharedFunctionInfo::SharedFunctionInfoPrint(std::ostream& os) { | ||
os << "<none>"; | ||
} | ||
os << "\n"; | ||
+ | ||
+ os << "\n; --- start SharedFunctionInfoDisassembly\n"; | ||
+ Isolate* isolate = GetIsolate(); | ||
+ if (this->HasBytecodeArray()) { | ||
+ this->GetBytecodeArray(isolate).Disassemble(os); | ||
+ os << std::flush; | ||
+ } | ||
+ os << "; --- end SharedFunctionInfoDisassembly\n"; | ||
} | ||
``` | ||
|
||
Modify function HeapObject::HeapObjectShortPrint in objects.cc. | ||
|
||
```diff | ||
diff --git a/src/objects/objects.cc b/src/objects/objects.cc | ||
index 4616ef7ab74..9b1e67a2f4a 100644 | ||
--- a/src/objects/objects.cc | ||
+++ b/src/objects/objects.cc | ||
@@ -1860,6 +1860,7 @@ void HeapObject::HeapObjectShortPrint(std::ostream& os) { | ||
os << accumulator.ToCString().get(); | ||
return; | ||
} | ||
+ int len; | ||
switch (map(cage_base).instance_type()) { | ||
case MAP_TYPE: { | ||
os << "<Map"; | ||
@@ -1941,11 +1942,27 @@ void HeapObject::HeapObjectShortPrint(std::ostream& os) { | ||
<< "]>"; | ||
break; | ||
case FIXED_ARRAY_TYPE: | ||
- os << "<FixedArray[" << FixedArray::cast(*this).length() << "]>"; | ||
+ len = FixedArray::cast(*this).length(); | ||
+ os << "<FixedArray[" << len << "]>"; | ||
+ | ||
+ if (len) { | ||
+ os << "\n; #region FixedArray\n"; | ||
+ FixedArray::cast(*this).FixedArrayPrint(os); | ||
+ os << "; #endregion"; | ||
+ } | ||
+ | ||
break; | ||
case OBJECT_BOILERPLATE_DESCRIPTION_TYPE: | ||
- os << "<ObjectBoilerplateDescription[" << FixedArray::cast(*this).length() | ||
- << "]>"; | ||
+ len = FixedArray::cast(*this).length(); | ||
+ os << "<ObjectBoilerplateDescription[" << len << "]>"; | ||
+ | ||
+ if (len) { | ||
+ os << "\n; #region ObjectBoilerplateDescription\n"; | ||
+ ObjectBoilerplateDescription::cast(*this) | ||
+ .ObjectBoilerplateDescriptionPrint(os); | ||
+ os << "; #endregion"; | ||
+ } | ||
+ | ||
break; | ||
case FIXED_DOUBLE_ARRAY_TYPE: | ||
``` | ||
|
||
## Configure and Build V8 | ||
Make sure to check if the bytecode you have works with `Pointer Compression` or not. | ||
|
||
For example, it will cause different length-of-steps when executing `CopySlots` fuction. | ||
|
||
```c++ | ||
void CopySlots(Address* dest, int number_of_slots) { | ||
base::AtomicWord* start = reinterpret_cast<base::AtomicWord*>(dest); | ||
base::AtomicWord* end = start + number_of_slots; | ||
for (base::AtomicWord* p = start; p < end; | ||
++p, position_ += sizeof(base::AtomicWord)) { | ||
base::AtomicWord val; | ||
memcpy(&val, data_ + position_, sizeof(base::AtomicWord)); | ||
base::Relaxed_Store(p, val); | ||
} | ||
} | ||
|
||
#ifdef V8_COMPRESS_POINTERS | ||
void CopySlots(Tagged_t* dest, int number_of_slots) { | ||
AtomicTagged_t* start = reinterpret_cast<AtomicTagged_t*>(dest); | ||
AtomicTagged_t* end = start + number_of_slots; | ||
for (AtomicTagged_t* p = start; p < end; | ||
++p, position_ += sizeof(AtomicTagged_t)) { | ||
AtomicTagged_t val; | ||
memcpy(&val, data_ + position_, sizeof(AtomicTagged_t)); | ||
base::Relaxed_Store(p, val); | ||
} | ||
} | ||
#endif | ||
``` | ||
Configure with the following arguments if `Pointer Compression` is disabled. | ||
``` | ||
is_debug = false | ||
target_cpu = "x64" | ||
v8_enable_backtrace = true | ||
v8_enable_slow_dchecks = true | ||
v8_optimized_debug = false | ||
is_component_build = false | ||
v8_static_library = true | ||
v8_enable_disassembler = true | ||
v8_enable_object_print = true | ||
use_custom_libcxx = false | ||
use_custom_libcxx_for_host = false | ||
v8_use_external_startup_data = false | ||
enable_iterator_debugging = false | ||
use_goma=false | ||
goma_dir="None" | ||
v8_enable_pointer_compression = false | ||
``` | ||
## Custom Code Cache loader | ||
Write a program to call `ScriptCompiler::CompileUnboundScript` to load code cache from file. `v8dasm.cpp` is a sample program for linux. | ||
Build | ||
```bash | ||
clang++ v8dasm.cpp -std=c++17 -I../../include -Lobj -lv8_libbase -lv8_libplatform -lv8_base_without_compiler -lwee8 -o v8dasm | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#include <libplatform/libplatform.h> | ||
#include <v8.h> | ||
|
||
#include <string> | ||
|
||
using namespace v8; | ||
|
||
static Isolate* isolate = nullptr; | ||
|
||
static v8::ScriptCompiler::CachedData* compileCode(const char* data) { | ||
auto str = String::NewFromUtf8(isolate, data).ToLocalChecked(); | ||
auto script = | ||
Script::Compile(isolate->GetCurrentContext(), str).ToLocalChecked(); | ||
auto unboundScript = script->GetUnboundScript(); | ||
|
||
return ScriptCompiler::CreateCodeCache(unboundScript); | ||
} | ||
|
||
static void fixBytecode(uint8_t* bytecodeBuffer, const char* code) { | ||
auto dummyBytecode = compileCode(code); | ||
// Copy version hash, source hash and flag hash from dummy bytecode to source | ||
// bytecode. Offsets of these value may differ in different version of V8. | ||
// Refer V8 src/snapshot/code-serializer.h for details. | ||
for (int i = 4; i < 16; i++) { | ||
bytecodeBuffer[i] = dummyBytecode->data[i]; | ||
} | ||
delete dummyBytecode; | ||
} | ||
|
||
static void runBytecode(uint8_t* bytecodeBuffer, int len) { | ||
// Compile some dummy code to get version hash, source hash and flag hash. | ||
const char* code = "console.log('hello');"; | ||
fixBytecode(bytecodeBuffer, code); | ||
|
||
// Load code into code cache. | ||
auto cached_data = new ScriptCompiler::CachedData(bytecodeBuffer, len); | ||
|
||
// Create dummy source. | ||
ScriptOrigin origin(isolate, String::NewFromUtf8Literal(isolate, "code.jsc")); | ||
ScriptCompiler::Source source( | ||
String::NewFromUtf8(isolate, code).ToLocalChecked(), origin, cached_data); | ||
|
||
// Compile code from code cache to print disassembly. | ||
MaybeLocal<UnboundScript> v8_script = ScriptCompiler::CompileUnboundScript( | ||
isolate, &source, ScriptCompiler::kConsumeCodeCache); | ||
} | ||
|
||
static void readAllBytes(const std::string& file, std::vector<char>& buffer) { | ||
std::ifstream infile(file, std::ifstream::binary); | ||
|
||
infile.seekg(0, infile.end); | ||
size_t length = infile.tellg(); | ||
infile.seekg(0, infile.beg); | ||
|
||
if (length > 0) { | ||
buffer.resize(length); | ||
infile.read(&buffer[0], length); | ||
} | ||
} | ||
|
||
int main(int argc, char* argv[]) { | ||
// Set flags here, flags that affects code generation and seririalzation | ||
// should be same as the target program. You can add other flags freely | ||
// because flag hash will be overrided in fixBytecode(). | ||
v8::V8::SetFlagsFromString("--no-lazy --no-flush-bytecode --log-all"); | ||
|
||
v8::V8::InitializeICUDefaultLocation(argv[0]); | ||
v8::V8::InitializeExternalStartupData(argv[0]); | ||
auto plat = v8::platform::NewDefaultPlatform(); | ||
v8::V8::InitializePlatform(plat.get()); | ||
v8::V8::Initialize(); | ||
|
||
Isolate::CreateParams p = {}; | ||
p.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); | ||
|
||
isolate = Isolate::New(p); | ||
{ | ||
v8::Isolate::Scope isolate_scope(isolate); | ||
v8::HandleScope scope(isolate); | ||
auto ctx = v8::Context::New(isolate); | ||
Context::Scope context_scope(ctx); | ||
|
||
std::vector<char> data; | ||
readAllBytes(argv[1], data); | ||
runBytecode((uint8_t*)data.data(), data.size()); | ||
} | ||
} |