/
githubmirror
/
strace
Обзор
Документация
Войти
/
githubmirror
/
strace
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
maint/gen/codegen.c
1 041 строка
25 KB
Dmitry V. Levin
Update copyright headers
05 авг 2025, 11:00
05 авг 2025, 11:00
7db24b3
Код
Авторство
О чём код?
/* * Copyright (c) 2021 Srikavin Ramkumar <srikavinramkumar@gmail.com> * Copyright (c) 2021-2025 The strace developers. * All rights reserved. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <assert.h> #include <ctype.h> #include <inttypes.h> #include <stdarg.h> #include <stdio.h> #include <string.h> #include "ast.h" #include "deflang.h" #include "symbols.h" struct { char *name; char *ctype; } basic_types[] = { {"uchar", "unsigned char"}, {"ushort", "unsigned short"}, {"uint", "unsigned int"}, {"ulong", "unsigned long"}, {"longlong", "long long"}, {"ulonglong", "unsigned long long"}, {"longdouble", "long double"}, {"string", "char"}, {"path", "char"}, {"size", "kernel_size_t"}, {"size_t", "kernel_size_t"}, {"gid", "gid_t"} }; char *signed_int_types[] = { "char", "short", "int", "long", "longlong", "kernel_long_t", "ssize_t" }; char *unsigned_int_types[] = { "uchar", "ushort", "uint", "ulong", "ulonglong", "kernel_ulong_t", "size_t", "size" }; static struct decoder_list *decoders = NULL; #define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0])) /* convenience macros */ #define OUTFI(...) outf_indent(indent_level, out, __VA_ARGS__) #define OUTF(...) outf(out, __VA_ARGS__) #define OUTC(c) outc(out, c) #define OUTSI(s) outs_indent(indent_level, out, s) #define OUTS(s) outs(out, s) static void outf_indent(int indent_level, FILE *out, const char *fmt, ...) __attribute__((format(printf, 3, 4))); static void outf(FILE *out, const char *fmt, ...) __attribute__((format(printf, 2, 3))); static void outc(FILE *out, int c) { fputc(c, out); } static void outs(FILE *out, const char *s) { fputs(s, out); } static void indent(FILE *out, int indent) { for (int i = 0; i < indent; ++i) { outc(out, '\t'); } } static void outs_indent(int indent_level, FILE *out, const char *s) { indent(out, indent_level); fprintf(out, "%s", s); } static void outf(FILE *out, const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(out, fmt, args); va_end(args); } static void outf_indent(int indent_level, FILE *out, const char *fmt, ...) { va_list args; va_start(args, fmt); indent(out, indent_level); vfprintf(out, fmt, args); va_end(args); } static void log_warning(char *fmt, struct ast_loc node, ...) { va_list args; va_start(args, node); fprintf(stderr, "Codegen Warning: "); fprintf(stderr, "line %d, col %d: ", node.lineno, node.colno); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); } static bool is_signed_integer_typename(const char *name) { for (size_t i = 0; i < ARRAY_LEN(signed_int_types); ++i) { if (strcmp(signed_int_types[i], name) == 0) { return true; } } return false; } static bool is_unsigned_integer_typename(const char *name) { for (size_t i = 0; i < ARRAY_LEN(unsigned_int_types); ++i) { if (strcmp(unsigned_int_types[i], name) == 0) { return true; } } return false; } /* * Stores a string referring to the i-th argument in the current syscall. */ static char * get_syscall_arg_value(struct syscall *syscall, size_t i) { if (syscall->is_ioctl) { if (i >= 1 && i <= 2) { const char *ioctl_args[3] = {"", "code", "arg"}; return xstrdup(ioctl_args[i]); } log_warning("ioctl decoder referenced OOB argument %zu", syscall->loc, i); } return xasprintf("tcp->u_arg[%zu]", i); } /* * Stores a string referring to the return value of the current syscall. */ static char * get_syscall_ret_value() { return xstrdup("tcp->u_rval"); } /* * Converts a string containing the C equivalent of a given type. */ static char * type_to_ctype(const struct ast_type *type) { if (type->type == TYPE_BASIC) { for (size_t i = 0; i < ARRAY_LEN(basic_types); ++i) { if (strcmp(type->name, basic_types[i].name) == 0) { return basic_types[i].ctype; } } struct ast_node *def = symbol_get(type->name); if (def != NULL && def->type == AST_STRUCT) { return xasprintf("struct %s", type->name); } return type->name; } if (type->type == TYPE_PTR) { return xasprintf("%s *", type_to_ctype(type->ptr.type)); } if (type->type == TYPE_XORFLAGS) { return type_to_ctype(type->xorflags.underlying); } if (type->type == TYPE_ORFLAGS) { return type_to_ctype(type->orflags.underlying); } return type->name; } static char * type_variable_declaration(const struct ast_type *type, const char *var_name) { if (type->type == TYPE_BASIC && strcmp(type->name, "array") == 0) { if (type->options != NULL && type->options->next != NULL) { struct ast_type_option *underlying_type = type->options->option; struct ast_type_option *member_count_type = type->options->next->option; if (underlying_type->child_type == AST_TYPE_CHILD_TYPE && member_count_type->child_type == AST_TYPE_CHILD_NUMBER) { return xasprintf("%s %s[%s];", type_to_ctype(underlying_type->type), var_name, member_count_type->number.raw); } } } return xasprintf("%s %s;", type_to_ctype(type), var_name); } /* * Get flags to return from a SYS_FUNC. */ static char * get_sys_func_return_flags(struct ast_type *type, bool is_ioctl) { struct { char *type; char *flag; } flags[] = { {"fd", "RVAL_FD"}, {"tid", "RVAL_TID"}, {"sid", "RVAL_SID"}, {"tgid", "RVAL_TGID"}, {"pgid", "RVAL_PGID"} }; char *base = "RVAL_DECODED"; if (is_ioctl) { base = "RVAL_IOCTL_DECODED"; } char *following = NULL; for (size_t i = 0; i < ARRAY_LEN(flags); ++i) { if (strcmp(flags[i].type, type->name) == 0) { following = flags[i].flag; break; } } if (following) { return xasprintf("%s | %s", base, following); } else { return xasprintf("%s", base); } } /* * Resolves a type option to a concrete value. * * For example, const[PATH_MAX] is resolved to PATH_MAX * and const[ref[argname]] is resolved to tcp->u_arg[2] * (where argname is the name of the 3rd syscall argument). * * The specified type option MUST NOT be a range or a template id. */ static char * resolve_type_option_to_value(struct syscall *syscall, struct ast_type_option *option) { assert(option->child_type != AST_TYPE_CHILD_RANGE && option->child_type != AST_TYPE_CHILD_TEMPLATE_ID); if (option->child_type == AST_TYPE_CHILD_NUMBER) { /* return the number exactly as specified in the source file */ return option->number.raw; } else if (option->child_type == AST_TYPE_CHILD_TYPE) { if (option->type->type == TYPE_REF) { /* identify which argument is being referred to */ /* syscall return value */ if (option->type->ref.return_value) { return get_syscall_ret_value(); } /* find syscall argument by name */ bool found = false; size_t index = 0; for (; index < syscall->arg_count; ++index) { if (strcmp(option->type->ref.argname, syscall->args[index].name) == 0) { found = true; break; } } if (found) { return get_syscall_arg_value(syscall, index); } log_warning("Failed to resolve 'ref' type" " with value \"%s\" to argument", syscall->loc, option->type->ref.argname); return "#error FAILED TO RESOLVE REF TYPE TO VALUE"; } else { /* assume the given value is a constant or from a #define */ return option->type->name; } } assert(false); } /* * Stores the value of a given variable using set_tcb_priv_data. */ static void store_single_value(FILE *out, struct ast_type *type, char *arg, int indent_level) { OUTFI("{\n"); indent_level++; OUTFI("%s\n", type_variable_declaration(type->ptr.type, "tmp_var")); OUTFI("if (!umove_or_printaddr(tcp, %s, &tmp_var)) {\n", arg); indent_level++; OUTFI("void *tmp_buffer = xmalloc(sizeof(%s));\n", "tmp_var"); OUTFI("memcpy(tmp_buffer, tmp_var, sizeof(%s));\n", "tmp_var"); OUTFI("set_tcb_priv_data(tcp, tmp_buffer, free);\n"); indent_level--; OUTFI("}\n"); indent_level--; OUTFI("}\n"); } static void generate_printer(FILE *out, struct syscall *syscall, const char *argname, const char *arg, bool entering, struct ast_type *type, int indent_level); static void generate_printer_ptr(FILE *out, struct syscall *syscall, const char *argname, const char *arg, bool entering, struct ast_type *type, int indent_level) { /* copy from target memory and use decoder for resulting value */ if ((IS_IN_PTR(type) && entering) || (IS_OUT_PTR(type) && !entering)) { CLEANUP_FREE char *var_name = xasprintf("tmpvar_%s", argname); OUTFI("%s\n", type_variable_declaration(type->ptr.type, var_name)); OUTFI("if (!umove_or_printaddr(tcp, %s, &%s)) {\n", arg, var_name); indent_level++; OUTFI("tprint_indirect_begin();\n"); generate_printer(out, syscall, argname, var_name, entering, type->ptr.type, indent_level); OUTFI("tprint_indirect_end();\n"); indent_level--; OUTSI("}\n"); } } static void generate_templated_printer(FILE *out, struct syscall *syscall, const char *arg, struct ast_type *arg_type, struct decoder templated_decoder) { struct { char *value; intmax_t template_id; } substitutions[256]; int subs_pos = 0; /* Do a DFS over the template type to find substitution markers. */ struct dfs_stack_entry { struct ast_type *template; struct ast_type *actual; }; struct dfs_stack_entry dfs_stack[128] = {0}; int stack_ptr = 0; dfs_stack[stack_ptr] = (struct dfs_stack_entry) { .template = templated_decoder.matching_type, .actual = arg_type }; stack_ptr++; while (stack_ptr != 0) { stack_ptr--; struct dfs_stack_entry entry = dfs_stack[stack_ptr]; if (entry.actual == NULL || entry.template == NULL) { continue; } if (strcmp(entry.actual->name, entry.template->name) != 0) { continue; } struct ast_type_option_list *template_option = entry.template->options; struct ast_type_option_list *actual_option = entry.actual->options; for (; actual_option != NULL && template_option != NULL; actual_option = actual_option->next, template_option = template_option->next) { if (template_option->option->child_type == AST_TYPE_CHILD_TEMPLATE_ID) { substitutions[subs_pos].value = resolve_type_option_to_value(syscall, actual_option->option); substitutions[subs_pos].template_id = template_option->option->template.id; subs_pos++; continue; } if (actual_option->option->child_type != template_option->option->child_type) { break; } if (template_option->option->child_type == AST_TYPE_CHILD_TYPE) { dfs_stack[stack_ptr] = (struct dfs_stack_entry) { .template = template_option->option->type, .actual = actual_option->option->type }; stack_ptr++; } } } /* * Output the template string and replace substitution markers * with real values. */ const char *template = templated_decoder.fmt_string; size_t template_len = strlen(template); intmax_t cur = 0; bool in_template_number = false; for (size_t i = 0; i < template_len; ++i) { if (template[i] == '$' && (template[i + 1] == '$')) { OUTF("(%s)", arg); i++; continue; } if (template[i] == '$' && (isdigit(template[i + 1]))) { cur = 0; in_template_number = true; continue; } if (!in_template_number) { OUTC(template[i]); continue; } if (isdigit(template[i])) { cur = cur * 10 + (template[i] - '0'); } if (!isdigit(template[i]) || i == template_len - 1) { in_template_number = false; int found = -1; /* find matching substitution */ for (int j = 0; j < subs_pos; ++j) { if (substitutions[j].template_id == cur) { found = j; break; } } if (found == -1) { log_warning("Template variable $%" PRIdMAX " could not be resolved!", syscall->loc, cur); continue; } OUTF("(%s)", substitutions[found].value); if (i != template_len - 1) { OUTC(template[i]); } } } } /* * Outputs a call to a function/macro to print out arg with the given type. */ static void generate_printer(FILE *out, struct syscall *syscall, const char *argname, const char *arg, bool entering, struct ast_type *type, int indent_level) { for (struct decoder_list *cur = decoders; cur != NULL; cur = cur->next) { if (ast_type_matching(cur->decoder.matching_type, type)) { OUTFI("/* using decoder from %s:%d:%d */\n", cur->decoder.loc.file, cur->decoder.loc.lineno, cur->decoder.loc.colno); generate_templated_printer(out, syscall, arg, type, cur->decoder); OUTC('\n'); return; } } if (type->type == TYPE_BASIC) { if (is_signed_integer_typename(type->name)) { OUTFI("PRINT_VAL_D((%s) %s);\n", type_to_ctype(type), arg); return; } else if (is_unsigned_integer_typename(type->name)) { OUTFI("PRINT_VAL_U((%s) %s);\n", type_to_ctype(type), arg); return; } log_warning("No known printer for basic type %s", syscall->loc, type->name); outf_indent(indent_level, out, "#error UNHANDLED BASIC TYPE: %s\n", type->name); } else if (type->type == TYPE_PTR) { generate_printer_ptr(out, syscall, argname, arg, entering, type, indent_level); } else if (type->type == TYPE_ORFLAGS) { OUTFI("printflags64(%s, zero_extend_signed_to_ull(%s), \"%s\");\n", type->orflags.flag_type->type->name, arg, type->orflags.dflt); } else if (type->type == TYPE_XORFLAGS) { OUTFI("printxval64(%s, zero_extend_signed_to_ull(%s), \"%s\");\n", type->xorflags.flag_type->type->name, arg, type->xorflags.dflt); } else if (strcmp(type->name, "stringnoz") == 0 || strcmp(type->name, "string") == 0) { log_warning("Type '%s' should be wrapped in a ptr type" " to indicate direction", syscall->loc, type->name); } else if (type->type == TYPE_CONST) { if (!type->constt.real_type) { log_warning("Const type (%s) has no matching" " parent syscall argument.", syscall->loc, argname); return; } OUTFI("/* inherited parent type (%s) */\n", type_to_ctype(type->constt.real_type)); generate_printer(out, syscall, argname, arg, entering, type->constt.real_type, indent_level); } else { log_warning("Type '%s' is currently unhandled", syscall->loc, type->name); outf_indent(indent_level, out, "#error UNHANDLED TYPE: %s\n", type->name); } } static void generate_return_flags(FILE *out, struct syscall *syscall, int indent_level) { struct ast_type ret = syscall->ret; if (ret.type == TYPE_ORFLAGS) { OUTFI("tcp->auxstr = sprintflags(\"%s\", %s" ", (kernel_ulong_t) tcp->u_rval);\n", ret.orflags.dflt, ret.orflags.flag_type->type->name); OUTFI("return RVAL_STR;\n"); } else if (ret.type == TYPE_XORFLAGS) { OUTFI("tcp->auxstr = xlookup(%s" ", (kernel_ulong_t) tcp->u_rval);\n", ret.xorflags.flag_type->type->name); OUTFI("return RVAL_STR;\n"); } else { CLEANUP_FREE char *flags = get_sys_func_return_flags(&ret, syscall->is_ioctl); OUTFI("return %s;\n", flags); } } /* * Transforms a variant syscall name (like fcntl$F_DUPFD) to a valid C function * name (like var_fcntl_F_DUPFD). * * The is_leaf parameter should be set if corresponding syscall is a leaf node, * i.e. has no sub syscalls. */ static char * get_variant_function_name(char *variant_name, bool is_leaf) { char *out = xasprintf("var_%s%s", is_leaf ? "leaf_" : "", variant_name); for (char *p = out; *p; ++p) { if (*p == '$') { *p = '_'; } } return out; } /* * Output the start of any preprocessor conditions. * * For example: * #ifdef linux */ void out_statement_condition_start(FILE *out, struct statement_condition *condition) { if (condition == NULL) { return; } for (size_t i = 0; i < condition->count; ++i) { OUTF("%s\n", condition->values[i]); } } /* * Output the end of the specified preprocessor conditions. * * For example: * #endif */ void out_statement_condition_end(FILE *out, struct statement_condition *condition) { if (condition == NULL) { return; } for (size_t i = 0; i < condition->count; ++i) { OUTS("#endif\n\n"); } } static char * get_decoder_prototype(bool internal, struct syscall *syscall, char *func_name) { return xasprintf("%sint\n" "%s(struct tcb *tcp%s)\n", internal ? "static " : "", func_name, syscall->is_ioctl ? ", unsigned int code, kernel_ulong_t arg" : ""); } /* * Prints out a decoder for the given system call. */ static void generate_decoder(FILE *out, struct syscall *syscall, bool is_variant, bool ioctl_fallback) { int indent_level = 0; out_statement_condition_start(out, syscall->conditions); int arg_offset = 0; int arg_index = 0; if (syscall->is_ioctl) { /* no need to decode code, or arg for ioctl variant syscalls */ arg_offset = 2; arg_index = 2; } /* * Determine which strategy to use depending on * how many OUT ptrs there are. */ size_t out_ptrs = 0; for (size_t i = arg_offset; i < syscall->arg_count; i++) { if (IS_OUT_PTR(syscall->args[i].type)) { out_ptrs++; } } /* output function declaration */ if (is_variant) { CLEANUP_FREE char *func_name = get_variant_function_name(syscall->name, true); CLEANUP_FREE char *decoder_prototype = get_decoder_prototype(true, syscall, func_name); OUTSI(decoder_prototype); } else { OUTFI("SYS_FUNC(%s)\n", syscall->name); } OUTSI("{\n"); indent_level++; if (syscall->is_ioctl && ioctl_fallback) { OUTSI("return RVAL_DECODED;\n"); indent_level--; OUTSI("}\n"); return; } if (out_ptrs == 0) { /* 0 out ptrs: print all args in sysenter */ for (size_t i = arg_offset; i < syscall->arg_count; i++) { struct syscall_argument arg = syscall->args[i]; OUTFI("/* arg: %s (%s) */\n", arg.name, type_to_ctype(arg.type)); if (i > arg_offset || syscall->is_ioctl) OUTFI("tprints_arg_next_name(\"%s\");\n", arg.name); else OUTFI("tprints_arg_name(\"%s\");\n", arg.name); CLEANUP_FREE char *arg_val = get_syscall_arg_value(syscall, arg_index++); generate_printer(out, syscall, arg.name, arg_val, true, arg.type, indent_level); OUTC('\n'); } } else if (out_ptrs == 1) { /* * == 1 out ptrs: print args until the out ptr in sysenter, * rest in sysexit */ size_t cur = arg_offset; OUTSI("if (entering(tcp)) {\n"); indent_level++; for (; cur < syscall->arg_count; ++cur) { struct syscall_argument arg = syscall->args[cur]; if (IS_OUT_PTR(arg.type)) { break; } OUTFI("/* arg: %s (%s) */\n", arg.name, type_to_ctype(arg.type)); if (cur > arg_offset || syscall->is_ioctl) OUTFI("tprints_arg_next_name(\"%s\");\n", arg.name); else OUTFI("tprints_arg_name(\"%s\");\n", arg.name); CLEANUP_FREE char *arg_val = get_syscall_arg_value(syscall, arg_index++); generate_printer(out, syscall, arg.name, arg_val, true, arg.type, indent_level); } if (cur < syscall->arg_count && IS_INOUT_PTR(syscall->args[cur].type)) { CLEANUP_FREE char *arg_val = get_syscall_arg_value(syscall, cur); store_single_value(out, syscall->args[cur].type, arg_val, indent_level); } OUTSI("return 0;\n"); indent_level--; OUTSI("}\n"); if (cur < syscall->arg_count && IS_INOUT_PTR(syscall->args[cur].type)) { /* * TODO: * Compare the current value with the previous value * and print only if changed. */ } for (; cur < syscall->arg_count; ++cur) { struct syscall_argument arg = syscall->args[cur]; OUTFI("/* arg: %s (%s) */\n", arg.name, type_to_ctype(arg.type)); if (cur > arg_offset || syscall->is_ioctl) OUTFI("tprints_arg_next_name(\"%s\");\n", arg.name); else OUTFI("tprints_arg_name(\"%s\");\n", arg.name); CLEANUP_FREE char *arg_val = get_syscall_arg_value(syscall, arg_index++); if (IS_INOUT_PTR(syscall->args[cur].type)) { generate_printer(out, syscall, arg.name, "get_tcb_priv_data(tcp)", false, arg.type, indent_level); } generate_printer(out, syscall, arg.name, arg_val, false, arg.type, indent_level); OUTC('\n'); } } else { /* * TODO * > 1 out ptrs; * store necessary ptr values using set_tcb_priv_data */ OUTSI("#error TODO\n"); } generate_return_flags(out, syscall, indent_level); indent_level--; OUTSI("}\n"); out_statement_condition_end(out, syscall->conditions); } /* * Write out the specified #define statements. */ void output_defines(FILE *out, struct preprocessor_statement_list *defines) { struct preprocessor_statement_list *cur = defines; while (cur != NULL) { out_statement_condition_start(out, cur->stmt.conditions); OUTF("#%s\n", cur->stmt.value); out_statement_condition_end(out, cur->stmt.conditions); cur = cur->next; } } /* * Outputs a function which delegates to the child syscalls based on the * values of the child's const-typed arguments. * * The is_variant flag indicates whether the group's base syscall is a child * of a variant syscall itself. */ void output_variant_syscall_group(FILE *out, struct syscall_group *group, bool is_variant) { int indent_level = 0; if (is_variant) { /* variant system call */ CLEANUP_FREE char *func_name = get_variant_function_name(group->base->name, false); CLEANUP_FREE char *decoder_prototype = get_decoder_prototype(false, group->base, func_name); OUTSI(decoder_prototype); } else { /* base system call */ OUTFI("SYS_FUNC(%s) {\n", group->base->name); } OUTSI("{\n"); indent_level++; OUTSI(""); for (size_t child = 0; child < group->child_count; child++) { struct syscall_group *cur_child_grp = &group->children[child]; struct syscall *cur_child = cur_child_grp->base; out_statement_condition_start(out, cur_child->conditions); OUTS("if ("); bool first = true; for (size_t arg_idx = 0; arg_idx < cur_child->arg_count; ++arg_idx) { struct syscall_argument arg = cur_child->args[arg_idx]; if (arg.type->type != TYPE_CONST) { continue; } if (first) { first = false; } else { OUTS(" && "); } CLEANUP_FREE char *arg_str = get_syscall_arg_value(cur_child, arg_idx); if (arg.type->constt.value->child_type == AST_TYPE_CHILD_RANGE) { OUTF("((%s) <= (%s) && (%s) <= (%s))", arg_str, resolve_type_option_to_value(cur_child, arg.type->constt.value->range.min), arg_str, resolve_type_option_to_value(cur_child, arg.type->constt.value->range.max) ); } else { OUTF("(%s) == (%s)", arg_str, resolve_type_option_to_value(cur_child, arg.type->constt.value)); } } OUTS(") {\n"); indent_level++; CLEANUP_FREE char *func_name = get_variant_function_name(cur_child->name, cur_child_grp->child_count == 0); OUTFI("return %s(tcp%s);\n", func_name, cur_child->is_ioctl ? ", code, arg" : ""); indent_level--; OUTSI("} else "); } OUTS("{\n"); indent_level++; CLEANUP_FREE char *func_name = get_variant_function_name(group->base->name, true); OUTFI("return %s(tcp%s);\n", func_name, group->base->is_ioctl ? ", code, arg" : ""); indent_level--; OUTSI("}\n"); indent_level--; OUTSI("}\n"); } /* * Outputs a syscall group and syscall variants. */ void output_syscall_groups(FILE *out, struct syscall_group *groups, size_t group_count, struct syscall_group *parent) { for (size_t i = 0; i < group_count; ++i) { struct syscall_group *cur = &groups[i]; if (parent) { /* * Store the real type of const parameters * based on their parent. */ for (size_t j = 0; j < cur->base->arg_count && j < parent->base->arg_count; ++j) { struct syscall_argument *cur_arg = &cur->base->args[j]; struct syscall_argument *parent_arg = &parent->base->args[j]; if (cur_arg->type->type == TYPE_CONST) { if (parent_arg->type->type == TYPE_CONST) { cur_arg->type->constt.real_type = parent_arg->type->constt.real_type; } else { cur_arg->type->constt.real_type = parent_arg->type; } } } } if (groups[i].child_count == 0) { generate_decoder(out, groups[i].base, parent != NULL, false); continue; } output_syscall_groups(out, groups[i].children, groups[i].child_count, &groups[i]); if (strcmp(groups[i].base->name, "ioctl") != 0) { generate_decoder(out, groups[i].base, true, true); output_variant_syscall_group(out, &groups[i], parent != NULL); } } } bool generate_code(const char *in_filename, const char *out_filename, struct processed_ast *ast) { FILE *out = fopen(out_filename, "w"); if (out == NULL) { return false; } outf(out, "/* Generated by ./maint/gen/generate.sh" " from ./maint/gen/%s; do not edit. */\n\n", in_filename); outf(out, "%s", "#include <stddef.h>\n" "#include \"generated.h\"\n\n" "typedef kernel_ulong_t kernel_size_t;\n\n" ); decoders = ast->decoders; output_defines(out, ast->preprocessor_stmts); output_syscall_groups(out, ast->syscall_groups, ast->syscall_group_count, NULL); fclose(out); return true; }