diff --git a/README.md b/README.md index f0c4d1a..404e342 100644 --- a/README.md +++ b/README.md @@ -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 << ""; + } + 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 << ""; + break; + case FIXED_ARRAY_TYPE: +- os << ""; ++ len = FixedArray::cast(*this).length(); ++ os << ""; ++ ++ if (len) { ++ os << "\n; #region FixedArray\n"; ++ FixedArray::cast(*this).FixedArrayPrint(os); ++ os << "; #endregion"; ++ } ++ + break; + case OBJECT_BOILERPLATE_DESCRIPTION_TYPE: +- os << ""; ++ len = FixedArray::cast(*this).length(); ++ os << ""; ++ ++ 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(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(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 +``` diff --git a/v8dasm.cpp b/v8dasm.cpp new file mode 100644 index 0000000..3336e45 --- /dev/null +++ b/v8dasm.cpp @@ -0,0 +1,87 @@ +#include +#include + +#include + +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 v8_script = ScriptCompiler::CompileUnboundScript( + isolate, &source, ScriptCompiler::kConsumeCodeCache); +} + +static void readAllBytes(const std::string& file, std::vector& 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 data; + readAllBytes(argv[1], data); + runBytecode((uint8_t*)data.data(), data.size()); + } +}