From 1644bbec6161ec971a2ba3c213ce285b995cac22 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Thu, 1 Jun 2023 11:04:38 +0200 Subject: cooker: OP_CALL and OP_COPY stuff ...mostly. Signed-off-by: Stefano Brivio --- cooker/call.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 cooker/call.c (limited to 'cooker/call.c') diff --git a/cooker/call.c b/cooker/call.c new file mode 100644 index 0000000..e1abb39 --- /dev/null +++ b/cooker/call.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +/* seitan - Syscall Expressive Interpreter, Transformer and Notifier + * + * cooker/call.c - Parse "call" rules from JSON recipe into bytecode + * + * Copyright 2023 Red Hat GmbH + * Author: Stefano Brivio + */ + +#include "parson.h" +#include "calls.h" +#include "cooker.h" +#include "gluten.h" +#include "emit.h" +#include "parse.h" +#include "util.h" + +/** + +struct syscall_desc { + unsigned nr:9; + + unsigned arg_count:3; + unsigned has_ret:1; + + unsigned arg_deref:6; + + struct gluten_offset data[]; +}; + +match: "tag": "x" -- meaning "set" +call: "tag": { "set": "x", "get": "y" } + +Examples of arguments: +- INT: 2 write 2 in ro_data, and pointer to it + parse_arg() passes null offset + parse_field() passes back ro_data offset + +- INT *: 2 write 2 in ro_data (using size), and pointer to it + no flags or tags parse_arg() passes ro_data offset + +- INT *: 2 - write 2 in ro_data at x + COPY_ON_CALL - emit op_copy from x to y + or "set" tag - write pointer to y + parse_arg() passes data offset + +- INT: "get" write pointer to tag + parse_arg() passes null offset + parse_field() gives back ro_data or data offset + +- INT *: "get" write pointer to tag + no COPY_ON_CALL parse_arg() passes null offset + parse_field() gives back data offset + +- STRING: abcd write abcd to ro_data (using size), and pointer to it + parse_arg() passes ro_data offset + +- STRUCT: 1, 2 write struct to ro_data, and pointer to it + parse_tag() passes ro_data offset + +- STRUCT: "get" write pointer to tag + parse_arg() passes null offset + parse_field() gives back data offset + +- STRUCT: , 2 - write 2 to ro_data at x + - allocate rw_data at y + - emit op_copy from tag to y + - emit op_copy from x to y + offset of "2" + - write pointer to y + parse_arg() passes data offset + */ + +static union value parse_field(struct gluten_ctx *g, + struct gluten_offset *base_offset, + int index, struct field *f, JSON_Value *jvalue, + bool dry_run, bool add) +{ + struct gluten_offset offset = *base_offset; + union value v = { .v_num = 0 }; + JSON_Object *tmp1, *tmp2; + struct field *f_inner; + JSON_Value *sel; + + if (f->name) + debug(" parsing field name %s", f->name); + + if (offset.type != OFFSET_NULL) + offset.offset += f->offset; + + if (json_value_get_type(jvalue) == JSONObject && + (tmp1 = json_value_get_object(jvalue)) && + (tmp2 = json_object_get_object(tmp1, "tag"))) { + const char *tag_set, *tag_get; + size_t count = 0; + + if ((tag_set = json_object_get_string(tmp2, "set"))) { + count++; + debug(" setting tag reference (post) '%s'", tag_set); + + if (!dry_run) + gluten_add_tag_post(g, tag_set, offset); + } + + if ((tag_get = json_object_get_string(tmp2, "get"))) { + struct gluten_offset tag_offset; + + count++; + debug(" getting tag reference '%s'", tag_get); + + /* TODO: Check type */ + tag_offset = gluten_get_tag(g, tag_get); + if (tag_offset.type == OFFSET_NULL) + die(" tag not found"); + + if (base_offset->type == OFFSET_NULL) + *base_offset = tag_offset; + else + emit_copy_field(g, f, offset, tag_offset); + } + + if (json_object_get_count(tmp2) > count) + die("stray object in tag reference"); + + if (!count) + die("invalid tag specification"); + + jvalue = json_object_get_value(tmp1, "value"); + + if (tag_get) { + if (jvalue) + die("stray value with \"get\" tag"); + + return v; + } + } + + if (!jvalue) + return v; + + switch (f->type) { + case INT: + case LONG: + case U32: + if (f->flags == FLAGS) { + /* fetch/combine expr algebra loop */ + ; + } + if (f->flags == MASK) { + /* calculate mask first */ + ; + } + + v.v_num = value_get_num(f->desc.d_num, jvalue); + if (dry_run) + break; + + if (base_offset->type == OFFSET_NULL) + *base_offset = emit_data(g, f->type, 0, &v); + else if (add) + emit_data_or(g, offset, f->type, &v); + else + emit_data_at(g, offset, f->type, &v); + + break; + case SELECT: + f_inner = f->desc.d_select->field; + + if ((tmp1 = json_value_get_object(jvalue))) { + if (!(sel = json_object_get_value(tmp1, f_inner->name))) + die(" no selector for '%s'", f_inner->name); + } else { + sel = jvalue; + } + + v = parse_field(g, &offset, index, f_inner, sel, false, false); + + f = select_field(g, index, f->desc.d_select, v); + if (f) + parse_field(g, &offset, index, f, jvalue, false, add); + break; + case STRING: + v.v_str = json_value_get_string(jvalue); + if (strlen(v.v_str) + 1 > f->size) + die(" string %s too long for field", v.v_str); + + if (dry_run) + break; + + emit_data_at(g, offset, f->type, &v); + break; + case STRUCT: + for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) { + JSON_Value *f_value; + + tmp1 = json_value_get_object(jvalue); + f_value = json_object_get_value(tmp1, f_inner->name); + if (!f_value) + continue; + + parse_field(g, &offset, index, f_inner, f_value, false, + add); + } + break; + default: + ; + } + + return v; +} + +bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, + bool *top_level_tag, int level) +{ + struct gluten_offset unused = { .type = OFFSET_NULL, .offset = 0 }; + union value v = { .v_num = 0 }; + struct field *f_inner; + JSON_Object *tmp; + JSON_Value *sel; + + if (f->flags & COPY_ON_CALL) + return true; + + if (f->flags & SIZE) + return false; + + if (json_value_get_type(jvalue) == JSONObject && + (tmp = json_value_get_object(jvalue)) && + (tmp = json_object_get_object(tmp, "tag"))) { + if (json_object_get_string(tmp, "set")) + return true; + + if (level) + return true; + + if (top_level_tag) + *top_level_tag = true; + + return false; + } + + switch (f->type) { + case INT: + case LONG: + case U32: + return false; + case SELECT: + f_inner = f->desc.d_select->field; + if (arg_needs_temp(f_inner, pos, jvalue, top_level_tag, level)) + return true; + + if ((tmp = json_value_get_object(jvalue))) { + if (!(sel = json_object_get_value(tmp, f_inner->name))) + die(" no selector for '%s'", f_inner->name); + } else { + sel = jvalue; + } + + v = parse_field(NULL, &unused, pos, f_inner, sel, true, false); + + f = select_field(NULL, pos, f->desc.d_select, v); + if (f) + return arg_needs_temp(f, pos, jvalue, NULL, level + 1); + + return false; + case STRING: + return false; + case STRUCT: + for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) { + JSON_Value *f_value; + + tmp = json_value_get_object(jvalue); + f_value = json_object_get_value(tmp, f_inner->name); + if (!f_value) + continue; + + if (arg_needs_temp(f, pos, f_value, NULL, level + 1)) + return true; + } + + return false; + default: + ; + } + + return false; +} + +static struct gluten_offset parse_arg(struct gluten_ctx *g, struct arg *a, + struct gluten_offset offset, + JSON_Value *jvalue) +{ + bool top_level_tag = false; + + debug(" Parsing call argument %s", a->f.name); + + if (offset.type != OFFSET_NULL) { + parse_field(g, &offset, a->pos, &a->f, jvalue, false, true); + return offset; + } + + if (arg_needs_temp(&a->f, a->pos, jvalue, &top_level_tag, 0)) + offset = gluten_rw_alloc(g, a->f.size); + else if (a->f.size && !top_level_tag) + offset = gluten_ro_alloc(g, a->f.size); + + parse_field(g, &offset, a->pos, &a->f, jvalue, false, false); + + return offset; +} + +static void parse_call(struct gluten_ctx *g, JSON_Object *obj, const char *ret, + struct arg *args) +{ + struct gluten_offset offset[6] = { 0 }, ret_offset = { 0 }; + /* Minimum requirements for argument specification: + * - if argument can be FDPATH, exactly one value for that position + * - if argument is a size field, value is optional + * - otherwise, every argument needs to be specified + */ + struct { + bool needs_fd; + bool has_fd; + } arg_check[6] = { 0 }; + unsigned count = 0; + struct arg *a; + + /* Set requirements first */ + for (a = args; a->f.name; a++) { + if (a->f.type == SELECTED) { + if (!g->selected_arg[a->pos]) + die(" No argument selected for %s", a->f.name); + a = g->selected_arg[a->pos]; + } + + if (a->f.type == FDPATH) + arg_check[a->pos].needs_fd = true; + } + + /* TODO: Factor this out into a function in... parse.c? */ + for (a = args; a->f.name; a++) { + JSON_Value *jvalue; + + if (a->f.type == SELECTED) + a = g->selected_arg[a->pos]; + + /* Not common with parse_match(), though */ + if ((jvalue = json_object_get_value(obj, a->f.name))) { + if (arg_check[a->pos].has_fd) + die(" Conflicting value for %s", a->f.name); + else if (arg_check[a->pos].needs_fd) + arg_check[a->pos].has_fd = true; + + offset[a->pos] = parse_arg(g, a, offset[a->pos], + jvalue); + count++; + } else if (arg_check[a->pos].needs_fd && + arg_check[a->pos].has_fd) { + ; + } else if (a->f.flags & SIZE) { + offset[a->pos] = parse_arg(g, a, offset[a->pos], + jvalue); + } else { + die(" No specification for argument %s", a->f.name); + } + } + + if (ret) { + ret_offset = gluten_rw_alloc_type(g, U64); + gluten_add_tag_post(g, ret, ret_offset); + } + + /* emit_call() */ + + if (count != json_object_get_count(obj)) + die(" Stray elements in call"); +} + +void handle_calls(struct gluten_ctx *g, JSON_Value *value) +{ + JSON_Array *calls = json_value_get_array(value); + unsigned i, count; + + if (calls) + count = json_array_get_count(calls); + else + count = 1; + + for (i = 0; i < count; i++) { + struct call **set, *call; + JSON_Object *obj, *args; + const char *name, *ret; + + if (calls) + obj = json_array_get_object(calls, i); + else + obj = json_value_get_object(value); + + name = json_object_get_name(obj, 0); + value = json_object_get_value_at(obj, 0); + args = json_object_get_object(obj, name); + + debug(" Parsing call %s", name); + + ret = json_object_get_string(obj, "ret"); + + /* TODO: Factor this out into a function in calls.c */ + for (set = call_sets, call = set[0]; *set; ) { + if (!call->name) { + set++; + call = set[0]; + continue; + } + + if (!strcmp(name, call->name)) { + debug(" Found description for %s", + name); + parse_call(g, args, ret, call->args); + break; + } + call++; + } + } +} -- cgit v1.2.3