xref: /third_party/node/src/node_sea.cc (revision 1cb0ef41)
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