From 82b77505f9420f11d614c2ae0f74153ca4ee3cb5 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Tue, 2 May 2023 09:48:50 +0200 Subject: cooker updates spilling all over the place Only tangentially related: - make seitan C99 again, so that I can build cooker without warnings - make Makefiles make use of the usual conventions about assigning directory paths in variables, drop numbers.h as requirement for cooker and make it convenient to run stand-alone Makefiles - fix up nr_syscalls.sh to be POSIX, otherwise it will give syntax errors on my system - define a single, common way to refer to offsets in gluten, and functions to use those offsets in a safe way. Immediates are gone: cooker will write any bit of "data" to the read-only section - call const what has to be const - define on-disk layout for gluten - add OP_NR (to check syscall numbers), rename OP_COPY_ARGS to OP_LOAD (it loads _selected_ stuff from arguments) As for cooker itself: - drop ARG_ and arg_ prefixes from struct names, and similar - add/rework functions to build OP_NR, OP_LOAD, OP_CMP, and to write constant data to gluten - add parsing for "compound" arguments, but that's not completely hooked into evaluation for numeric arguments yet Signed-off-by: Stefano Brivio --- cooker/Makefile | 36 ++++++++----------- cooker/calls/net.c | 91 +++++++++++++++++++++++++++--------------------- cooker/cooker.h | 97 ++++++++++++++++++++++++++++++--------------------- cooker/emit.c | 94 +++++++++++++++++++++++++++++++++++++++++++++---- cooker/emit.h | 14 ++++++-- cooker/gluten.c | 44 +++++++++++++++-------- cooker/gluten.h | 28 +++++++++------ cooker/parse.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++------- 8 files changed, 356 insertions(+), 148 deletions(-) (limited to 'cooker') diff --git a/cooker/Makefile b/cooker/Makefile index 4f72448..be8f703 100644 --- a/cooker/Makefile +++ b/cooker/Makefile @@ -5,31 +5,25 @@ # cooker/Makefile - Makefile for seitan-cooker # # Copyright 2023 Red Hat GmbH -# Author: Stefano Brivio +# Authors: Alice Frosi +# Stefano Brivio -COMMON_DIR := ../common -SRCS := calls.c emit.c filter.c gluten.c main.c parse.c parson.c \ - $(COMMON_DIR)/util.c calls/net.c -HEADERS := calls.h cooker.h emit.h filter.h \ - gluten.h parse.h parson.h calls/net.h \ - $(COMMON_DIR)/util.h -BIN := $(OUTDIR)seitan-cooker +OUTDIR ?= .. +COMMON := ../common +BIN := $(OUTDIR)/seitan-cooker +CFLAGS := -O0 -g -Wall -Wextra -pedantic -std=c99 -I$(COMMON) -TARGET := $(shell $(CC) -dumpmachine) -TARGET_ARCH := $(shell echo $(TARGET) | cut -f1 -d- | tr [A-Z] [a-z]) -TARGET_ARCH := $(shell echo $(TARGET_ARCH) | sed 's/powerpc/ppc/') +SRCS := calls.c emit.c gluten.c main.c parse.c parson.c \ + $(COMMON)/util.c \ + calls/net.c +HEADERS := calls.h cooker.h emit.h gluten.h parse.h parson.h \ + $(COMMON)/gluten.h $(COMMON)/util.h \ + calls/net.h -AUDIT_ARCH := $(shell echo $(TARGET_ARCH) | tr [a-z] [A-Z] | sed 's/^ARM.*/ARM/') -AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/I[456]86/I386/') -AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/PPC64/PPC/') -AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/PPCLE/PPC64LE/') - -CFLAGS += -DSEITAN_AUDIT_ARCH=AUDIT_ARCH_$(AUDIT_ARCH) -CFLAGS += -O0 -g -Wall -Wextra -pedantic -std=c99 -I$(COMMON_DIR) -cooker: $(SRCS) $(HEADERS) +$(BIN): $(SRCS) $(HEADERS) $(CC) $(CFLAGS) -o $(BIN) $(SRCS) -all: cooker +all: $(BIN) clean: - rm -f cooker + rm -f $(BIN) diff --git a/cooker/calls/net.c b/cooker/calls/net.c index c0949cc..370a3a1 100644 --- a/cooker/calls/net.c +++ b/cooker/calls/net.c @@ -39,7 +39,7 @@ n = sendmmsg(fd, *msgvec, vlen, flags) #include "../cooker.h" #include "../calls.h" -static struct arg_num af[] = { +static struct num af[] = { { "unix", AF_UNIX }, { "ipv4", AF_INET }, { "ipv6", AF_INET6 }, @@ -49,7 +49,7 @@ static struct arg_num af[] = { { 0 }, }; -static struct arg_num socket_types[] = { +static struct num socket_types[] = { { "stream", SOCK_STREAM }, { "dgram", SOCK_DGRAM }, { "seq", SOCK_SEQPACKET }, @@ -58,13 +58,13 @@ static struct arg_num socket_types[] = { { 0 }, }; -static struct arg_num socket_flags[] = { +static struct num socket_flags[] = { { "nonblock", SOCK_NONBLOCK }, { "cloexec", SOCK_CLOEXEC }, { 0 }, }; -static struct arg_num protocols[] = { +static struct num protocols[] = { { "ip", IPPROTO_IP }, { "icmp", IPPROTO_ICMP }, { "igmp", IPPROTO_IGMP }, @@ -83,86 +83,97 @@ static struct arg_num protocols[] = { }; static struct arg socket_args[] = { - { 0, "family", ARG_INT, 0, { .d_num = af } }, - { 1, "type", ARG_INTMASK, 0, { .d_num = socket_types } }, - { 1, "flags", ARG_INTFLAGS, 0, { .d_num = socket_flags } }, - { 2, "protocol", ARG_INT, 0, { .d_num = protocols } }, + { 0, "family", INT, 0, { .d_num = af } }, + { 1, "type", INTMASK, 0, { .d_num = socket_types } }, + { 1, "flags", INTFLAGS, 0, { .d_num = socket_flags } }, + { 2, "protocol", INT, 0, { .d_num = protocols } }, { 0 }, }; -static struct arg_struct connect_addr_unix[] = { - { "path", ARG_STRING, +static struct field connect_addr_unix[] = { + { + "path", STRING, offsetof(struct sockaddr_un, sun_path), - UNIX_PATH_MAX, { 0 } + UNIX_PATH_MAX, { 0 } }, { 0 }, }; -static struct arg_struct connect_addr_ipv4[] = { - { "port", ARG_PORT, +static struct field connect_addr_ipv4[] = { + { + "port", PORT, offsetof(struct sockaddr_in, sin_port), - 0, { 0 } + 0, { 0 } }, - { "addr", ARG_IPV4, + { + "addr", IPV4, offsetof(struct sockaddr_in, sin_addr), - 0, { 0 } + 0, { 0 } }, { 0 }, }; -static struct arg_struct connect_addr_ipv6[] = { - { "port", ARG_PORT, +static struct field connect_addr_ipv6[] = { + { + "port", PORT, offsetof(struct sockaddr_in6, sin6_port), - 0, { 0 } + 0, { 0 } }, - { "addr", ARG_IPV6, + { + "addr", IPV6, offsetof(struct sockaddr_in6, sin6_addr), - 0, { 0 } + 0, { 0 } }, { 0 }, }; -static struct arg_struct connect_addr_nl[] = { - { "pid", ARG_PID, +static struct field connect_addr_nl[] = { + { + "pid", PID, offsetof(struct sockaddr_nl, nl_pid), - 0, { 0 } + 0, { 0 } }, - { "groups", ARG_U32, + { + "groups", U32, offsetof(struct sockaddr_in6, sin6_addr), - 0, { 0 } + 0, { 0 } }, { 0 }, }; -static struct arg_struct connect_family = { - "family", ARG_INT, - offsetof(struct sockaddr, sa_family), - 0, { .d_num = af } +static struct field connect_family = { + "family", INT, + offsetof(struct sockaddr, sa_family), + 0, { .d_num = af } }; -static struct arg_select_num connect_addr_select_family[] = { - { AF_UNIX, ARG_STRUCT, { .d_struct = connect_addr_unix } }, - { AF_INET, ARG_STRUCT, { .d_struct = connect_addr_ipv4 } }, - { AF_INET6, ARG_STRUCT, { .d_struct = connect_addr_ipv6 } }, - { AF_NETLINK, ARG_STRUCT, { .d_struct = connect_addr_nl } }, +static struct select_num connect_addr_select_family[] = { + { AF_UNIX, STRUCT, { .d_struct = connect_addr_unix } }, + { AF_INET, STRUCT, { .d_struct = connect_addr_ipv4 } }, + { AF_INET6, STRUCT, { .d_struct = connect_addr_ipv6 } }, + { AF_NETLINK, STRUCT, { .d_struct = connect_addr_nl } }, { 0 }, }; -static struct arg_select connect_addr_select = { +static struct select connect_addr_select = { &connect_family, { .d_num = connect_addr_select_family } }; static struct arg connect_args[] = { - { 0, "fd", ARG_INT, 0, + { + 0, "fd", INT, 0, { 0 }, }, - { 0, "path", ARG_FDPATH, 0, + { + 0, "path", FDPATH, 0, { 0 }, }, - { 1, "addr", ARG_SELECT, sizeof(struct sockaddr_storage), + { + 1, "addr", SELECT, sizeof(struct sockaddr_storage), { .d_select = &connect_addr_select }, }, - { 2, "addrlen", ARG_LONG, 0, + { + 2, "addrlen", LONG, 0, { 0 }, }, }; diff --git a/cooker/cooker.h b/cooker/cooker.h index 53aa0db..a1cc360 100644 --- a/cooker/cooker.h +++ b/cooker/cooker.h @@ -11,78 +11,95 @@ #include #include #include +#include +#include #define REFS_MAX 256 +#define REF_NAMEMAX 256 #define CALL_ARGS 6 -struct arg_num; -struct arg_struct; -struct arg_select; +struct num; +struct field; +struct select; -union arg_value { - struct arg_num *d_num; - struct arg_struct *d_struct; - struct arg_select *d_select; +union desc { + struct num *d_num; + struct field *d_struct; + struct select *d_select; }; -enum arg_type { - ARG_INT, - ARG_INTMASK, - ARG_INTFLAGS, +union value { + int v_int; + uint32_t v_u32; + long long v_num; +}; + +enum type { + INT, + INTMASK, + INTFLAGS, - ARG_U32, - ARG_U32MASK, - ARG_U32FLAGS, + U32, + U32MASK, + U32FLAGS, - ARG_LONG, - ARG_LONGMASK, - ARG_LONGFLAGS, + LONG, + LONGMASK, + LONGFLAGS, - ARG_STRING, + STRING, - ARG_STRUCT, - ARG_SELECT, + STRUCT, + SELECT, - ARG_PID, + PID, - ARG_PORT, - ARG_IPV4, - ARG_IPV6, + PORT, + IPV4, + IPV6, - ARG_FDPATH, + FDPATH, - ARG_TYPE_END, + TYPE_END, }; -#define ARG_TYPE_COUNT (ARG_TYPE_END - 1) +#define TYPE_COUNT (TYPE_END - 1) + +#define TYPE_IS_COMPOUND(t) ((t) == STRUCT || (t) == SELECT) +#define TYPE_IS_NUM(t) ((t) == INT || (t) == U32 || (t) == LONG) + +enum jump_type { + NEXT_BLOCK, + END, +}; -struct arg_num { +struct num { char *name; long long value; }; -struct arg_struct { +struct field { char *name; - enum arg_type type; - size_t offset; + enum type type; + off_t offset; size_t strlen; - union arg_value desc; + union desc desc; }; -struct arg_select_num { +struct select_num { long long value; - enum arg_type type; - union arg_value desc; + enum type type; + union desc desc; }; -struct arg_select { - struct arg_struct *field; +struct select { + struct field *field; union { - struct arg_select_num *d_num; + struct select_num *d_num; } desc; }; @@ -90,10 +107,10 @@ struct arg { int pos; char *name; - enum arg_type type; + enum type type; size_t size; - union arg_value desc; + union desc desc; }; #endif /* COOKER_H */ diff --git a/cooker/emit.c b/cooker/emit.c index a82529c..8c35f1d 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -2,7 +2,7 @@ /* seitan - Syscall Expressive Interpreter, Transformer and Notifier * - * cooker/emit.c - Generate gluten (bytecode) instructions + * cooker/emit.c - Generate gluten (bytecode) instructions and read-only data * * Copyright 2023 Red Hat GmbH * Author: Stefano Brivio @@ -11,17 +11,97 @@ #include "cooker.h" #include "gluten.h" #include "util.h" +#include "emit.h" -int emit_nr(struct gluten_ctx *g, long number) +static const char *type_str[] = { + "INT", "INTMASK", "INTFLAGS", + "U32", "U32MASK", "U32FLAGS", + "LONG", "LONGMASK", "LONGFLAGS", + "STRING", + "STRUCT", "SELECT", + "PID", + "PORT", "IPV4", "IPV6", + "FDPATH", + NULL +}; + +static const char *cmp_type_str[] = { "EQ", "GT", "GE", "LT", "LE", NULL }; + +void emit_nr(struct gluten_ctx *g, long number) +{ + struct op_nr *nr = (struct op_nr *)gluten_ptr(&g->g, g->ip); + + nr->nr = number; + nr->no_match.type = OFFSET_INSTRUCTION; + nr->no_match.offset = NEXT_BLOCK; + + debug(" %i: OP_NR %li, < >", g->ip.offset, number); + + if (++g->ip.offset > INST_MAX) + die("Too many instructions"); +} + +void emit_load(struct gluten_ctx *g, struct gluten_offset dst, + int index, size_t len) +{ + struct op_load *load = (struct op_load *)gluten_ptr(&g->g, g->ip); + + load->src.type = OFFSET_SECCOMP_DATA; + load->src.offset = index; + + load->dst = dst; + + debug(" %i: OP_LOAD #%i < %i (%lu)", g->ip.offset, dst.offset, + index, len); + + if (++g->ip.offset > INST_MAX) + die("Too many instructions"); +} + +void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, + struct gluten_offset x, struct gluten_offset y, size_t size, + enum jump_type jmp) { - debug(" %i: OP_NR %li, < >", g->ip++, number); + struct op_cmp *op = (struct op_cmp *)gluten_ptr(&g->g, g->ip); + + op->x = x; + op->y = y; + op->size = size; + op->cmp = cmp; + op->jmp = jmp; + + debug(" %i: OP_CMP (#%lu) %%%lu %s %%%lu", g->ip.offset, size, + x.offset, cmp_type_str[cmp], y.offset); - return 0; + if (++g->ip.offset > INST_MAX) + die("Too many instructions"); } -int emit_load(struct gluten_ctx *g, int offset, int index, size_t len) +void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, + struct field *field, + struct gluten_offset base, struct gluten_offset match, + enum jump_type jmp) { - debug(" %i: OP_LOAD #%i < %i (%lu)", g->ip++, offset, index, len); + base.offset += field->offset; + + emit_cmp(g, cmp, base, match, + field->strlen ? field->strlen : gluten_size[field->type], + jmp); +} + +struct gluten_offset emit_data(struct gluten_ctx *g, enum type type, + union value *value) +{ + void *p = gluten_ptr(&g->g, g->cp); + struct gluten_offset ret = g->cp; + + if (type == INT) { + *(int *)p = value->v_int; + debug(" C#%i: (%s) %i", g->cp.offset, type_str[type], + value->v_int); + if ((g->cp.offset += sizeof(int)) > RO_DATA_SIZE) + die(" Read-only data section exceeded"); + } - return 0; + return ret; } diff --git a/cooker/emit.h b/cooker/emit.h index 74264b1..94b2600 100644 --- a/cooker/emit.h +++ b/cooker/emit.h @@ -6,7 +6,17 @@ #ifndef EMIT_H #define EMIT_H -int emit_nr(struct gluten_ctx *g, long number); -int emit_load(struct gluten_ctx *g, int offset, int index, size_t len); +void emit_nr(struct gluten_ctx *g, long number); +void emit_load(struct gluten_ctx *g, struct gluten_offset dst, + int index, size_t len); +void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, + struct gluten_offset x, struct gluten_offset y, size_t size, + enum jump_type jmp); +void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, + struct field *field, + struct gluten_offset base, struct gluten_offset match, + enum jump_type jmp); +struct gluten_offset emit_data(struct gluten_ctx *g, enum type type, + union value *value); #endif /* EMIT_H */ diff --git a/cooker/gluten.c b/cooker/gluten.c index 1116e6b..6460798 100644 --- a/cooker/gluten.c +++ b/cooker/gluten.c @@ -12,33 +12,47 @@ #include "gluten.h" #include "util.h" -#define GLUTEN_INST_SIZE BUFSIZ -#define GLUTEN_DATA_SIZE BUFSIZ +size_t gluten_size[TYPE_COUNT] = { + [INT] = sizeof(int), + [INTMASK] = sizeof(int), + [INTFLAGS] = sizeof(int), -static char gluten[GLUTEN_INST_SIZE + GLUTEN_DATA_SIZE]; + [U32] = sizeof(uint32_t), + [U32MASK] = sizeof(uint32_t), + [U32FLAGS] = sizeof(uint32_t), + + [LONG] = sizeof(long), + [LONGMASK] = sizeof(long), + [LONGFLAGS] = sizeof(long), + + [PID] = sizeof(pid_t), + [PORT] = sizeof(in_port_t), + [IPV4] = sizeof(struct in_addr), + [IPV6] = sizeof(struct in6_addr), -static size_t gluten_arg_storage[ARG_TYPE_COUNT] = { - [ARG_INT] = sizeof(int), - [ARG_INTMASK] = sizeof(int), }; -int gluten_alloc(struct gluten_ctx *g, size_t size) +struct gluten_offset gluten_alloc(struct gluten_ctx *g, size_t size) { - debug(" allocating %lu at offset %i", size, g->sp); - if ((g->sp += size) >= GLUTEN_DATA_SIZE) + struct gluten_offset ret = g->dp; + + debug(" allocating %lu at offset %i", size, g->dp.offset); + if ((g->dp.offset += size) >= DATA_SIZE) die("Temporary data size exceeded"); - return g->sp - size; + return ret; } -int gluten_alloc_type(struct gluten_ctx *g, enum arg_type type) +struct gluten_offset gluten_alloc_type(struct gluten_ctx *g, enum type type) { - return gluten_alloc(g, gluten_arg_storage[type]); + return gluten_alloc(g, gluten_size[type]); } -int gluten_init(struct gluten_ctx *g) +void gluten_init(struct gluten_ctx *g) { - g->gluten = gluten; + (void)g; - return 0; + g->ip.type = g->lr.type = OFFSET_INSTRUCTION; + g->dp.type = OFFSET_DATA; + g->cp.type = OFFSET_RO_DATA; } diff --git a/cooker/gluten.h b/cooker/gluten.h index 440029d..a48cd6d 100644 --- a/cooker/gluten.h +++ b/cooker/gluten.h @@ -6,22 +6,27 @@ #ifndef GLUTEN_H #define GLUTEN_H +#define COOKER +#include + struct gluten_arg_data { - int offset; + struct gluten_offset offset; size_t len; }; struct gluten_ref_data { - int name; - int offset; + char name[REF_NAMEMAX]; + struct gluten_offset offset; size_t len; }; struct gluten_ctx { - int ip; - int lr; - int sp; - char *gluten; + struct gluten_offset ip; + struct gluten_offset lr; + struct gluten_offset cp; + struct gluten_offset dp; + + struct gluten g; struct gluten_arg_data match_dst[CALL_ARGS]; struct gluten_arg_data call_src[CALL_ARGS]; @@ -29,8 +34,11 @@ struct gluten_ctx { struct gluten_ref_data refs[REFS_MAX]; }; -int gluten_alloc(struct gluten_ctx *g, size_t size); -int gluten_alloc_type(struct gluten_ctx *g, enum arg_type type); -int gluten_init(struct gluten_ctx *g); +struct gluten_offset gluten_alloc(struct gluten_ctx *g, size_t size); +struct gluten_offset gluten_alloc_type(struct gluten_ctx *g, enum type type); +void gluten_init(struct gluten_ctx *g); +void gluten_block_init(struct gluten_ctx *g); + +extern size_t gluten_size[TYPE_COUNT]; #endif /* GLUTEN_H */ diff --git a/cooker/parse.c b/cooker/parse.c index 9d8a7be..0a87088 100644 --- a/cooker/parse.c +++ b/cooker/parse.c @@ -35,7 +35,7 @@ static int parse_match_load(struct gluten_ctx *g, struct arg *a) return 0; } -static long long parse_match_expr_num(struct arg_num *desc, JSON_Value *value) +static long long parse_match_expr_num(struct num *desc, JSON_Value *value) { const char *s = NULL; long long n; @@ -59,35 +59,109 @@ static long long parse_match_expr_num(struct arg_num *desc, JSON_Value *value) return n; } -static int parse_match_key(struct gluten_ctx *g, int index, enum arg_type type, - union arg_value desc, JSON_Value *value) +static void parse_match_expr_value(union desc desc, enum type type, + JSON_Value *value, union value *out) +{ + if (TYPE_IS_NUM(type)) + out->v_num = parse_match_expr_num(desc.d_num, value); +} + +/** + * parse_match_select() - Get description and type for selected value + * @s: Possible selection choices + * @v: Selector value + * @type: Type of selected value, set on return + * @desc: Description of selected value, set on return + */ +static void parse_match_select(struct select *s, union value v, + enum type *type, union desc *desc) +{ + if (TYPE_IS_NUM(s->field->type)) { + struct select_num *d_num; + + for (d_num = s->desc.d_num; d_num->type; d_num++) { + if (d_num->value == v.v_num) { + *type = d_num->type; + *desc = d_num->desc; + return; + } + } + + if (!d_num->type) + die(" No match for numeric selector %i", v.v_num); + } + + die(" not supported yet"); +} + +/* + * parse_match_arg() + * load argument + * parse_match_key() + * compound types? demux, parse_match_expr + * parse_match_expr + * in/all/not/false-true array: demux, parse_match_expr_{num,string} + * parse_match_expr_{num,string} + * + * at terminal values + * store ref *pointers*! no copies + * emit additional bpf statement if syscall arg is also terminal +*/ +static int parse_match_key(struct gluten_ctx *g, int index, enum type type, + union desc desc, JSON_Value *value) { JSON_Object *tmp; const char *ref; - (void)index; + if (type == SELECT) { + struct gluten_offset base_offset, const_offset; + struct select *select = desc.d_select; + struct field *field = select->field; + JSON_Value *selector; + union value v; + + if (!(tmp = json_value_get_object(value))) + die(" no object for compound value"); + + if (!(selector = json_object_get_value(tmp, field->name))) + die(" missing selector for '%s'", field->name); + + parse_match_expr_value(field->desc, field->type, selector, &v); + + base_offset = g->match_dst[index].offset; + const_offset = emit_data(g, field->type, &v); + emit_cmp_field(g, CMP_NE, field, base_offset, const_offset, + NEXT_BLOCK); + + parse_match_select(select, v, &type, &desc); + } if (json_value_get_type(value) == JSONObject && (tmp = json_value_get_object(value)) && (ref = json_object_get_string(tmp, "ref"))) { + if (TYPE_IS_COMPOUND(type)) + die("Reference '%s' to compound value"); + debug(" setting reference '%s'", ref); - gluten_alloc_type(g, type); value = json_object_get_value(tmp, "value"); + + emit_load(g, gluten_alloc_type(g, type), index, type); } + /* Nothing to match on: just store as reference */ if (!value) return 0; switch (type) { - case ARG_INTFLAGS: - case ARG_LONGFLAGS: - case ARG_U32FLAGS: + case INTFLAGS: + case LONGFLAGS: + case U32FLAGS: /* fetch/combine expr algebra loop */ - case ARG_INTMASK: + case INTMASK: /* calculate mask first */ - case ARG_INT: - case ARG_LONG: - case ARG_U32: + case INT: + case LONG: + case U32: parse_match_expr_num(desc.d_num, value); //emit_cmp(...); default: @@ -139,7 +213,6 @@ static int parse_matches(struct gluten_ctx *g, JSON_Value *value) const char *name; g->lr = g->ip; - g->sp = 0; match = json_array_get_object(matches, i); name = json_object_get_name(match, 0); @@ -229,6 +302,7 @@ int parse_file(struct gluten_ctx *g, const char *path) blocks = json_value_get_array(root); for (i = 0; i < json_array_get_count(blocks); i++) { obj = json_array_get_object(blocks, i); + debug("Parsing block %i", i); parse_block(g, obj); } -- cgit v1.2.3