1
1
#include " node_sea.h"
2
2
3
+ #include " blob_serializer_deserializer-inl.h"
3
4
#include " debug_utils-inl.h"
4
5
#include " env-inl.h"
5
6
#include " json_parser.h"
@@ -34,16 +35,6 @@ namespace node {
34
35
namespace sea {
35
36
36
37
namespace {
37
- // A special number that will appear at the beginning of the single executable
38
- // preparation blobs ready to be injected into the binary. We use this to check
39
- // that the data given to us are intended for building single executable
40
- // applications.
41
- const uint32_t kMagic = 0x143da20 ;
42
-
43
- enum class SeaFlags : uint32_t {
44
- kDefault = 0 ,
45
- kDisableExperimentalSeaWarning = 1 << 0 ,
46
- };
47
38
48
39
SeaFlags operator |(SeaFlags x, SeaFlags y) {
49
40
return static_cast <SeaFlags>(static_cast <uint32_t >(x) |
@@ -59,47 +50,100 @@ SeaFlags operator|=(/* NOLINT (runtime/references) */ SeaFlags& x, SeaFlags y) {
59
50
return x = x | y;
60
51
}
61
52
62
- struct SeaResource {
63
- SeaFlags flags = SeaFlags::kDefault ;
64
- std::string_view code;
65
- static constexpr size_t kHeaderSize = sizeof (kMagic ) + sizeof (SeaFlags);
53
+ class SeaSerializer : public BlobSerializer <SeaSerializer> {
54
+ public:
55
+ SeaSerializer ()
56
+ : BlobSerializer<SeaSerializer>(
57
+ per_process::enabled_debug_list.enabled(DebugCategory::SEA)) {}
58
+
59
+ template <typename T,
60
+ std::enable_if_t <!std::is_same<T, std::string>::value>* = nullptr ,
61
+ std::enable_if_t <!std::is_arithmetic<T>::value>* = nullptr >
62
+ size_t Write (const T& data);
66
63
};
67
64
68
- SeaResource FindSingleExecutableResource () {
65
+ template <>
66
+ size_t SeaSerializer::Write (const SeaResource& sea) {
67
+ sink.reserve (SeaResource::kHeaderSize + sea.code .size ());
68
+
69
+ Debug (" Write SEA magic %x\n " , kMagic );
70
+ size_t written_total = WriteArithmetic<uint32_t >(kMagic );
71
+
72
+ uint32_t flags = static_cast <uint32_t >(sea.flags );
73
+ Debug (" Write SEA flags %x\n " , flags);
74
+ written_total += WriteArithmetic<uint32_t >(flags);
75
+ DCHECK_EQ (written_total, SeaResource::kHeaderSize );
76
+
77
+ Debug (" Write SEA resource code %p, size=%zu\n " ,
78
+ sea.code .data (),
79
+ sea.code .size ());
80
+ written_total += WriteStringView (sea.code , StringLogMode::kAddressAndContent );
81
+ return written_total;
82
+ }
83
+
84
+ class SeaDeserializer : public BlobDeserializer <SeaDeserializer> {
85
+ public:
86
+ explicit SeaDeserializer (std::string_view v)
87
+ : BlobDeserializer<SeaDeserializer>(
88
+ per_process::enabled_debug_list.enabled(DebugCategory::SEA), v) {}
89
+
90
+ template <typename T,
91
+ std::enable_if_t <!std::is_same<T, std::string>::value>* = nullptr ,
92
+ std::enable_if_t <!std::is_arithmetic<T>::value>* = nullptr >
93
+ T Read ();
94
+ };
95
+
96
+ template <>
97
+ SeaResource SeaDeserializer::Read () {
98
+ uint32_t magic = ReadArithmetic<uint32_t >();
99
+ Debug (" Read SEA magic %x\n " , magic);
100
+
101
+ CHECK_EQ (magic, kMagic );
102
+ SeaFlags flags (static_cast <SeaFlags>(ReadArithmetic<uint32_t >()));
103
+ Debug (" Read SEA flags %x\n " , static_cast <uint32_t >(flags));
104
+ CHECK_EQ (read_total, SeaResource::kHeaderSize );
105
+
106
+ std::string_view code = ReadStringView (StringLogMode::kAddressAndContent );
107
+ Debug (" Read SEA resource code %p, size=%zu\n " , code.data (), code.size ());
108
+ return {flags, code};
109
+ }
110
+
111
+ std::string_view FindSingleExecutableBlob () {
69
112
CHECK (IsSingleExecutable ());
70
- static const SeaResource sea_resource = []() -> SeaResource {
113
+ static const std::string_view result = []() -> std::string_view {
71
114
size_t size;
72
115
#ifdef __APPLE__
73
116
postject_options options;
74
117
postject_options_init (&options);
75
118
options.macho_segment_name = " NODE_SEA" ;
76
- const char * code = static_cast <const char *>(
119
+ const char * blob = static_cast <const char *>(
77
120
postject_find_resource (" NODE_SEA_BLOB" , &size, &options));
78
121
#else
79
- const char * code = static_cast <const char *>(
122
+ const char * blob = static_cast <const char *>(
80
123
postject_find_resource (" NODE_SEA_BLOB" , &size, nullptr ));
81
124
#endif
82
- uint32_t first_word = reinterpret_cast <const uint32_t *>(code)[0 ];
83
- CHECK_EQ (first_word, kMagic );
84
- SeaFlags flags{
85
- reinterpret_cast <const SeaFlags*>(code + sizeof (first_word))[0 ]};
86
- // TODO(joyeecheung): do more checks here e.g. matching the versions.
87
- return {
88
- flags,
89
- {
90
- code + SeaResource::kHeaderSize ,
91
- size - SeaResource::kHeaderSize ,
92
- },
93
- };
125
+ return {blob, size};
94
126
}();
95
- return sea_resource;
127
+ per_process::Debug (DebugCategory::SEA,
128
+ " Found SEA blob %p, size=%zu\n " ,
129
+ result.data (),
130
+ result.size ());
131
+ return result;
96
132
}
97
133
98
- } // namespace
134
+ } // anonymous namespace
99
135
100
- std::string_view FindSingleExecutableCode () {
101
- SeaResource sea_resource = FindSingleExecutableResource ();
102
- return sea_resource.code ;
136
+ SeaResource FindSingleExecutableResource () {
137
+ static const SeaResource sea_resource = []() -> SeaResource {
138
+ std::string_view blob = FindSingleExecutableBlob ();
139
+ per_process::Debug (DebugCategory::SEA,
140
+ " Found SEA resource %p, size=%zu\n " ,
141
+ blob.data (),
142
+ blob.size ());
143
+ SeaDeserializer deserializer (blob);
144
+ return deserializer.Read <SeaResource>();
145
+ }();
146
+ return sea_resource;
103
147
}
104
148
105
149
bool IsSingleExecutable () {
@@ -194,51 +238,46 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
194
238
return result;
195
239
}
196
240
197
- bool GenerateSingleExecutableBlob (const SeaConfig& config) {
241
+ ExitCode GenerateSingleExecutableBlob (const SeaConfig& config) {
198
242
std::string main_script;
199
243
// TODO(joyeecheung): unify the file utils.
200
244
int r = ReadFileSync (&main_script, config.main_path .c_str ());
201
245
if (r != 0 ) {
202
246
const char * err = uv_strerror (r);
203
247
FPrintF (stderr, " Cannot read main script %s:%s\n " , config.main_path , err);
204
- return false ;
248
+ return ExitCode:: kGenericUserError ;
205
249
}
206
250
207
- std::vector<char > sink;
208
- // TODO(joyeecheung): reuse the SnapshotSerializerDeserializer for this.
209
- sink.reserve (SeaResource::kHeaderSize + main_script.size ());
210
- const char * pos = reinterpret_cast <const char *>(&kMagic );
211
- sink.insert (sink.end (), pos, pos + sizeof (kMagic ));
212
- pos = reinterpret_cast <const char *>(&(config.flags ));
213
- sink.insert (sink.end (), pos, pos + sizeof (SeaFlags));
214
- sink.insert (
215
- sink.end (), main_script.data (), main_script.data () + main_script.size ());
216
-
217
- uv_buf_t buf = uv_buf_init (sink.data (), sink.size ());
251
+ SeaResource sea{config.flags , main_script};
252
+
253
+ SeaSerializer serializer;
254
+ serializer.Write (sea);
255
+
256
+ uv_buf_t buf = uv_buf_init (serializer.sink .data (), serializer.sink .size ());
218
257
r = WriteFileSync (config.output_path .c_str (), buf);
219
258
if (r != 0 ) {
220
259
const char * err = uv_strerror (r);
221
260
FPrintF (stderr, " Cannot write output to %s:%s\n " , config.output_path , err);
222
- return false ;
261
+ return ExitCode:: kGenericUserError ;
223
262
}
224
263
225
264
FPrintF (stderr,
226
265
" Wrote single executable preparation blob to %s\n " ,
227
266
config.output_path );
228
- return true ;
267
+ return ExitCode:: kNoFailure ;
229
268
}
230
269
231
270
} // anonymous namespace
232
271
233
272
ExitCode BuildSingleExecutableBlob (const std::string& config_path) {
234
273
std::optional<SeaConfig> config_opt =
235
274
ParseSingleExecutableConfig (config_path);
236
- if (! config_opt.has_value () ||
237
- ! GenerateSingleExecutableBlob (config_opt.value ())) {
238
- return ExitCode:: kGenericUserError ;
275
+ if (config_opt.has_value ()) {
276
+ ExitCode code = GenerateSingleExecutableBlob (config_opt.value ());
277
+ return code ;
239
278
}
240
279
241
- return ExitCode::kNoFailure ;
280
+ return ExitCode::kGenericUserError ;
242
281
}
243
282
244
283
void Initialize (Local<Object> target,
0 commit comments