1#include "node_sea.h" 2 3#include "env-inl.h" 4#include "node_external_reference.h" 5#include "node_internals.h" 6#include "node_union_bytes.h" 7#include "simdutf.h" 8#include "v8.h" 9 10// The POSTJECT_SENTINEL_FUSE macro is a string of random characters selected by 11// the Node.js project that is present only once in the entire binary. It is 12// used by the postject_has_resource() function to efficiently detect if a 13// resource has been injected. See 14// https://github.com/nodejs/postject/blob/35343439cac8c488f2596d7c4c1dddfec1fddcae/postject-api.h#L42-L45. 15#define POSTJECT_SENTINEL_FUSE "NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2" 16#include "postject-api.h" 17#undef POSTJECT_SENTINEL_FUSE 18 19#include <memory> 20#include <string_view> 21#include <tuple> 22#include <vector> 23 24#if !defined(DISABLE_SINGLE_EXECUTABLE_APPLICATION) 25 26using v8::Context; 27using v8::FunctionCallbackInfo; 28using v8::Local; 29using v8::Object; 30using v8::Value; 31 32namespace { 33 34const std::string_view FindSingleExecutableCode() { 35 static const std::string_view sea_code = []() -> std::string_view { 36 size_t size; 37#ifdef __APPLE__ 38 postject_options options; 39 postject_options_init(&options); 40 options.macho_segment_name = "NODE_JS"; 41 const char* code = static_cast<const char*>( 42 postject_find_resource("NODE_JS_CODE", &size, &options)); 43#else 44 const char* code = static_cast<const char*>( 45 postject_find_resource("NODE_JS_CODE", &size, nullptr)); 46#endif 47 return {code, size}; 48 }(); 49 return sea_code; 50} 51 52void GetSingleExecutableCode(const FunctionCallbackInfo<Value>& args) { 53 node::Environment* env = node::Environment::GetCurrent(args); 54 55 static const std::string_view sea_code = FindSingleExecutableCode(); 56 57 if (sea_code.empty()) { 58 return; 59 } 60 61 // TODO(joyeecheung): Use one-byte strings for ASCII-only source to save 62 // memory/binary size - using UTF16 by default results in twice of the size 63 // than necessary. 64 static const node::UnionBytes sea_code_union_bytes = 65 []() -> node::UnionBytes { 66 size_t expected_u16_length = 67 simdutf::utf16_length_from_utf8(sea_code.data(), sea_code.size()); 68 auto out = std::make_shared<std::vector<uint16_t>>(expected_u16_length); 69 size_t u16_length = simdutf::convert_utf8_to_utf16( 70 sea_code.data(), 71 sea_code.size(), 72 reinterpret_cast<char16_t*>(out->data())); 73 out->resize(u16_length); 74 return node::UnionBytes{out}; 75 }(); 76 77 args.GetReturnValue().Set( 78 sea_code_union_bytes.ToStringChecked(env->isolate())); 79} 80 81} // namespace 82 83namespace node { 84namespace sea { 85 86bool IsSingleExecutable() { 87 return postject_has_resource(); 88} 89 90std::tuple<int, char**> FixupArgsForSEA(int argc, char** argv) { 91 // Repeats argv[0] at position 1 on argv as a replacement for the missing 92 // entry point file path. 93 if (IsSingleExecutable()) { 94 static std::vector<char*> new_argv; 95 new_argv.reserve(argc + 2); 96 new_argv.emplace_back(argv[0]); 97 new_argv.insert(new_argv.end(), argv, argv + argc); 98 new_argv.emplace_back(nullptr); 99 argc = new_argv.size() - 1; 100 argv = new_argv.data(); 101 } 102 103 return {argc, argv}; 104} 105 106void Initialize(Local<Object> target, 107 Local<Value> unused, 108 Local<Context> context, 109 void* priv) { 110 SetMethod( 111 context, target, "getSingleExecutableCode", GetSingleExecutableCode); 112} 113 114void RegisterExternalReferences(ExternalReferenceRegistry* registry) { 115 registry->Register(GetSingleExecutableCode); 116} 117 118} // namespace sea 119} // namespace node 120 121NODE_BINDING_CONTEXT_AWARE_INTERNAL(sea, node::sea::Initialize) 122NODE_BINDING_EXTERNAL_REFERENCE(sea, node::sea::RegisterExternalReferences) 123 124#endif // !defined(DISABLE_SINGLE_EXECUTABLE_APPLICATION) 125