Handle serialization endianness transparently (#152)

Change JS_WriteObject() and JS_WriteObject2() to write little-endian
data and update JS_ReadObject() to byte-swap data when running on a
big-endian system.

Obsoletes the JS_WRITE_OBJ_BSWAP flag, it is now a no-op.

Fixes: https://github.com/quickjs-ng/quickjs/issues/125
This commit is contained in:
Ben Noordhuis 2023-11-28 22:49:01 +01:00 committed by GitHub
parent 0ecb2c86b5
commit a6e73ca73c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 18 additions and 36 deletions

View file

@ -167,9 +167,6 @@ find the name of the dynamically loaded modules.
Add initialization code for an external C module. See the Add initialization code for an external C module. See the
@code{c_module} example. @code{c_module} example.
@item -x
Byte swapped output (only used for cross compilation).
@item -flto @item -flto
Use link time optimization. The compilation is slower but the Use link time optimization. The compilation is slower but the
executable is smaller and faster. This option is automatically set executable is smaller and faster. This option is automatically set

13
qjsc.c
View file

@ -51,7 +51,6 @@ static namelist_t cname_list;
static namelist_t cmodule_list; static namelist_t cmodule_list;
static namelist_t init_module_list; static namelist_t init_module_list;
static FILE *outfile; static FILE *outfile;
static BOOL byte_swap;
static const char *c_ident_prefix = "qjsc_"; static const char *c_ident_prefix = "qjsc_";
@ -152,11 +151,8 @@ static void output_object_code(JSContext *ctx,
{ {
uint8_t *out_buf; uint8_t *out_buf;
size_t out_buf_len; size_t out_buf_len;
int flags;
flags = JS_WRITE_OBJ_BYTECODE; out_buf = JS_WriteObject(ctx, &out_buf_len, obj, JS_WRITE_OBJ_BYTECODE);
if (byte_swap)
flags |= JS_WRITE_OBJ_BSWAP;
out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags);
if (!out_buf) { if (!out_buf) {
js_std_dump_error(ctx); js_std_dump_error(ctx);
exit(1); exit(1);
@ -322,7 +318,6 @@ void help(void)
"-m compile as Javascript module (default=autodetect)\n" "-m compile as Javascript module (default=autodetect)\n"
"-D module_name compile a dynamically loaded module or worker\n" "-D module_name compile a dynamically loaded module or worker\n"
"-M module_name[,cname] add initialization code for an external C module\n" "-M module_name[,cname] add initialization code for an external C module\n"
"-x byte swapped output\n"
"-p prefix set the prefix of the generated C names\n" "-p prefix set the prefix of the generated C names\n"
"-S n set the maximum stack size to 'n' bytes (default=%d)\n", "-S n set the maximum stack size to 'n' bytes (default=%d)\n",
JS_GetVersion(), JS_GetVersion(),
@ -352,7 +347,6 @@ int main(int argc, char **argv)
output_type = OUTPUT_C; output_type = OUTPUT_C;
cname = NULL; cname = NULL;
module = -1; module = -1;
byte_swap = FALSE;
verbose = 0; verbose = 0;
stack_size = 0; stack_size = 0;
memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); memset(&dynamic_module_list, 0, sizeof(dynamic_module_list));
@ -399,9 +393,6 @@ int main(int argc, char **argv)
case 'D': case 'D':
namelist_add(&dynamic_module_list, optarg, NULL, 0); namelist_add(&dynamic_module_list, optarg, NULL, 0);
break; break;
case 'x':
byte_swap = TRUE;
break;
case 'v': case 'v':
verbose++; verbose++;
break; break;

View file

@ -31664,18 +31664,11 @@ typedef enum BCTagEnum {
BC_TAG_OBJECT_REFERENCE, BC_TAG_OBJECT_REFERENCE,
} BCTagEnum; } BCTagEnum;
#define BC_BASE_VERSION 2 #define BC_VERSION 3
#define BC_BE_VERSION 0x40
#ifdef WORDS_BIGENDIAN
#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)
#else
#define BC_VERSION BC_BASE_VERSION
#endif
typedef struct BCWriterState { typedef struct BCWriterState {
JSContext *ctx; JSContext *ctx;
DynBuf dbuf; DynBuf dbuf;
BOOL byte_swap : 8;
BOOL allow_bytecode : 8; BOOL allow_bytecode : 8;
BOOL allow_sab : 8; BOOL allow_sab : 8;
BOOL allow_reference : 8; BOOL allow_reference : 8;
@ -31717,6 +31710,15 @@ static const char * const bc_tag_str[] = {
}; };
#endif #endif
static inline BOOL is_be(void)
{
union {
uint16_t a;
uint8_t b;
} u = {0x100};
return u.b;
}
static void bc_put_u8(BCWriterState *s, uint8_t v) static void bc_put_u8(BCWriterState *s, uint8_t v)
{ {
dbuf_putc(&s->dbuf, v); dbuf_putc(&s->dbuf, v);
@ -31724,21 +31726,21 @@ static void bc_put_u8(BCWriterState *s, uint8_t v)
static void bc_put_u16(BCWriterState *s, uint16_t v) static void bc_put_u16(BCWriterState *s, uint16_t v)
{ {
if (s->byte_swap) if (is_be())
v = bswap16(v); v = bswap16(v);
dbuf_put_u16(&s->dbuf, v); dbuf_put_u16(&s->dbuf, v);
} }
static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
{ {
if (s->byte_swap) if (is_be())
v = bswap32(v); v = bswap32(v);
dbuf_put_u32(&s->dbuf, v); dbuf_put_u32(&s->dbuf, v);
} }
static void bc_put_u64(BCWriterState *s, uint64_t v) static void bc_put_u64(BCWriterState *s, uint64_t v)
{ {
if (s->byte_swap) if (is_be())
v = bswap64(v); v = bswap64(v);
dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v)); dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
} }
@ -31908,7 +31910,7 @@ static int JS_WriteFunctionBytecode(BCWriterState *s,
pos += len; pos += len;
} }
if (s->byte_swap) if (is_be())
bc_byte_swap(bc_buf, bc_len); bc_byte_swap(bc_buf, bc_len);
dbuf_put(&s->dbuf, bc_buf, bc_len); dbuf_put(&s->dbuf, bc_buf, bc_len);
@ -32405,15 +32407,10 @@ static int JS_WriteObjectAtoms(BCWriterState *s)
JSRuntime *rt = s->ctx->rt; JSRuntime *rt = s->ctx->rt;
DynBuf dbuf1; DynBuf dbuf1;
int i, atoms_size; int i, atoms_size;
uint8_t version;
dbuf1 = s->dbuf; dbuf1 = s->dbuf;
js_dbuf_init(s->ctx, &s->dbuf); js_dbuf_init(s->ctx, &s->dbuf);
bc_put_u8(s, BC_VERSION);
version = BC_VERSION;
if (s->byte_swap)
version ^= BC_BE_VERSION;
bc_put_u8(s, version);
bc_put_leb128(s, s->idx_to_atom_count); bc_put_leb128(s, s->idx_to_atom_count);
for(i = 0; i < s->idx_to_atom_count; i++) { for(i = 0; i < s->idx_to_atom_count; i++) {
@ -32446,8 +32443,6 @@ uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
memset(s, 0, sizeof(*s)); memset(s, 0, sizeof(*s));
s->ctx = ctx; s->ctx = ctx;
/* XXX: byte swapped output is untested */
s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);
s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
@ -33531,7 +33526,6 @@ static int JS_ReadObjectAtoms(BCReaderState *s)
if (bc_get_u8(s, &v8)) if (bc_get_u8(s, &v8))
return -1; return -1;
/* XXX: could support byte swapped input */
if (v8 != BC_VERSION) { if (v8 != BC_VERSION) {
JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
v8, BC_VERSION); v8, BC_VERSION);

View file

@ -838,7 +838,7 @@ JS_EXTERN int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx);
/* Object Writer/Reader (currently only used to handle precompiled code) */ /* Object Writer/Reader (currently only used to handle precompiled code) */
#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ #define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ #define JS_WRITE_OBJ_BSWAP (0) /* byte swapped output (obsolete, handled transparently) */
#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ #define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to #define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to
encode arbitrary object encode arbitrary object