From ebb9b54ddbceb533c19b176a0ecbb1f3f9c26543 Mon Sep 17 00:00:00 2001 From: zet Date: Wed, 29 Nov 2017 15:46:24 +0800 Subject: [PATCH] alpha version rap code --- .DS_Store | Bin 8196 -> 8196 bytes src/Makefile | 6 + src/gcc-common.h | 916 +++++++++++++++++++++++++++++ src/gcc-generate-gimple-pass.h | 175 ++++++ src/gcc-generate-ipa-pass.h | 289 +++++++++ src/gcc-generate-rtl-pass.h | 175 ++++++ src/gcc-generate-simple_ipa-pass.h | 175 ++++++ src/rap.h | 45 ++ src/rap_fptr_pass.c | 278 +++++++++ src/rap_hash.c | 380 ++++++++++++ src/rap_plugin.c | 876 +++++++++++++++++++++++++++ src/rap_ret_pass.c | 346 +++++++++++ 12 files changed, 3661 insertions(+) create mode 100755 src/Makefile create mode 100755 src/gcc-common.h create mode 100755 src/gcc-generate-gimple-pass.h create mode 100755 src/gcc-generate-ipa-pass.h create mode 100755 src/gcc-generate-rtl-pass.h create mode 100755 src/gcc-generate-simple_ipa-pass.h create mode 100755 src/rap.h create mode 100755 src/rap_fptr_pass.c create mode 100755 src/rap_hash.c create mode 100755 src/rap_plugin.c create mode 100755 src/rap_ret_pass.c diff --git a/.DS_Store b/.DS_Store index dff041b748ac3780e079dc35ce15171c954d6178..2cdbea7251df0a0914096ace81318650f8e33ffa 100644 GIT binary patch delta 19 acmZp1XmQwZT7b>cP)EVsVDm)*V}1Zamj(?0 delta 19 acmZp1XmQwZT7b>MNJqiUWb;J= 6000 +#include "gcc-plugin.h" +#else +#include "plugin.h" +#endif +#include "plugin-version.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "line-map.h" +#include "input.h" +#include "tree.h" + +#include "tree-inline.h" +#include "version.h" +#include "rtl.h" +#include "tm_p.h" +#include "flags.h" +#include "hard-reg-set.h" +#include "output.h" +#include "except.h" +#include "function.h" +#include "toplev.h" +#include "basic-block.h" +#include "intl.h" +#include "ggc.h" +#include "timevar.h" + +#include "params.h" + +#if BUILDING_GCC_VERSION <= 4009 +#include "pointer-set.h" +#else +#include "hash-map.h" +#endif + +#if BUILDING_GCC_VERSION >= 7000 +#include "memmodel.h" +#endif +#include "emit-rtl.h" +#include "debug.h" +#include "target.h" +#include "langhooks.h" +#include "cfgloop.h" +#include "cgraph.h" +#include "opts.h" + +#if BUILDING_GCC_VERSION == 4005 +#include +#endif + +#if BUILDING_GCC_VERSION >= 4007 +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#endif + +#if BUILDING_GCC_VERSION >= 4006 +#include "c-family/c-common.h" +#else +#include "c-common.h" +#endif + +#if BUILDING_GCC_VERSION <= 4008 +#include "tree-flow.h" +#else +#include "tree-cfgcleanup.h" +#include "tree-ssa-operands.h" +#include "tree-into-ssa.h" +#endif + +#if BUILDING_GCC_VERSION >= 4008 +#include "is-a.h" +#endif + +#include "diagnostic.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "predict.h" +#include "ipa-utils.h" + +#if BUILDING_GCC_VERSION >= 4009 +#include "attribs.h" +#include "varasm.h" +#include "stor-layout.h" +#include "internal-fn.h" +#include "gimple-expr.h" +#include "gimple-fold.h" +#include "context.h" +#include "tree-ssa-alias.h" +#include "tree-ssa.h" +#include "stringpool.h" +#if BUILDING_GCC_VERSION >= 7000 +#include "tree-vrp.h" +#endif +#include "tree-ssanames.h" +#include "print-tree.h" +#include "tree-eh.h" +#include "stmt.h" +#include "gimplify.h" +#endif + +#include "gimple.h" + +#if BUILDING_GCC_VERSION >= 4009 +#include "tree-ssa-operands.h" +#include "tree-phinodes.h" +#include "tree-cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include "ssa-iterators.h" +#endif + +#if BUILDING_GCC_VERSION >= 5000 +#include "builtins.h" +#endif + +/* #include "expr.h" where are you... */ +extern rtx emit_move_insn(rtx x, rtx y); + +/* missing from basic_block.h... */ +extern void debug_dominance_info(enum cdi_direction dir); +extern void debug_dominance_tree(enum cdi_direction dir, basic_block root); + +#if BUILDING_GCC_VERSION == 4006 +extern void debug_gimple_stmt(gimple); +extern void debug_gimple_seq(gimple_seq); +extern void print_gimple_seq(FILE *, gimple_seq, int, int); +extern void print_gimple_stmt(FILE *, gimple, int, int); +extern void print_gimple_expr(FILE *, gimple, int, int); +extern void dump_gimple_stmt(pretty_printer *, gimple, int, int); +#endif + +#define __unused __attribute__((__unused__)) +#define __visible __attribute__((visibility("default"))) + +#define DECL_NAME_POINTER(node) IDENTIFIER_POINTER(DECL_NAME(node)) +#define DECL_NAME_LENGTH(node) IDENTIFIER_LENGTH(DECL_NAME(node)) +#define TYPE_NAME_POINTER(node) IDENTIFIER_POINTER(TYPE_NAME(node)) +#define TYPE_NAME_LENGTH(node) IDENTIFIER_LENGTH(TYPE_NAME(node)) + +/* should come from c-tree.h if only it were installed for gcc 4.5... */ +#define C_TYPE_FIELDS_READONLY(TYPE) TREE_LANG_FLAG_1(TYPE) + +#if BUILDING_GCC_VERSION == 4005 +#define FOR_EACH_LOCAL_DECL(FUN, I, D) \ + for (tree vars = (FUN)->local_decls, (I) = 0; \ + vars && ((D) = TREE_VALUE(vars)); \ + vars = TREE_CHAIN(vars), (I)++) +#define DECL_CHAIN(NODE) (TREE_CHAIN(DECL_MINIMAL_CHECK(NODE))) +#define FOR_EACH_VEC_ELT(T, V, I, P) \ + for (I = 0; VEC_iterate(T, (V), (I), (P)); ++(I)) +#define TODO_rebuild_cgraph_edges 0 +#define SCOPE_FILE_SCOPE_P(EXP) (!(EXP)) + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +typedef struct varpool_node *varpool_node_ptr; + +static inline bool gimple_call_builtin_p(gimple stmt, enum built_in_function code) +{ + tree fndecl; + + if (!is_gimple_call(stmt)) + return false; + fndecl = gimple_call_fndecl(stmt); + if (!fndecl || DECL_BUILT_IN_CLASS(fndecl) != BUILT_IN_NORMAL) + return false; + return DECL_FUNCTION_CODE(fndecl) == code; +} + +static inline bool is_simple_builtin(tree decl) +{ + if (decl && DECL_BUILT_IN_CLASS(decl) != BUILT_IN_NORMAL) + return false; + + switch (DECL_FUNCTION_CODE(decl)) { + /* Builtins that expand to constants. */ + case BUILT_IN_CONSTANT_P: + case BUILT_IN_EXPECT: + case BUILT_IN_OBJECT_SIZE: + case BUILT_IN_UNREACHABLE: + /* Simple register moves or loads from stack. */ + case BUILT_IN_RETURN_ADDRESS: + case BUILT_IN_EXTRACT_RETURN_ADDR: + case BUILT_IN_FROB_RETURN_ADDR: + case BUILT_IN_RETURN: + case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: + case BUILT_IN_FRAME_ADDRESS: + case BUILT_IN_VA_END: + case BUILT_IN_STACK_SAVE: + case BUILT_IN_STACK_RESTORE: + /* Exception state returns or moves registers around. */ + case BUILT_IN_EH_FILTER: + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_COPY_VALUES: + return true; + + default: + return false; + } +} + +static inline void add_local_decl(struct function *fun, tree d) +{ + gcc_assert(TREE_CODE(d) == VAR_DECL); + fun->local_decls = tree_cons(NULL_TREE, d, fun->local_decls); +} +#endif + +#if BUILDING_GCC_VERSION <= 4006 +#define ANY_RETURN_P(rtx) (GET_CODE(rtx) == RETURN) +#define C_DECL_REGISTER(EXP) DECL_LANG_FLAG_4(EXP) +#define EDGE_PRESERVE 0ULL +#define HOST_WIDE_INT_PRINT_HEX_PURE "%" HOST_WIDE_INT_PRINT "x" +#define flag_fat_lto_objects true + +#define get_random_seed(noinit) ({ \ + unsigned HOST_WIDE_INT seed; \ + sscanf(get_random_seed(noinit), "%" HOST_WIDE_INT_PRINT "x", &seed); \ + seed * seed; }) + +#define int_const_binop(code, arg1, arg2) \ + int_const_binop((code), (arg1), (arg2), 0) + +static inline bool gimple_clobber_p(gimple s __unused) +{ + return false; +} + +static inline bool gimple_asm_clobbers_memory_p(const_gimple stmt) +{ + unsigned i; + + for (i = 0; i < gimple_asm_nclobbers(stmt); i++) { + tree op = gimple_asm_clobber_op(stmt, i); + + if (!strcmp(TREE_STRING_POINTER(TREE_VALUE(op)), "memory")) + return true; + } + + return false; +} + +static inline tree builtin_decl_implicit(enum built_in_function fncode) +{ + return implicit_built_in_decls[fncode]; +} + +static inline int ipa_reverse_postorder(struct cgraph_node **order) +{ + return cgraph_postorder(order); +} + +static inline struct cgraph_node *cgraph_create_node(tree decl) +{ + return cgraph_node(decl); +} + +static inline struct cgraph_node *cgraph_get_create_node(tree decl) +{ + struct cgraph_node *node = cgraph_get_node(decl); + + return node ? node : cgraph_node(decl); +} + +static inline bool cgraph_function_with_gimple_body_p(struct cgraph_node *node) +{ + return node->analyzed && !node->thunk.thunk_p && !node->alias; +} + +static inline struct cgraph_node *cgraph_first_function_with_gimple_body(void) +{ + struct cgraph_node *node; + + for (node = cgraph_nodes; node; node = node->next) + if (cgraph_function_with_gimple_body_p(node)) + return node; + return NULL; +} + +static inline struct cgraph_node *cgraph_next_function_with_gimple_body(struct cgraph_node *node) +{ + for (node = node->next; node; node = node->next) + if (cgraph_function_with_gimple_body_p(node)) + return node; + return NULL; +} + +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable) +{ + cgraph_node_ptr alias; + + if (callback(node, data)) + return true; + + for (alias = node->same_body; alias; alias = alias->next) { + if (include_overwritable || cgraph_function_body_availability(alias) > AVAIL_OVERWRITABLE) + if (cgraph_for_node_and_aliases(alias, callback, data, include_overwritable)) + return true; + } + + return false; +} + +#define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \ + for ((node) = cgraph_first_function_with_gimple_body(); (node); \ + (node) = cgraph_next_function_with_gimple_body(node)) + +static inline void varpool_add_new_variable(tree decl) +{ + varpool_finalize_decl(decl); +} +#endif + +#if BUILDING_GCC_VERSION <= 4007 +#define FOR_EACH_FUNCTION(node) \ + for (node = cgraph_nodes; node; node = node->next) +#define FOR_EACH_VARIABLE(node) \ + for (node = varpool_nodes; node; node = node->next) +#define PROP_loops 0 +#define NODE_SYMBOL(node) (node) +#define NODE_DECL(node) (node)->decl +#define INSN_LOCATION(INSN) RTL_LOCATION(INSN) +#define vNULL NULL + +static inline int bb_loop_depth(const_basic_block bb) +{ + return bb->loop_father ? loop_depth(bb->loop_father) : 0; +} + +static inline bool gimple_store_p(gimple gs) +{ + tree lhs = gimple_get_lhs(gs); + + return lhs && !is_gimple_reg(lhs); +} + +static inline void gimple_init_singleton(gimple g __unused) +{ +} +#endif + +#if BUILDING_GCC_VERSION == 4007 || BUILDING_GCC_VERSION == 4008 +static inline struct cgraph_node *cgraph_alias_target(struct cgraph_node *n) +{ + return cgraph_alias_aliased_node(n); +} +#endif + +#if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION <= 4009 +#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \ + cgraph_create_edge((caller), (callee), (call_stmt), (count), (freq)) +#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \ + cgraph_create_edge_including_clones((caller), (callee), (old_call_stmt), (call_stmt), (count), (freq), (reason)) +#endif + +#if BUILDING_GCC_VERSION <= 4008 +#define ENTRY_BLOCK_PTR_FOR_FN(FN) ENTRY_BLOCK_PTR_FOR_FUNCTION(FN) +#define EXIT_BLOCK_PTR_FOR_FN(FN) EXIT_BLOCK_PTR_FOR_FUNCTION(FN) +#define basic_block_info_for_fn(FN) ((FN)->cfg->x_basic_block_info) +#define n_basic_blocks_for_fn(FN) ((FN)->cfg->x_n_basic_blocks) +#define n_edges_for_fn(FN) ((FN)->cfg->x_n_edges) +#define last_basic_block_for_fn(FN) ((FN)->cfg->x_last_basic_block) +#define label_to_block_map_for_fn(FN) ((FN)->cfg->x_label_to_block_map) +#define profile_status_for_fn(FN) ((FN)->cfg->x_profile_status) +#define BASIC_BLOCK_FOR_FN(FN, N) BASIC_BLOCK_FOR_FUNCTION((FN), (N)) +#define NODE_IMPLICIT_ALIAS(node) (node)->same_body_alias +#define VAR_P(NODE) (TREE_CODE(NODE) == VAR_DECL) + +static inline bool tree_fits_shwi_p(const_tree t) +{ + if (t == NULL_TREE || TREE_CODE(t) != INTEGER_CST) + return false; + + if (TREE_INT_CST_HIGH(t) == 0 && (HOST_WIDE_INT)TREE_INT_CST_LOW(t) >= 0) + return true; + + if (TREE_INT_CST_HIGH(t) == -1 && (HOST_WIDE_INT)TREE_INT_CST_LOW(t) < 0 && !TYPE_UNSIGNED(TREE_TYPE(t))) + return true; + + return false; +} + +static inline bool tree_fits_uhwi_p(const_tree t) +{ + if (t == NULL_TREE || TREE_CODE(t) != INTEGER_CST) + return false; + + return TREE_INT_CST_HIGH(t) == 0; +} + +static inline HOST_WIDE_INT tree_to_shwi(const_tree t) +{ + gcc_assert(tree_fits_shwi_p(t)); + return TREE_INT_CST_LOW(t); +} + +static inline unsigned HOST_WIDE_INT tree_to_uhwi(const_tree t) +{ + gcc_assert(tree_fits_uhwi_p(t)); + return TREE_INT_CST_LOW(t); +} + +static inline const char *get_tree_code_name(enum tree_code code) +{ + gcc_assert(code < MAX_TREE_CODES); + return tree_code_name[code]; +} + +#define ipa_remove_stmt_references(cnode, stmt) + +typedef union gimple_statement_d gasm; +typedef union gimple_statement_d gassign; +typedef union gimple_statement_d gcall; +typedef union gimple_statement_d gcond; +typedef union gimple_statement_d gdebug; +typedef union gimple_statement_d ggoto; +typedef union gimple_statement_d gphi; +typedef union gimple_statement_d greturn; + +static inline gasm *as_a_gasm(gimple stmt) +{ + return stmt; +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return stmt; +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return stmt; +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return stmt; +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return stmt; +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return stmt; +} + +static inline gcond *as_a_gcond(gimple stmt) +{ + return stmt; +} + +static inline const gcond *as_a_const_gcond(const_gimple stmt) +{ + return stmt; +} + +static inline gdebug *as_a_gdebug(gimple stmt) +{ + return stmt; +} + +static inline const gdebug *as_a_const_gdebug(const_gimple stmt) +{ + return stmt; +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return stmt; +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return stmt; +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return stmt; +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return stmt; +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return stmt; +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return stmt; +} +#endif + +#if BUILDING_GCC_VERSION == 4008 +#define NODE_SYMBOL(node) (&(node)->symbol) +#define NODE_DECL(node) (node)->symbol.decl +#endif + +#if BUILDING_GCC_VERSION >= 4008 +#define add_referenced_var(var) +#define mark_sym_for_renaming(var) +#define varpool_mark_needed_node(node) +#define create_var_ann(var) +#define TODO_dump_func 0 +#define TODO_dump_cgraph 0 +#endif + +#if BUILDING_GCC_VERSION <= 4009 +#define TODO_verify_il 0 +#define AVAIL_INTERPOSABLE AVAIL_OVERWRITABLE + +#define section_name_prefix LTO_SECTION_NAME_PREFIX +#define fatal_error(loc, gmsgid, ...) fatal_error((gmsgid), __VA_ARGS__) + +typedef struct rtx_def rtx_insn; + +static inline const char *get_decl_section_name(const_tree decl) +{ + if (DECL_SECTION_NAME(decl) == NULL_TREE) + return NULL; + + return TREE_STRING_POINTER(DECL_SECTION_NAME(decl)); +} + +static inline void set_decl_section_name(tree node, const char *value) +{ + if (value) + DECL_SECTION_NAME(node) = build_string(strlen(value) + 1, value); + else + DECL_SECTION_NAME(node) = NULL; +} +#endif + +#if BUILDING_GCC_VERSION == 4009 +typedef struct gimple_statement_asm gasm; +typedef struct gimple_statement_base gassign; +typedef struct gimple_statement_call gcall; +typedef struct gimple_statement_base gcond; +typedef struct gimple_statement_base gdebug; +typedef struct gimple_statement_base ggoto; +typedef struct gimple_statement_phi gphi; +typedef struct gimple_statement_base greturn; + +static inline gasm *as_a_gasm(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return stmt; +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return stmt; +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gcond *as_a_gcond(gimple stmt) +{ + return stmt; +} + +static inline const gcond *as_a_const_gcond(const_gimple stmt) +{ + return stmt; +} + +static inline gdebug *as_a_gdebug(gimple stmt) +{ + return stmt; +} + +static inline const gdebug *as_a_const_gdebug(const_gimple stmt) +{ + return stmt; +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return stmt; +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return stmt; +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return stmt; +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return stmt; +} +#endif + +#if BUILDING_GCC_VERSION >= 4009 +#define TODO_ggc_collect 0 +#define NODE_SYMBOL(node) (node) +#define NODE_DECL(node) (node)->decl +#define cgraph_node_name(node) (node)->name() +#define NODE_IMPLICIT_ALIAS(node) (node)->cpp_implicit_alias +#endif + +#if BUILDING_GCC_VERSION >= 5000 && BUILDING_GCC_VERSION < 6000 +/* gimple related */ +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_ASSIGN; +} +#endif + +#if BUILDING_GCC_VERSION >= 5000 +#define TODO_verify_ssa TODO_verify_il +#define TODO_verify_flow TODO_verify_il +#define TODO_verify_stmts TODO_verify_il +#define TODO_verify_rtl_sharing TODO_verify_il + +#define INSN_DELETED_P(insn) (insn)->deleted() + +static inline const char *get_decl_section_name(const_tree decl) +{ + return DECL_SECTION_NAME(decl); +} + +/* symtab/cgraph related */ +#define debug_cgraph_node(node) (node)->debug() +#define cgraph_get_node(decl) cgraph_node::get(decl) +#define cgraph_get_create_node(decl) cgraph_node::get_create(decl) +#define cgraph_create_node(decl) cgraph_node::create(decl) +#define cgraph_n_nodes symtab->cgraph_count +#define cgraph_max_uid symtab->cgraph_max_uid +#define varpool_get_node(decl) varpool_node::get(decl) +#define dump_varpool_node(file, node) (node)->dump(file) + +#define cgraph_create_edge(caller, callee, call_stmt, count, freq, nest) \ + (caller)->create_edge((callee), (call_stmt), (count), (freq)) +#define cgraph_create_edge_including_clones(caller, callee, old_call_stmt, call_stmt, count, freq, nest, reason) \ + (caller)->create_edge_including_clones((callee), (old_call_stmt), (call_stmt), (count), (freq), (reason)) + +typedef struct cgraph_node *cgraph_node_ptr; +typedef struct cgraph_edge *cgraph_edge_p; +typedef struct varpool_node *varpool_node_ptr; + +static inline void change_decl_assembler_name(tree decl, tree name) +{ + symtab->change_decl_assembler_name(decl, name); +} + +static inline void varpool_finalize_decl(tree decl) +{ + varpool_node::finalize_decl(decl); +} + +static inline void varpool_add_new_variable(tree decl) +{ + varpool_node::add(decl); +} + +static inline unsigned int rebuild_cgraph_edges(void) +{ + return cgraph_edge::rebuild_edges(); +} + +static inline cgraph_node_ptr cgraph_function_node(cgraph_node_ptr node, enum availability *availability) +{ + return node->function_symbol(availability); +} + +static inline cgraph_node_ptr cgraph_function_or_thunk_node(cgraph_node_ptr node, enum availability *availability = NULL) +{ + return node->ultimate_alias_target(availability); +} + +static inline bool cgraph_only_called_directly_p(cgraph_node_ptr node) +{ + return node->only_called_directly_p(); +} + +static inline enum availability cgraph_function_body_availability(cgraph_node_ptr node) +{ + return node->get_availability(); +} + +static inline cgraph_node_ptr cgraph_alias_target(cgraph_node_ptr node) +{ + return node->get_alias_target(); +} + +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable) +{ + return node->call_for_symbol_thunks_and_aliases(callback, data, include_overwritable); +} + +static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data) +{ + return symtab->add_cgraph_insertion_hook(hook, data); +} + +static inline void cgraph_remove_function_insertion_hook(struct cgraph_node_hook_list *entry) +{ + symtab->remove_cgraph_insertion_hook(entry); +} + +static inline struct cgraph_node_hook_list *cgraph_add_node_removal_hook(cgraph_node_hook hook, void *data) +{ + return symtab->add_cgraph_removal_hook(hook, data); +} + +static inline void cgraph_remove_node_removal_hook(struct cgraph_node_hook_list *entry) +{ + symtab->remove_cgraph_removal_hook(entry); +} + +static inline struct cgraph_2node_hook_list *cgraph_add_node_duplication_hook(cgraph_2node_hook hook, void *data) +{ + return symtab->add_cgraph_duplication_hook(hook, data); +} + +static inline void cgraph_remove_node_duplication_hook(struct cgraph_2node_hook_list *entry) +{ + symtab->remove_cgraph_duplication_hook(entry); +} + +static inline void cgraph_call_node_duplication_hooks(cgraph_node_ptr node, cgraph_node_ptr node2) +{ + symtab->call_cgraph_duplication_hooks(node, node2); +} + +static inline void cgraph_call_edge_duplication_hooks(cgraph_edge *cs1, cgraph_edge *cs2) +{ + symtab->call_edge_duplication_hooks(cs1, cs2); +} + +#if BUILDING_GCC_VERSION >= 6000 +typedef gimple *gimple_ptr; +typedef const gimple *const_gimple_ptr; +#define gimple gimple_ptr +#define const_gimple const_gimple_ptr +#undef CONST_CAST_GIMPLE +#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X)) +#endif + +/* gimple related */ +static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL) +{ + return gimple_build_assign(lhs, subcode, op1, op2 PASS_MEM_STAT); +} + +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_GOTO; +} + +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_RETURN; +} + +static inline gasm *as_a_gasm(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return as_a(stmt); +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return as_a(stmt); +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return as_a(stmt); +} + +/* IPA/LTO related */ +#define ipa_ref_list_referring_iterate(L, I, P) \ + (L)->referring.iterate((I), &(P)) +#define ipa_ref_list_reference_iterate(L, I, P) \ + (L)->reference.iterate((I), &(P)) + +static inline cgraph_node_ptr ipa_ref_referring_node(struct ipa_ref *ref) +{ + return dyn_cast(ref->referring); +} + +static inline void ipa_remove_stmt_references(symtab_node *referring_node, gimple stmt) +{ + referring_node->remove_stmt_references(stmt); +} +#endif + +#if BUILDING_GCC_VERSION < 6000 +#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ + get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, pvolatilep, keep_aligning) +#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET(VOIDmode, (ARG0), (ARG1)) +#endif + +#if BUILDING_GCC_VERSION >= 6000 +#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET((ARG0), (ARG1)) +#endif + +#ifdef __cplusplus +static inline void debug_tree(const_tree t) +{ + debug_tree(CONST_CAST_TREE(t)); +} + +static inline void debug_gimple_stmt(const_gimple s) +{ + debug_gimple_stmt(CONST_CAST_GIMPLE(s)); +} +#else +#define debug_tree(t) debug_tree(CONST_CAST_TREE(t)) +#define debug_gimple_stmt(s) debug_gimple_stmt(CONST_CAST_GIMPLE(s)) +#endif + +#if BUILDING_GCC_VERSION >= 7000 +#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ + get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep) +#endif + +#endif diff --git a/src/gcc-generate-gimple-pass.h b/src/gcc-generate-gimple-pass.h new file mode 100755 index 0000000..526c3c7 --- /dev/null +++ b/src/gcc-generate-gimple-pass.h @@ -0,0 +1,175 @@ +/* + * Generator for GIMPLE pass related boilerplate code/data + * + * Supports gcc 4.5-6 + * + * Usage: + * + * 1. before inclusion define PASS_NAME + * 2. before inclusion define NO_* for unimplemented callbacks + * NO_GATE + * NO_EXECUTE + * 3. before inclusion define PROPERTIES_* and TODO_FLAGS_* to override + * the default 0 values + * 4. for convenience, all the above will be undefined after inclusion! + * 5. the only exported name is make_PASS_NAME_pass() to register with gcc + */ + +#ifndef PASS_NAME +#error at least PASS_NAME must be defined +#else +#define __GCC_PLUGIN_STRINGIFY(n) #n +#define _GCC_PLUGIN_STRINGIFY(n) __GCC_PLUGIN_STRINGIFY(n) +#define _GCC_PLUGIN_CONCAT2(x, y) x ## y +#define _GCC_PLUGIN_CONCAT3(x, y, z) x ## y ## z + +#define __PASS_NAME_PASS_DATA(n) _GCC_PLUGIN_CONCAT2(n, _pass_data) +#define _PASS_NAME_PASS_DATA __PASS_NAME_PASS_DATA(PASS_NAME) + +#define __PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT2(n, _pass) +#define _PASS_NAME_PASS __PASS_NAME_PASS(PASS_NAME) + +#define _PASS_NAME_NAME _GCC_PLUGIN_STRINGIFY(PASS_NAME) + +#define __MAKE_PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT3(make_, n, _pass) +#define _MAKE_PASS_NAME_PASS __MAKE_PASS_NAME_PASS(PASS_NAME) + +#ifdef NO_GATE +#define _GATE NULL +#define _HAS_GATE false +#else +#define __GATE(n) _GCC_PLUGIN_CONCAT2(n, _gate) +#define _GATE __GATE(PASS_NAME) +#define _HAS_GATE true +#endif + +#ifdef NO_EXECUTE +#define _EXECUTE NULL +#define _HAS_EXECUTE false +#else +#define __EXECUTE(n) _GCC_PLUGIN_CONCAT2(n, _execute) +#define _EXECUTE __EXECUTE(PASS_NAME) +#define _HAS_EXECUTE true +#endif + +#ifndef PROPERTIES_REQUIRED +#define PROPERTIES_REQUIRED 0 +#endif + +#ifndef PROPERTIES_PROVIDED +#define PROPERTIES_PROVIDED 0 +#endif + +#ifndef PROPERTIES_DESTROYED +#define PROPERTIES_DESTROYED 0 +#endif + +#ifndef TODO_FLAGS_START +#define TODO_FLAGS_START 0 +#endif + +#ifndef TODO_FLAGS_FINISH +#define TODO_FLAGS_FINISH 0 +#endif + +#if BUILDING_GCC_VERSION >= 4009 +namespace { +static const pass_data _PASS_NAME_PASS_DATA = { +#else +static struct gimple_opt_pass _PASS_NAME_PASS = { + .pass = { +#endif + .type = GIMPLE_PASS, + .name = _PASS_NAME_NAME, +#if BUILDING_GCC_VERSION >= 4008 + .optinfo_flags = OPTGROUP_NONE, +#endif +#if BUILDING_GCC_VERSION >= 5000 +#elif BUILDING_GCC_VERSION == 4009 + .has_gate = _HAS_GATE, + .has_execute = _HAS_EXECUTE, +#else + .gate = _GATE, + .execute = _EXECUTE, + .sub = NULL, + .next = NULL, + .static_pass_number = 0, +#endif + .tv_id = TV_NONE, + .properties_required = PROPERTIES_REQUIRED, + .properties_provided = PROPERTIES_PROVIDED, + .properties_destroyed = PROPERTIES_DESTROYED, + .todo_flags_start = TODO_FLAGS_START, + .todo_flags_finish = TODO_FLAGS_FINISH, +#if BUILDING_GCC_VERSION < 4009 + } +#endif +}; + +#if BUILDING_GCC_VERSION >= 4009 +class _PASS_NAME_PASS : public gimple_opt_pass { +public: + _PASS_NAME_PASS() : gimple_opt_pass(_PASS_NAME_PASS_DATA, g) {} + +#ifndef NO_GATE +#if BUILDING_GCC_VERSION >= 5000 + virtual bool gate(function *) { return _GATE(); } +#else + virtual bool gate(void) { return _GATE(); } +#endif +#endif + + virtual opt_pass * clone () { return new _PASS_NAME_PASS(); } + +#ifndef NO_EXECUTE +#if BUILDING_GCC_VERSION >= 5000 + virtual unsigned int execute(function *) { return _EXECUTE(); } +#else + virtual unsigned int execute(void) { return _EXECUTE(); } +#endif +#endif +}; +} + +opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return new _PASS_NAME_PASS(); +} +#else +struct opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return &_PASS_NAME_PASS.pass; +} +#endif + +/* clean up user provided defines */ +#undef PASS_NAME +#undef NO_GATE +#undef NO_EXECUTE + +#undef PROPERTIES_DESTROYED +#undef PROPERTIES_PROVIDED +#undef PROPERTIES_REQUIRED +#undef TODO_FLAGS_FINISH +#undef TODO_FLAGS_START + +/* clean up generated defines */ +#undef _EXECUTE +#undef __EXECUTE +#undef _GATE +#undef __GATE +#undef _GCC_PLUGIN_CONCAT2 +#undef _GCC_PLUGIN_CONCAT3 +#undef _GCC_PLUGIN_STRINGIFY +#undef __GCC_PLUGIN_STRINGIFY +#undef _HAS_EXECUTE +#undef _HAS_GATE +#undef _MAKE_PASS_NAME_PASS +#undef __MAKE_PASS_NAME_PASS +#undef _PASS_NAME_NAME +#undef _PASS_NAME_PASS +#undef __PASS_NAME_PASS +#undef _PASS_NAME_PASS_DATA +#undef __PASS_NAME_PASS_DATA + +#endif /* PASS_NAME */ diff --git a/src/gcc-generate-ipa-pass.h b/src/gcc-generate-ipa-pass.h new file mode 100755 index 0000000..9bd926e --- /dev/null +++ b/src/gcc-generate-ipa-pass.h @@ -0,0 +1,289 @@ +/* + * Generator for IPA pass related boilerplate code/data + * + * Supports gcc 4.5-6 + * + * Usage: + * + * 1. before inclusion define PASS_NAME + * 2. before inclusion define NO_* for unimplemented callbacks + * NO_GENERATE_SUMMARY + * NO_READ_SUMMARY + * NO_WRITE_SUMMARY + * NO_READ_OPTIMIZATION_SUMMARY + * NO_WRITE_OPTIMIZATION_SUMMARY + * NO_STMT_FIXUP + * NO_FUNCTION_TRANSFORM + * NO_VARIABLE_TRANSFORM + * NO_GATE + * NO_EXECUTE + * 3. before inclusion define PROPERTIES_* and *TODO_FLAGS_* to override + * the default 0 values + * 4. for convenience, all the above will be undefined after inclusion! + * 5. the only exported name is make_PASS_NAME_pass() to register with gcc + */ + +#ifndef PASS_NAME +#error at least PASS_NAME must be defined +#else +#define __GCC_PLUGIN_STRINGIFY(n) #n +#define _GCC_PLUGIN_STRINGIFY(n) __GCC_PLUGIN_STRINGIFY(n) +#define _GCC_PLUGIN_CONCAT2(x, y) x ## y +#define _GCC_PLUGIN_CONCAT3(x, y, z) x ## y ## z + +#define __PASS_NAME_PASS_DATA(n) _GCC_PLUGIN_CONCAT2(n, _pass_data) +#define _PASS_NAME_PASS_DATA __PASS_NAME_PASS_DATA(PASS_NAME) + +#define __PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT2(n, _pass) +#define _PASS_NAME_PASS __PASS_NAME_PASS(PASS_NAME) + +#define _PASS_NAME_NAME _GCC_PLUGIN_STRINGIFY(PASS_NAME) + +#define __MAKE_PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT3(make_, n, _pass) +#define _MAKE_PASS_NAME_PASS __MAKE_PASS_NAME_PASS(PASS_NAME) + +#ifdef NO_GENERATE_SUMMARY +#define _GENERATE_SUMMARY NULL +#else +#define __GENERATE_SUMMARY(n) _GCC_PLUGIN_CONCAT2(n, _generate_summary) +#define _GENERATE_SUMMARY __GENERATE_SUMMARY(PASS_NAME) +#endif + +#ifdef NO_READ_SUMMARY +#define _READ_SUMMARY NULL +#else +#define __READ_SUMMARY(n) _GCC_PLUGIN_CONCAT2(n, _read_summary) +#define _READ_SUMMARY __READ_SUMMARY(PASS_NAME) +#endif + +#ifdef NO_WRITE_SUMMARY +#define _WRITE_SUMMARY NULL +#else +#define __WRITE_SUMMARY(n) _GCC_PLUGIN_CONCAT2(n, _write_summary) +#define _WRITE_SUMMARY __WRITE_SUMMARY(PASS_NAME) +#endif + +#ifdef NO_READ_OPTIMIZATION_SUMMARY +#define _READ_OPTIMIZATION_SUMMARY NULL +#else +#define __READ_OPTIMIZATION_SUMMARY(n) _GCC_PLUGIN_CONCAT2(n, _read_optimization_summary) +#define _READ_OPTIMIZATION_SUMMARY __READ_OPTIMIZATION_SUMMARY(PASS_NAME) +#endif + +#ifdef NO_WRITE_OPTIMIZATION_SUMMARY +#define _WRITE_OPTIMIZATION_SUMMARY NULL +#else +#define __WRITE_OPTIMIZATION_SUMMARY(n) _GCC_PLUGIN_CONCAT2(n, _write_optimization_summary) +#define _WRITE_OPTIMIZATION_SUMMARY __WRITE_OPTIMIZATION_SUMMARY(PASS_NAME) +#endif + +#ifdef NO_STMT_FIXUP +#define _STMT_FIXUP NULL +#else +#define __STMT_FIXUP(n) _GCC_PLUGIN_CONCAT2(n, _stmt_fixup) +#define _STMT_FIXUP __STMT_FIXUP(PASS_NAME) +#endif + +#ifdef NO_FUNCTION_TRANSFORM +#define _FUNCTION_TRANSFORM NULL +#else +#define __FUNCTION_TRANSFORM(n) _GCC_PLUGIN_CONCAT2(n, _function_transform) +#define _FUNCTION_TRANSFORM __FUNCTION_TRANSFORM(PASS_NAME) +#endif + +#ifdef NO_VARIABLE_TRANSFORM +#define _VARIABLE_TRANSFORM NULL +#else +#define __VARIABLE_TRANSFORM(n) _GCC_PLUGIN_CONCAT2(n, _variable_transform) +#define _VARIABLE_TRANSFORM __VARIABLE_TRANSFORM(PASS_NAME) +#endif + +#ifdef NO_GATE +#define _GATE NULL +#define _HAS_GATE false +#else +#define __GATE(n) _GCC_PLUGIN_CONCAT2(n, _gate) +#define _GATE __GATE(PASS_NAME) +#define _HAS_GATE true +#endif + +#ifdef NO_EXECUTE +#define _EXECUTE NULL +#define _HAS_EXECUTE false +#else +#define __EXECUTE(n) _GCC_PLUGIN_CONCAT2(n, _execute) +#define _EXECUTE __EXECUTE(PASS_NAME) +#define _HAS_EXECUTE true +#endif + +#ifndef PROPERTIES_REQUIRED +#define PROPERTIES_REQUIRED 0 +#endif + +#ifndef PROPERTIES_PROVIDED +#define PROPERTIES_PROVIDED 0 +#endif + +#ifndef PROPERTIES_DESTROYED +#define PROPERTIES_DESTROYED 0 +#endif + +#ifndef TODO_FLAGS_START +#define TODO_FLAGS_START 0 +#endif + +#ifndef TODO_FLAGS_FINISH +#define TODO_FLAGS_FINISH 0 +#endif + +#ifndef FUNCTION_TRANSFORM_TODO_FLAGS_START +#define FUNCTION_TRANSFORM_TODO_FLAGS_START 0 +#endif + +#if BUILDING_GCC_VERSION >= 4009 +namespace { +static const pass_data _PASS_NAME_PASS_DATA = { +#else +static struct ipa_opt_pass_d _PASS_NAME_PASS = { + .pass = { +#endif + .type = IPA_PASS, + .name = _PASS_NAME_NAME, +#if BUILDING_GCC_VERSION >= 4008 + .optinfo_flags = OPTGROUP_NONE, +#endif +#if BUILDING_GCC_VERSION >= 5000 +#elif BUILDING_GCC_VERSION == 4009 + .has_gate = _HAS_GATE, + .has_execute = _HAS_EXECUTE, +#else + .gate = _GATE, + .execute = _EXECUTE, + .sub = NULL, + .next = NULL, + .static_pass_number = 0, +#endif + .tv_id = TV_NONE, + .properties_required = PROPERTIES_REQUIRED, + .properties_provided = PROPERTIES_PROVIDED, + .properties_destroyed = PROPERTIES_DESTROYED, + .todo_flags_start = TODO_FLAGS_START, + .todo_flags_finish = TODO_FLAGS_FINISH, +#if BUILDING_GCC_VERSION < 4009 + }, + .generate_summary = _GENERATE_SUMMARY, + .write_summary = _WRITE_SUMMARY, + .read_summary = _READ_SUMMARY, +#if BUILDING_GCC_VERSION >= 4006 + .write_optimization_summary = _WRITE_OPTIMIZATION_SUMMARY, + .read_optimization_summary = _READ_OPTIMIZATION_SUMMARY, +#endif + .stmt_fixup = _STMT_FIXUP, + .function_transform_todo_flags_start = FUNCTION_TRANSFORM_TODO_FLAGS_START, + .function_transform = _FUNCTION_TRANSFORM, + .variable_transform = _VARIABLE_TRANSFORM, +#endif +}; + +#if BUILDING_GCC_VERSION >= 4009 +class _PASS_NAME_PASS : public ipa_opt_pass_d { +public: + _PASS_NAME_PASS() : ipa_opt_pass_d(_PASS_NAME_PASS_DATA, + g, + _GENERATE_SUMMARY, + _WRITE_SUMMARY, + _READ_SUMMARY, + _WRITE_OPTIMIZATION_SUMMARY, + _READ_OPTIMIZATION_SUMMARY, + _STMT_FIXUP, + FUNCTION_TRANSFORM_TODO_FLAGS_START, + _FUNCTION_TRANSFORM, + _VARIABLE_TRANSFORM) {} + +#ifndef NO_GATE +#if BUILDING_GCC_VERSION >= 5000 + virtual bool gate(function *) { return _GATE(); } +#else + virtual bool gate(void) { return _GATE(); } +#endif +#endif + + virtual opt_pass *clone() { return new _PASS_NAME_PASS(); } + +#ifndef NO_EXECUTE +#if BUILDING_GCC_VERSION >= 5000 + virtual unsigned int execute(function *) { return _EXECUTE(); } +#else + virtual unsigned int execute(void) { return _EXECUTE(); } +#endif +#endif +}; +} + +opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return new _PASS_NAME_PASS(); +} +#else +struct opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return &_PASS_NAME_PASS.pass; +} +#endif + +/* clean up user provided defines */ +#undef PASS_NAME +#undef NO_GENERATE_SUMMARY +#undef NO_WRITE_SUMMARY +#undef NO_READ_SUMMARY +#undef NO_WRITE_OPTIMIZATION_SUMMARY +#undef NO_READ_OPTIMIZATION_SUMMARY +#undef NO_STMT_FIXUP +#undef NO_FUNCTION_TRANSFORM +#undef NO_VARIABLE_TRANSFORM +#undef NO_GATE +#undef NO_EXECUTE + +#undef FUNCTION_TRANSFORM_TODO_FLAGS_START +#undef PROPERTIES_DESTROYED +#undef PROPERTIES_PROVIDED +#undef PROPERTIES_REQUIRED +#undef TODO_FLAGS_FINISH +#undef TODO_FLAGS_START + +/* clean up generated defines */ +#undef _EXECUTE +#undef __EXECUTE +#undef _FUNCTION_TRANSFORM +#undef __FUNCTION_TRANSFORM +#undef _GATE +#undef __GATE +#undef _GCC_PLUGIN_CONCAT2 +#undef _GCC_PLUGIN_CONCAT3 +#undef _GCC_PLUGIN_STRINGIFY +#undef __GCC_PLUGIN_STRINGIFY +#undef _GENERATE_SUMMARY +#undef __GENERATE_SUMMARY +#undef _HAS_EXECUTE +#undef _HAS_GATE +#undef _MAKE_PASS_NAME_PASS +#undef __MAKE_PASS_NAME_PASS +#undef _PASS_NAME_NAME +#undef _PASS_NAME_PASS +#undef __PASS_NAME_PASS +#undef _PASS_NAME_PASS_DATA +#undef __PASS_NAME_PASS_DATA +#undef _READ_OPTIMIZATION_SUMMARY +#undef __READ_OPTIMIZATION_SUMMARY +#undef _READ_SUMMARY +#undef __READ_SUMMARY +#undef _STMT_FIXUP +#undef __STMT_FIXUP +#undef _VARIABLE_TRANSFORM +#undef __VARIABLE_TRANSFORM +#undef _WRITE_OPTIMIZATION_SUMMARY +#undef __WRITE_OPTIMIZATION_SUMMARY +#undef _WRITE_SUMMARY +#undef __WRITE_SUMMARY + +#endif /* PASS_NAME */ diff --git a/src/gcc-generate-rtl-pass.h b/src/gcc-generate-rtl-pass.h new file mode 100755 index 0000000..1dc67a5 --- /dev/null +++ b/src/gcc-generate-rtl-pass.h @@ -0,0 +1,175 @@ +/* + * Generator for RTL pass related boilerplate code/data + * + * Supports gcc 4.5-6 + * + * Usage: + * + * 1. before inclusion define PASS_NAME + * 2. before inclusion define NO_* for unimplemented callbacks + * NO_GATE + * NO_EXECUTE + * 3. before inclusion define PROPERTIES_* and TODO_FLAGS_* to override + * the default 0 values + * 4. for convenience, all the above will be undefined after inclusion! + * 5. the only exported name is make_PASS_NAME_pass() to register with gcc + */ + +#ifndef PASS_NAME +#error at least PASS_NAME must be defined +#else +#define __GCC_PLUGIN_STRINGIFY(n) #n +#define _GCC_PLUGIN_STRINGIFY(n) __GCC_PLUGIN_STRINGIFY(n) +#define _GCC_PLUGIN_CONCAT2(x, y) x ## y +#define _GCC_PLUGIN_CONCAT3(x, y, z) x ## y ## z + +#define __PASS_NAME_PASS_DATA(n) _GCC_PLUGIN_CONCAT2(n, _pass_data) +#define _PASS_NAME_PASS_DATA __PASS_NAME_PASS_DATA(PASS_NAME) + +#define __PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT2(n, _pass) +#define _PASS_NAME_PASS __PASS_NAME_PASS(PASS_NAME) + +#define _PASS_NAME_NAME _GCC_PLUGIN_STRINGIFY(PASS_NAME) + +#define __MAKE_PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT3(make_, n, _pass) +#define _MAKE_PASS_NAME_PASS __MAKE_PASS_NAME_PASS(PASS_NAME) + +#ifdef NO_GATE +#define _GATE NULL +#define _HAS_GATE false +#else +#define __GATE(n) _GCC_PLUGIN_CONCAT2(n, _gate) +#define _GATE __GATE(PASS_NAME) +#define _HAS_GATE true +#endif + +#ifdef NO_EXECUTE +#define _EXECUTE NULL +#define _HAS_EXECUTE false +#else +#define __EXECUTE(n) _GCC_PLUGIN_CONCAT2(n, _execute) +#define _EXECUTE __EXECUTE(PASS_NAME) +#define _HAS_EXECUTE true +#endif + +#ifndef PROPERTIES_REQUIRED +#define PROPERTIES_REQUIRED 0 +#endif + +#ifndef PROPERTIES_PROVIDED +#define PROPERTIES_PROVIDED 0 +#endif + +#ifndef PROPERTIES_DESTROYED +#define PROPERTIES_DESTROYED 0 +#endif + +#ifndef TODO_FLAGS_START +#define TODO_FLAGS_START 0 +#endif + +#ifndef TODO_FLAGS_FINISH +#define TODO_FLAGS_FINISH 0 +#endif + +#if BUILDING_GCC_VERSION >= 4009 +namespace { +static const pass_data _PASS_NAME_PASS_DATA = { +#else +static struct rtl_opt_pass _PASS_NAME_PASS = { + .pass = { +#endif + .type = RTL_PASS, + .name = _PASS_NAME_NAME, +#if BUILDING_GCC_VERSION >= 4008 + .optinfo_flags = OPTGROUP_NONE, +#endif +#if BUILDING_GCC_VERSION >= 5000 +#elif BUILDING_GCC_VERSION == 4009 + .has_gate = _HAS_GATE, + .has_execute = _HAS_EXECUTE, +#else + .gate = _GATE, + .execute = _EXECUTE, + .sub = NULL, + .next = NULL, + .static_pass_number = 0, +#endif + .tv_id = TV_NONE, + .properties_required = PROPERTIES_REQUIRED, + .properties_provided = PROPERTIES_PROVIDED, + .properties_destroyed = PROPERTIES_DESTROYED, + .todo_flags_start = TODO_FLAGS_START, + .todo_flags_finish = TODO_FLAGS_FINISH, +#if BUILDING_GCC_VERSION < 4009 + } +#endif +}; + +#if BUILDING_GCC_VERSION >= 4009 +class _PASS_NAME_PASS : public rtl_opt_pass { +public: + _PASS_NAME_PASS() : rtl_opt_pass(_PASS_NAME_PASS_DATA, g) {} + +#ifndef NO_GATE +#if BUILDING_GCC_VERSION >= 5000 + virtual bool gate(function *) { return _GATE(); } +#else + virtual bool gate(void) { return _GATE(); } +#endif +#endif + + virtual opt_pass *clone() { return new _PASS_NAME_PASS(); } + +#ifndef NO_EXECUTE +#if BUILDING_GCC_VERSION >= 5000 + virtual unsigned int execute(function *) { return _EXECUTE(); } +#else + virtual unsigned int execute(void) { return _EXECUTE(); } +#endif +#endif +}; +} + +opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return new _PASS_NAME_PASS(); +} +#else +struct opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return &_PASS_NAME_PASS.pass; +} +#endif + +/* clean up user provided defines */ +#undef PASS_NAME +#undef NO_GATE +#undef NO_EXECUTE + +#undef PROPERTIES_DESTROYED +#undef PROPERTIES_PROVIDED +#undef PROPERTIES_REQUIRED +#undef TODO_FLAGS_FINISH +#undef TODO_FLAGS_START + +/* clean up generated defines */ +#undef _EXECUTE +#undef __EXECUTE +#undef _GATE +#undef __GATE +#undef _GCC_PLUGIN_CONCAT2 +#undef _GCC_PLUGIN_CONCAT3 +#undef _GCC_PLUGIN_STRINGIFY +#undef __GCC_PLUGIN_STRINGIFY +#undef _HAS_EXECUTE +#undef _HAS_GATE +#undef _MAKE_PASS_NAME_PASS +#undef __MAKE_PASS_NAME_PASS +#undef _PASS_NAME_NAME +#undef _PASS_NAME_PASS +#undef __PASS_NAME_PASS +#undef _PASS_NAME_PASS_DATA +#undef __PASS_NAME_PASS_DATA + +#endif /* PASS_NAME */ diff --git a/src/gcc-generate-simple_ipa-pass.h b/src/gcc-generate-simple_ipa-pass.h new file mode 100755 index 0000000..a27e2b3 --- /dev/null +++ b/src/gcc-generate-simple_ipa-pass.h @@ -0,0 +1,175 @@ +/* + * Generator for SIMPLE_IPA pass related boilerplate code/data + * + * Supports gcc 4.5-6 + * + * Usage: + * + * 1. before inclusion define PASS_NAME + * 2. before inclusion define NO_* for unimplemented callbacks + * NO_GATE + * NO_EXECUTE + * 3. before inclusion define PROPERTIES_* and TODO_FLAGS_* to override + * the default 0 values + * 4. for convenience, all the above will be undefined after inclusion! + * 5. the only exported name is make_PASS_NAME_pass() to register with gcc + */ + +#ifndef PASS_NAME +#error at least PASS_NAME must be defined +#else +#define __GCC_PLUGIN_STRINGIFY(n) #n +#define _GCC_PLUGIN_STRINGIFY(n) __GCC_PLUGIN_STRINGIFY(n) +#define _GCC_PLUGIN_CONCAT2(x, y) x ## y +#define _GCC_PLUGIN_CONCAT3(x, y, z) x ## y ## z + +#define __PASS_NAME_PASS_DATA(n) _GCC_PLUGIN_CONCAT2(n, _pass_data) +#define _PASS_NAME_PASS_DATA __PASS_NAME_PASS_DATA(PASS_NAME) + +#define __PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT2(n, _pass) +#define _PASS_NAME_PASS __PASS_NAME_PASS(PASS_NAME) + +#define _PASS_NAME_NAME _GCC_PLUGIN_STRINGIFY(PASS_NAME) + +#define __MAKE_PASS_NAME_PASS(n) _GCC_PLUGIN_CONCAT3(make_, n, _pass) +#define _MAKE_PASS_NAME_PASS __MAKE_PASS_NAME_PASS(PASS_NAME) + +#ifdef NO_GATE +#define _GATE NULL +#define _HAS_GATE false +#else +#define __GATE(n) _GCC_PLUGIN_CONCAT2(n, _gate) +#define _GATE __GATE(PASS_NAME) +#define _HAS_GATE true +#endif + +#ifdef NO_EXECUTE +#define _EXECUTE NULL +#define _HAS_EXECUTE false +#else +#define __EXECUTE(n) _GCC_PLUGIN_CONCAT2(n, _execute) +#define _EXECUTE __EXECUTE(PASS_NAME) +#define _HAS_EXECUTE true +#endif + +#ifndef PROPERTIES_REQUIRED +#define PROPERTIES_REQUIRED 0 +#endif + +#ifndef PROPERTIES_PROVIDED +#define PROPERTIES_PROVIDED 0 +#endif + +#ifndef PROPERTIES_DESTROYED +#define PROPERTIES_DESTROYED 0 +#endif + +#ifndef TODO_FLAGS_START +#define TODO_FLAGS_START 0 +#endif + +#ifndef TODO_FLAGS_FINISH +#define TODO_FLAGS_FINISH 0 +#endif + +#if BUILDING_GCC_VERSION >= 4009 +namespace { +static const pass_data _PASS_NAME_PASS_DATA = { +#else +static struct simple_ipa_opt_pass _PASS_NAME_PASS = { + .pass = { +#endif + .type = SIMPLE_IPA_PASS, + .name = _PASS_NAME_NAME, +#if BUILDING_GCC_VERSION >= 4008 + .optinfo_flags = OPTGROUP_NONE, +#endif +#if BUILDING_GCC_VERSION >= 5000 +#elif BUILDING_GCC_VERSION == 4009 + .has_gate = _HAS_GATE, + .has_execute = _HAS_EXECUTE, +#else + .gate = _GATE, + .execute = _EXECUTE, + .sub = NULL, + .next = NULL, + .static_pass_number = 0, +#endif + .tv_id = TV_NONE, + .properties_required = PROPERTIES_REQUIRED, + .properties_provided = PROPERTIES_PROVIDED, + .properties_destroyed = PROPERTIES_DESTROYED, + .todo_flags_start = TODO_FLAGS_START, + .todo_flags_finish = TODO_FLAGS_FINISH, +#if BUILDING_GCC_VERSION < 4009 + } +#endif +}; + +#if BUILDING_GCC_VERSION >= 4009 +class _PASS_NAME_PASS : public simple_ipa_opt_pass { +public: + _PASS_NAME_PASS() : simple_ipa_opt_pass(_PASS_NAME_PASS_DATA, g) {} + +#ifndef NO_GATE +#if BUILDING_GCC_VERSION >= 5000 + virtual bool gate(function *) { return _GATE(); } +#else + virtual bool gate(void) { return _GATE(); } +#endif +#endif + + virtual opt_pass *clone() { return new _PASS_NAME_PASS(); } + +#ifndef NO_EXECUTE +#if BUILDING_GCC_VERSION >= 5000 + virtual unsigned int execute(function *) { return _EXECUTE(); } +#else + virtual unsigned int execute(void) { return _EXECUTE(); } +#endif +#endif +}; +} + +opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return new _PASS_NAME_PASS(); +} +#else +struct opt_pass *_MAKE_PASS_NAME_PASS(void) +{ + return &_PASS_NAME_PASS.pass; +} +#endif + +/* clean up user provided defines */ +#undef PASS_NAME +#undef NO_GATE +#undef NO_EXECUTE + +#undef PROPERTIES_DESTROYED +#undef PROPERTIES_PROVIDED +#undef PROPERTIES_REQUIRED +#undef TODO_FLAGS_FINISH +#undef TODO_FLAGS_START + +/* clean up generated defines */ +#undef _EXECUTE +#undef __EXECUTE +#undef _GATE +#undef __GATE +#undef _GCC_PLUGIN_CONCAT2 +#undef _GCC_PLUGIN_CONCAT3 +#undef _GCC_PLUGIN_STRINGIFY +#undef __GCC_PLUGIN_STRINGIFY +#undef _HAS_EXECUTE +#undef _HAS_GATE +#undef _MAKE_PASS_NAME_PASS +#undef __MAKE_PASS_NAME_PASS +#undef _PASS_NAME_NAME +#undef _PASS_NAME_PASS +#undef __PASS_NAME_PASS +#undef _PASS_NAME_PASS_DATA +#undef __PASS_NAME_PASS_DATA + +#endif /* PASS_NAME */ diff --git a/src/rap.h b/src/rap.h new file mode 100755 index 0000000..f0e20ee --- /dev/null +++ b/src/rap.h @@ -0,0 +1,45 @@ +#ifndef RAP_H_INCLUDED +#define RAP_H_INCLUDED + +#include "gcc-common.h" + +typedef struct { + int hash; // will be sign extended to long in reality +} rap_hash_t; + +typedef struct { + unsigned int qual_const:1; + unsigned int qual_volatile:1; +} rap_hash_flags_t; +extern rap_hash_flags_t imprecise_rap_hash_flags; + +extern bool report_fptr_hash; + +extern GTY(()) tree rap_hash_type_node; +extern const char *rap_abort_ret; +extern const char *rap_abort_call; +extern bool enable_type_ret, enable_type_call; + +void siphash24fold(unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *k); +void rap_calculate_func_hashes(void *event_data, void *data); +rap_hash_t rap_hash_function_type(const_tree fntype, rap_hash_flags_t flags); +rap_hash_t rap_hash_function_decl(const_tree fndecl, rap_hash_flags_t flags); +rap_hash_t rap_hash_function_node_imprecise(cgraph_node_ptr node); +tree get_rap_hash(gimple_seq *stmts, location_t loc, tree fptr, HOST_WIDE_INT rap_hash_offset); +const_tree type_name(const_tree type); +tree create_new_var(tree type, const char *name); + +gimple barrier(tree var, bool full); +bool rap_cmodel_check(void); + +#if BUILDING_GCC_VERSION >= 4009 +opt_pass *make_rap_ret_pass(void); +opt_pass *make_rap_fptr_pass(void); +opt_pass *make_rap_mark_retloc_pass(void); +#else +struct opt_pass *make_rap_ret_pass(void); +struct opt_pass *make_rap_fptr_pass(void); +struct opt_pass *make_rap_mark_retloc_pass(void); +#endif + +#endif diff --git a/src/rap_fptr_pass.c b/src/rap_fptr_pass.c new file mode 100755 index 0000000..09e1377 --- /dev/null +++ b/src/rap_fptr_pass.c @@ -0,0 +1,278 @@ +/* + * Copyright 2012-2017 by PaX Team + * Licensed under the GPL v2 + * + * Homepage: http://pax.grsecurity.net/ + */ + +#include "rap.h" + +bool report_fptr_hash; + +static bool rap_fptr_gate(void) +{ + return rap_cmodel_check(); +} + +static tree build_rap_hash(gimple call_stmt, tree fntype) +{ + rap_hash_t hash; + + hash = rap_hash_function_type(fntype, imprecise_rap_hash_flags); + if (report_fptr_hash) + inform(gimple_location(call_stmt), "fptr rap_hash: %x", hash.hash); + return build_int_cst_type(rap_hash_type_node, hash.hash); +} + +// extract the rap_hash stored at an offset from ptr +tree get_rap_hash(gimple_seq *stmts, location_t loc, tree fptr, HOST_WIDE_INT rap_hash_offset) +{ + gimple assign_hash; + tree target_hash; +#if BUILDING_GCC_VERSION == 4005 + tree fptr2; +#endif + + // target_hash = *(long*)((void*)fptr + rap_hash_offset) + target_hash = create_tmp_var(rap_hash_type_node, "rap_hash"); + add_referenced_var(target_hash); + target_hash = make_ssa_name(target_hash, NULL); +#if BUILDING_GCC_VERSION == 4005 + fptr2 = create_tmp_var(ptr_type_node, "rap_fptr2"); + fptr2 = make_ssa_name(fptr2, NULL); + assign_hash = gimple_build_assign(fptr2, build2(POINTER_PLUS_EXPR, ptr_type_node, fptr, build_int_cst_type(sizetype, rap_hash_offset))); + gimple_set_location(assign_hash, loc); + SSA_NAME_DEF_STMT(fptr2) = assign_hash; + gimple_seq_add_stmt(stmts, assign_hash); + fptr = gimple_get_lhs(assign_hash); + + fptr2 = create_tmp_var(build_pointer_type(rap_hash_type_node), "rap_fptr2"); + fptr2 = make_ssa_name(fptr2, NULL); + assign_hash = gimple_build_assign(fptr2, fold_convert(build_pointer_type(TREE_TYPE(target_hash)), fptr)); + gimple_set_location(assign_hash, loc); + SSA_NAME_DEF_STMT(fptr2) = assign_hash; + gimple_seq_add_stmt(stmts, assign_hash); + fptr = gimple_get_lhs(assign_hash); + + assign_hash = gimple_build_assign(target_hash, build1(INDIRECT_REF, rap_hash_type_node, fptr)); +#else + assign_hash = gimple_build_assign(target_hash, build2(MEM_REF, rap_hash_type_node, fptr, build_int_cst_type(build_pointer_type(rap_hash_type_node), rap_hash_offset))); +#endif + gimple_set_location(assign_hash, loc); + SSA_NAME_DEF_STMT(target_hash) = assign_hash; + gimple_seq_add_stmt(stmts, assign_hash); + + return target_hash; +} + +static void rap_mark_retloc(gimple_stmt_iterator *gsi, tree computed_hash) +{ + gimple stmt; + VEC(tree, gc) *inputs = NULL; + VEC(tree, gc) *clobbers = NULL; + tree input, clobber, fndecl; + + fndecl = gimple_call_fndecl(gsi_stmt(*gsi)); + if (fndecl && is_simple_builtin(fndecl)) + return; + + input = build_tree_list(NULL_TREE, build_const_char_string(2, "i")); + input = chainon(NULL_TREE, build_tree_list(input, computed_hash)); + VEC_safe_push(tree, gc, inputs, input); + + clobber = build_tree_list(NULL_TREE, build_const_char_string(3, "cx")); + VEC_safe_push(tree, gc, clobbers, clobber); + + stmt = gimple_build_asm_vec("", inputs, NULL, NULL, NULL); + gimple_asm_set_volatile(as_a_gasm(stmt), true); + gimple_set_location(stmt, gimple_location(gsi_stmt(*gsi))); + gsi_insert_before(gsi, stmt, GSI_SAME_STMT); +} + +// check the function hash of the target of the fptr +static void rap_instrument_fptr(gimple_stmt_iterator *gsi) +{ + gimple check_hash, call_stmt, stmt; + gimple_seq stmts = NULL; + location_t loc; + tree computed_hash, target_hash, fptr, fntype; + basic_block cond_bb, join_bb, true_bb; + edge e; + HOST_WIDE_INT rap_hash_offset; + + call_stmt = gsi_stmt(*gsi); + loc = gimple_location(call_stmt); + fptr = gimple_call_fn(call_stmt); + fntype = TREE_TYPE(TREE_TYPE(fptr)); + + if (TREE_CODE(fntype) == FUNCTION_TYPE) { + computed_hash = build_rap_hash(call_stmt, fntype); + } else { + debug_tree(fntype); + gcc_unreachable(); + } + + if (UNITS_PER_WORD == 8) + rap_hash_offset = 2 * sizeof(rap_hash_t); + else if (UNITS_PER_WORD == 4) + rap_hash_offset = sizeof(rap_hash_t); + else + gcc_unreachable(); + + target_hash = get_rap_hash(&stmts, loc, fptr, -rap_hash_offset); + gsi_insert_seq_before(gsi, stmts, GSI_SAME_STMT); + + // compare target_hash against computed function hash + // bail out on mismatch + check_hash = gimple_build_cond(NE_EXPR, target_hash, computed_hash, NULL_TREE, NULL_TREE); + gimple_set_location(check_hash, loc); + gsi_insert_before(gsi, check_hash, GSI_NEW_STMT); + + cond_bb = gimple_bb(gsi_stmt(*gsi)); + gcc_assert(!gsi_end_p(*gsi)); + gcc_assert(check_hash == gsi_stmt(*gsi)); + + e = split_block(cond_bb, gsi_stmt(*gsi)); + cond_bb = e->src; + join_bb = e->dest; + e->flags = EDGE_FALSE_VALUE; + e->probability = REG_BR_PROB_BASE; + + true_bb = create_empty_bb(EXIT_BLOCK_PTR_FOR_FN(cfun)->prev_bb); + make_edge(cond_bb, true_bb, EDGE_TRUE_VALUE | EDGE_PRESERVE); + + gcc_assert(dom_info_available_p(CDI_DOMINATORS)); + set_immediate_dominator(CDI_DOMINATORS, true_bb, cond_bb); + set_immediate_dominator(CDI_DOMINATORS, join_bb, cond_bb); + + gcc_assert(cond_bb->loop_father == join_bb->loop_father); + add_bb_to_loop(true_bb, cond_bb->loop_father); + + *gsi = gsi_start_bb(true_bb); + + if (rap_abort_call) { + stmt = gimple_build_asm_vec(rap_abort_call, NULL, NULL, NULL, NULL); + gimple_asm_set_volatile(as_a_gasm(stmt), true); + gimple_set_location(stmt, loc); + gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING); + + stmt = gimple_build_call(builtin_decl_implicit(BUILT_IN_UNREACHABLE), 0); + } else { + // this fake dependency is to prevent PRE from merging this BB with others of the same kind + stmt = barrier(fptr, false); + gimple_set_location(stmt, loc); + gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING); + + stmt = gimple_build_call(builtin_decl_implicit(BUILT_IN_TRAP), 0); + } + + gimple_set_location(stmt, loc); + gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING); + + *gsi = gsi_start_bb(join_bb); +} + +// find all language level function pointer dereferences and verify the target function +static unsigned int rap_fptr_execute(void) +{ + basic_block bb; + + loop_optimizer_init(LOOPS_NORMAL | LOOPS_HAVE_RECORDED_EXITS); + gcc_assert(current_loops); + + calculate_dominance_info(CDI_DOMINATORS); + calculate_dominance_info(CDI_POST_DOMINATORS); + + // 1. loop through BBs and GIMPLE statements + FOR_EACH_BB_FN(bb, cfun) { + gimple_stmt_iterator gsi; + + for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) { + // gimple match: h_1 = get_fptr (); D.2709_3 = h_1 (x_2(D)); + tree fptr, fntype, computed_hash; + gimple call_stmt; + rap_hash_t hash; + + // is it a call ... + call_stmt = gsi_stmt(gsi); + if (!is_gimple_call(call_stmt)) + continue; + + fptr = gimple_call_fn(call_stmt); + if (!fptr) + continue; + + switch (TREE_CODE(fptr)) { + default: + debug_gimple_stmt(call_stmt); + debug_tree(fptr); + debug_tree(TREE_TYPE(fptr)); + gcc_unreachable(); + + case ADDR_EXPR: + hash = rap_hash_function_type(TREE_TYPE(TREE_OPERAND(fptr, 0)), imprecise_rap_hash_flags); + computed_hash = build_int_cst_type(rap_hash_type_node, -hash.hash); + rap_mark_retloc(&gsi, computed_hash); + continue; + + case SSA_NAME: + if (SSA_NAME_VAR(fptr) == NULL_TREE) + break; + + switch (TREE_CODE(SSA_NAME_VAR(fptr))) { + default: + debug_gimple_stmt(call_stmt); + debug_tree(fptr); + gcc_unreachable(); + + case VAR_DECL: + case PARM_DECL: + break; + } + break; + + case INTEGER_CST: + case OBJ_TYPE_REF: + break; + } + + // ... through a function pointer + fntype = TREE_TYPE(fptr); + if (TREE_CODE(fntype) != POINTER_TYPE) { + gcc_assert(TREE_CODE(fntype) == FUNCTION_TYPE || TREE_CODE(fntype) == METHOD_TYPE); + hash = rap_hash_function_type(fntype, imprecise_rap_hash_flags); + computed_hash = build_int_cst_type(rap_hash_type_node, -hash.hash); + rap_mark_retloc(&gsi, computed_hash); + continue; + } + + fntype = TREE_TYPE(fntype); + gcc_assert(TREE_CODE(fntype) == FUNCTION_TYPE || TREE_CODE(fntype) == METHOD_TYPE); + + if (enable_type_call) { + rap_instrument_fptr(&gsi); + bb = gsi_bb(gsi); + gcc_assert(call_stmt == gsi_stmt(gsi)); + } + + if (enable_type_ret) { + hash = rap_hash_function_type(fntype, imprecise_rap_hash_flags); + computed_hash = build_int_cst_type(rap_hash_type_node, -hash.hash); + rap_mark_retloc(&gsi, computed_hash); + } + + bb = gsi_bb(gsi); + gcc_assert(call_stmt == gsi_stmt(gsi)); + } + } + + free_dominance_info(CDI_DOMINATORS); + free_dominance_info(CDI_POST_DOMINATORS); + loop_optimizer_finalize(); + + return 0; +} + +#define PASS_NAME rap_fptr +#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func | TODO_remove_unused_locals | TODO_update_ssa | TODO_cleanup_cfg | TODO_rebuild_cgraph_edges | TODO_verify_flow +#include "gcc-generate-gimple-pass.h" diff --git a/src/rap_hash.c b/src/rap_hash.c new file mode 100755 index 0000000..161102f --- /dev/null +++ b/src/rap_hash.c @@ -0,0 +1,380 @@ +/* + * Copyright 2012-2016 by PaX Team + * Licensed under the GPL v2 + * + * Homepage: http://pax.grsecurity.net/ + */ + +#include "rap.h" + +static rap_hash_t *rap_imprecise_hashes; +static int rap_cgraph_max_uid; + +static void rap_hash_function(const_tree fntype, rap_hash_flags_t flags, unsigned char sip_hash[8]); + +static const unsigned char rap_hash_tree_code[MAX_TREE_CODES] = { + [0] = 0, + [1] = 0, + [2] = 0, + [3] = 0, + [4] = 0, + [OFFSET_TYPE] = 10, + [ENUMERAL_TYPE] = 20, + [BOOLEAN_TYPE] = 30, + [INTEGER_TYPE] = 40, + [REAL_TYPE] = 50, + [POINTER_TYPE] = 60, + [REFERENCE_TYPE] = 70, +#if BUILDING_GCC_VERSION >= 4006 + [NULLPTR_TYPE] = 80, +#endif + [FIXED_POINT_TYPE] = 0, + [COMPLEX_TYPE] = 100, + [VECTOR_TYPE] = 110, + [ARRAY_TYPE] = 120, + [RECORD_TYPE] = 130, + [UNION_TYPE] = 140, + [QUAL_UNION_TYPE] = 0, + [VOID_TYPE] = 160, +#if BUILDING_GCC_VERSION >= 5000 + [POINTER_BOUNDS_TYPE] = 170, +#endif + [FUNCTION_TYPE] = 180, + [METHOD_TYPE] = 0, + [LANG_TYPE] = 0, +}; + +static void rap_fold_hash(unsigned char *out, const unsigned char *in, unsigned long long inlen) +{ + static const unsigned char rap_sip_key[16] = { + 'P', 'a', 'X', ' ', 'T', 'e', 'a', 'm', + 'R', 'A', 'P', ' ', 'H', 'A', 'S', 'H', + }; + + siphash24fold(out, in, inlen, rap_sip_key); +} + +// compute the final hash value in the range [1,INT_MAX] +// the % and +1 trick leaves the value 0 available for marking non-indirectly callable functions +// and INT_MIN (0x80000000) for longjmp targets (sign extended) +// return places will use the (sign extended) range [INT_MIN+1,-1] ([0x8000001,0xffffffff]) +static rap_hash_t rap_extract_hash(const unsigned char sip_hash[8]) +{ + rap_hash_t hash; + unsigned long long dividend, divisor; + + memcpy(÷nd, sip_hash, sizeof dividend); +// divisor = 1ULL << (sizeof hash * 8 - 1); +// divisor |= divisor - 1; + divisor = 0x7fffffffUL; + hash.hash = dividend % divisor + 1; + return hash; +} + +static void rap_hash_type_name(const_tree type, unsigned char sip_hash[8]) +{ + const_tree name = type_name(TYPE_MAIN_VARIANT(type)); + + // handle typedefs of anonymous structs/unions + if (name == NULL_TREE) + name = type_name(type); + + if (name == NULL_TREE) + return; + + gcc_assert(TREE_CODE(name) == IDENTIFIER_NODE); + rap_fold_hash(sip_hash, (const unsigned char *)IDENTIFIER_POINTER(name), IDENTIFIER_LENGTH(name)); +} + +static void rap_hash_type_precision(const_tree type, unsigned char sip_hash[8]) +{ + unsigned HOST_WIDE_INT size; + + gcc_assert(TYPE_PRECISION(type)); + + size = TYPE_PRECISION(type); + rap_fold_hash(sip_hash, (const unsigned char *)&size, sizeof size); +} + +const_tree type_name(const_tree type) +{ + const_tree name; + + name = TYPE_NAME(type); + if (!name) + return NULL_TREE; + + switch (TREE_CODE(name)) { + case IDENTIFIER_NODE: + return name; + + case TYPE_DECL: + gcc_assert(DECL_NAME(name)); + return DECL_NAME(name); + + default: + gcc_unreachable(); + } +} + +// the core computation of the rap hash +// the first piece is a (hopefully) compiler independent encondig of the type, derived from the gcc tree code +// the second piece is type specific information, such as the size, qualifiers, (recursively) referenced types, etc +static void rap_hash_tree(const_tree type, rap_hash_flags_t flags, unsigned char sip_hash[8]) +{ + enum tree_code code; + unsigned int attrs; + + code = TREE_CODE(type); + attrs = rap_hash_tree_code[code]; + if (!attrs) { + fprintf(stderr, "unhandled tree_code %s %d\n", get_tree_code_name(code), code); + debug_tree(type); + gcc_unreachable(); + } + rap_fold_hash(sip_hash, (const unsigned char *)&attrs, sizeof attrs); + + enum { + // attrs layout for + // - all types: + RAP_HASH_VOLATILE = 1U << 31, + RAP_HASH_NOT_VOLATILE = 1U << 30, + RAP_HASH_CONST = 1U << 29, + RAP_HASH_NOT_CONST = 1U << 28, + + // - pointer types: + RAP_HASH_RESTRICT = 1U << 27, + RAP_HASH_NOT_RESTRICT = 1U << 26, + + // - C integer types: + RAP_HASH_UNSIGNED = 1U << 25, + RAP_HASH_SIGNED = 1U << 24, + + RAP_HASH_UNQUALIFIED_CHAR = 1U << 23, + RAP_HASH_CHAR = 1U << 22, + RAP_HASH_SHORT = 1U << 21, + RAP_HASH_INT = 1U << 20, + RAP_HASH_LONG = 1U << 19, + RAP_HASH_LONG_LONG = 1U << 18, + RAP_HASH_WCHAR = 1U << 17, + RAP_HASH_CHAR16 = 1U << 16, + RAP_HASH_CHAR32 = 1U << 15, + + // - C float types + RAP_HASH_FLOAT = 1U << 14, + RAP_HASH_DOUBLE = 1U << 13, + RAP_HASH_LONG_DOUBLE = 1U << 12, + RAP_HASH_DFLOAT32 = 1U << 11, + RAP_HASH_DFLOAT64 = 1U << 10, + RAP_HASH_DFLOAT128 = 1U << 9, + }; + + attrs = 0; + if (flags.qual_volatile) + attrs |= TYPE_VOLATILE(type) ? RAP_HASH_VOLATILE : RAP_HASH_NOT_VOLATILE; + if (flags.qual_const) + attrs |= TYPE_READONLY(type) ? RAP_HASH_CONST : RAP_HASH_NOT_CONST; + + switch (code) { + default: + debug_tree(type); + gcc_unreachable(); + break; + + case VOID_TYPE: + break; + + case OFFSET_TYPE: + rap_hash_tree(TREE_TYPE(type), flags, sip_hash); + rap_hash_tree(TYPE_OFFSET_BASETYPE(type), flags, sip_hash); + break; + + case FUNCTION_TYPE: + rap_hash_function(type, flags, sip_hash); + break; + + case RECORD_TYPE: + rap_hash_type_name(type, sip_hash); + break; + + case UNION_TYPE: + rap_hash_type_name(type, sip_hash); + break; + + case POINTER_TYPE: + case REFERENCE_TYPE: + rap_hash_tree(TREE_TYPE(type), flags, sip_hash); + break; + + case VECTOR_TYPE: + rap_hash_tree(TREE_TYPE(type), flags, sip_hash); + rap_hash_type_precision(TREE_TYPE(type), sip_hash); + break; + + case ARRAY_TYPE: + rap_hash_tree(TREE_TYPE(type), flags, sip_hash); + break; + + case REAL_TYPE: { + const_tree main_variant = TYPE_MAIN_VARIANT(type); + + switch (TYPE_PRECISION(main_variant)) { + default: + debug_tree(type); + debug_tree(TYPE_MAIN_VARIANT(type)); + gcc_unreachable(); + + case 32: +// attrs |= RAP_HASH_FLOAT; + break; + + case 64: +// attrs |= RAP_HASH_DOUBLE; + break; + + case 80: + case 128: + attrs |= RAP_HASH_LONG_DOUBLE; + break; + } + rap_hash_type_precision(main_variant, sip_hash); + break; + } + + case ENUMERAL_TYPE: + rap_hash_type_name(type, sip_hash); + case BOOLEAN_TYPE: + rap_hash_type_precision(type, sip_hash); + break; + + case INTEGER_TYPE: { + attrs |= TYPE_UNSIGNED(type) ? RAP_HASH_UNSIGNED : RAP_HASH_SIGNED; + rap_hash_type_precision(type, sip_hash); + break; + } + } + + rap_fold_hash(sip_hash, (const unsigned char *)&attrs, sizeof attrs); +} + +static const_tree rap_dequal_argtype(const_tree argtype) +{ + // since gcc/tree.c:free_lang_data_in_type removes const/volatile from the top level param decl + // we have to simulate it here as this can be called earlier from the frontend as well + if (TYPE_READONLY(argtype) || TYPE_VOLATILE(argtype)) { + int quals; + + quals = TYPE_QUALS(argtype) & ~TYPE_QUAL_CONST & ~TYPE_QUAL_VOLATILE; + argtype = build_qualified_type(CONST_CAST_TREE(argtype), quals); + } + + return argtype; +} + +// main function to compute the rap hash for function types +// while virtual class methods are always replaced with their ancestor, +// callers can decide whether to fully utilize that information via flags.method_ancestor +static void rap_hash_function(const_tree fntype, rap_hash_flags_t flags, unsigned char sip_hash[8]) +{ + function_args_iterator args_iter; + const_tree arg; + + switch (TREE_CODE(fntype)) { + default: + debug_tree(fntype); + gcc_unreachable(); + + case FUNCTION_TYPE: + // 1. hash the result + rap_hash_tree(TREE_TYPE(fntype), flags, sip_hash); + + // 2. hash the function parameters + FOREACH_FUNCTION_ARGS(fntype, arg, args_iter) { + const_tree argtype = arg; + + argtype = rap_dequal_argtype(argtype); + rap_hash_tree(argtype, flags, sip_hash); + } + break; + } +} + +rap_hash_t rap_hash_function_type(const_tree fntype, rap_hash_flags_t flags) +{ + unsigned char sip_hash[8] = { }; + rap_hash_t hash; + + rap_hash_function(fntype, flags, sip_hash); + hash = rap_extract_hash(sip_hash); + + gcc_assert(hash.hash); + return hash; +} + +rap_hash_t rap_hash_function_decl(const_tree fndecl, rap_hash_flags_t flags) +{ + tree fntype; + + gcc_assert(TREE_CODE(fndecl) == FUNCTION_DECL); + fntype = TREE_TYPE(fndecl); + + switch (TREE_CODE(fntype)) { + default: + debug_tree(fndecl); + gcc_unreachable(); + + case FUNCTION_TYPE: + return rap_hash_function_type(fntype, flags); + } +} + +rap_hash_t rap_hash_function_node_imprecise(cgraph_node_ptr node) +{ + rap_hash_t hash; + tree fndecl; + + gcc_assert(rap_imprecise_hashes); + + hash.hash = 0; + if (node->uid < rap_cgraph_max_uid) + hash = rap_imprecise_hashes[node->uid]; + + if (hash.hash) + return hash; + + fndecl = NODE_DECL(node); + if (TREE_CODE(TREE_TYPE(fndecl)) == FUNCTION_TYPE) + return rap_hash_function_decl(fndecl, imprecise_rap_hash_flags); + + debug_cgraph_node(node); + debug_tree(fndecl); + error("indirect call to function %qD with a reserved hash value", fndecl); + return hash; +} + +void rap_calculate_func_hashes(void *event_data __unused, void *data __unused) +{ + cgraph_node_ptr node; + int uid; + + gcc_assert(!rap_imprecise_hashes); + + rap_imprecise_hashes = (rap_hash_t *)xcalloc(cgraph_max_uid, sizeof(*rap_imprecise_hashes)); + rap_cgraph_max_uid = cgraph_max_uid; + + FOR_EACH_FUNCTION(node) { + const_tree fndecl; + + uid = node->uid; + gcc_assert(uid < rap_cgraph_max_uid); + + if (node->global.inlined_to) + continue; + + fndecl = NODE_DECL(node); + gcc_assert(fndecl); + + rap_imprecise_hashes[uid] = rap_hash_function_decl(fndecl, imprecise_rap_hash_flags); + gcc_assert(rap_imprecise_hashes[uid].hash); + } +} diff --git a/src/rap_plugin.c b/src/rap_plugin.c new file mode 100755 index 0000000..9fd7ee2 --- /dev/null +++ b/src/rap_plugin.c @@ -0,0 +1,876 @@ +/* + * Copyright 2012-2017 by PaX Team + * Licensed under the GPL v2 + * + * Homepage: http://pax.grsecurity.net/ + * + * Usage: + * $ # for 4.5/4.6/C based 4.7 + * $ gcc -I`gcc -print-file-name=plugin`/include -I`gcc -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o rap_plugin.so rap_plugin.c + * $ # for C++ based 4.7/4.8+ + * $ g++ -I`g++ -print-file-name=plugin`/include -I`g++ -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o rap_plugin.so rap_plugin.c + * $ gcc -fplugin=./rap_plugin.so -fplugin-arg-rap_plugin-check=call test.c -O2 + */ + +#include "rap.h" + +__visible int plugin_is_GPL_compatible; + +static struct plugin_info rap_plugin_info = { + .version = "201612091515", + .help = "typecheck=ret,call\tenable the corresponding type hash checking based features\n" + "retabort=ud2\t\t\toverride __builtin_trap with specified asm for both kinds of return address checking\n" + "callabort=ud2\t\t\toverride __builtin_trap with specified asm for indirect call checking\n" + "hash=abs,abs-finish,abs-ops,abs-attr,const,volatile\n" + "report=func,fptr,abs\n" +}; + +rap_hash_flags_t imprecise_rap_hash_flags = { + .qual_const = 1, + .qual_volatile = 1, +}; + +tree rap_hash_type_node; + +static bool report_func_hash, report_abs_hash; +const char *rap_abort_ret; +const char *rap_abort_call; + +bool enable_type_ret = false; +bool enable_type_call = false; + +#define RAP_TARGET_IA 1 +#define RAP_TARGET_ARM 2 + +#ifdef TARGET_386 +int rap_target_current = RAP_TARGET_IA; +#elif __aarch64__ +int rap_target_current = RAP_TARGET_ARM; +#endif + +//==------------------------------------------------------------------------==// +// ARM64 is a weak memory model cpu, has more effecient code sequence for +// synchronized. +// asm("isb"); +// asm("dmb"); +// asm("ldar": "+rm"(var)); +// asm("stlr": :"rm"(var)); +// +// create the equivalent of +// asm volatile("" : : : "memory"); +// or +// asm("" : "+rm"(var)); +// or +// asm("" : : "rm"(var)); +gimple barrier(tree var, bool full) +{ + gimple stmt; + gasm *asm_stmt; +#if BUILDING_GCC_VERSION <= 4007 + VEC(tree, gc) *insns = NULL; + VEC(tree, gc) *inputs = NULL; + VEC(tree, gc) *outputs = NULL; + VEC(tree, gc) *clobbers = NULL; +#else + vec *insns = NULL; + vec *inputs = NULL; + vec *outputs = NULL; + vec *clobbers = NULL; +#endif + + tree insn = NULL; + + if (!var && full) { + tree clobber; + + if (rap_target_current == RAP_TARGET_IA) + clobber = build_tree_list(NULL_TREE, build_const_char_string(7, "memory")); + else if (rap_target_current == RAP_TARGET_ARM) + insn = build_tree_list(NULL_TREE, build_const_char_string(4, "dmb")); + else + assert(!"rap_target_current broken"); + +#if BUILDING_GCC_VERSION <= 4007 + if (rap_target_current == RAP_TARGET_IA) + VEC_safe_push(tree, gc, clobbers, clobber); + else if (rap_target_current = RAP_TARGET_ARM) + VEC_safe_push(tree, gc, insns, insn); + else + assert(!"rap_target_current broken"); + + +#else + if (rap_target_current == RAP_TARGET_IA) + vec_safe_push(clobbers, clobber); + else if (rap_target_current == RAP_TARGET_ARM) + vec_safe_push(insns, insn); + else + assert(!"rap_target_current broken"); +#endif + } else if (full) { + tree input, output; + + input = build_tree_list(NULL_TREE, build_const_char_string(2, "0")); + input = chainon(NULL_TREE, build_tree_list(input, var)); +#if BUILDING_GCC_VERSION <= 4007 + VEC_safe_push(tree, gc, inputs, input); +#else + vec_safe_push(inputs, input); +#endif + + if (rap_target_current == RAP_TARGET_ARM) + insn = build_tree_list(NULL_TREE, build_const_char_string(5, "ldar")); + output = build_tree_list(NULL_TREE, build_const_char_string(4, "=rm")); + gcc_assert(SSA_NAME_VAR(var)); + var = make_ssa_name(SSA_NAME_VAR(var), NULL); + output = chainon(NULL_TREE, build_tree_list(output, var)); +#if BUILDING_GCC_VERSION <= 4007 + if (rap_target_current == RAP_TARGET_ARM) + VEC_safe_push(tree, gc, insns, insn); + else { + assert(rap_target_current == RAP_TARGET_IA && !"rap_target_current broken"); + VEC_safe_push(tree, gc, outputs, output); + } +#else + if (rap_target_current == RAP_TARGET_ARM) + vec_safe_push(insns, insn); + else { + assert(rap_target_current == RAP_TARGET_IA && !"rap_target_current broken"); + vec_safe_push(outputs, output); + } +#endif + } else { + tree insn, input; + + if (rap_target_current == RAP_TARGET_ARM) + insn = build_tree_list(NULL_TREE, build_const_char_string(5, "stlr")); + input = build_tree_list(NULL_TREE, build_const_char_string(3, "rm")); + input = chainon(NULL_TREE, build_tree_list(input, var)); +#if BUILDING_GCC_VERSION <= 4007 + if (rap_target_current == RAP_TARGET_ARM) + VEC_safe_push(tree, gc, insns, insn); + else { + assert(rap_target_current == RAP_TARGET_IA && !"rap_target_current broken"); + VEC_safe_push(tree, gc, inputs, input); + } +#else + if (rap_target_current == RAP_TARGET_ARM) + VEC_safe_push(tree, gc, insns, insn); + else { + assert(rap_target_current == RAP_TARGET_IA && !"rap_target_current broken"); + VEC_safe_push(tree, gc, inputs, input); +#endif + } + + stmt = gimple_build_asm_vec("", inputs, outputs, clobbers, NULL); + asm_stmt = as_a_gasm(stmt); + if (!var && full) + gimple_asm_set_volatile(asm_stmt, true); + else if (full) + SSA_NAME_DEF_STMT(var) = stmt; + return stmt; +} + +static const struct gcc_debug_hooks *old_debug_hooks; +static struct gcc_debug_hooks rap_debug_hooks; + +static bool __rap_cgraph_indirectly_callable(cgraph_node_ptr node, void *data __unused) +{ +#if BUILDING_GCC_VERSION >= 4008 + if (NODE_SYMBOL(node)->externally_visible) +#else + if (node->local.externally_visible) +#endif + return true; + + if (NODE_SYMBOL(node)->address_taken) + return true; + + return false; +} + +static bool rap_cgraph_indirectly_callable(cgraph_node_ptr node) +{ + return cgraph_for_node_and_aliases(node, __rap_cgraph_indirectly_callable, NULL, true); +} + +static void rap_hash_align(const_tree decl) +{ + unsigned HOST_WIDE_INT rap_hash_offset; + unsigned HOST_WIDE_INT skip; + + skip = 1ULL << align_functions_log; + if (DECL_USER_ALIGN(decl)) + return; + + if (!optimize_function_for_speed_p(cfun)) + return; + + if (UNITS_PER_WORD == 8) + rap_hash_offset = 2 * sizeof(rap_hash_t); + else if (UNITS_PER_WORD == 4) + rap_hash_offset = sizeof(rap_hash_t); + else + gcc_unreachable(); + + if (skip <= rap_hash_offset) + return; + +#ifdef TARGET_386 + { + char padding[skip - rap_hash_offset]; + + // this byte sequence helps disassemblers not trip up on the following rap hash + memset(padding, 0xcc, sizeof padding - 1); + padding[sizeof padding - 1] = 0xb8; + if (TARGET_64BIT && sizeof padding > 1) + padding[sizeof padding - 2] = 0x48; + ASM_OUTPUT_ASCII(asm_out_file, padding, sizeof padding); + } +#else + ASM_OUTPUT_SKIP(asm_out_file, skip - rap_hash_offset); +#endif +} + +static void rap_begin_function(tree decl) +{ + cgraph_node_ptr node; + rap_hash_t imprecise_rap_hash; + + gcc_assert(debug_hooks == &rap_debug_hooks); + + // chain to previous callback + if (old_debug_hooks && old_debug_hooks->begin_function) + old_debug_hooks->begin_function(decl); + + // align the rap hash if necessary + rap_hash_align(decl); + + // don't compute hash for functions called only directly + node = cgraph_get_node(decl); + gcc_assert(node); + if (!rap_cgraph_indirectly_callable(node)) { + imprecise_rap_hash.hash = 0; + } else { + imprecise_rap_hash = rap_hash_function_node_imprecise(node); + } + + if (report_func_hash) + inform(DECL_SOURCE_LOCATION(decl), "func rap_hash: %x %s", imprecise_rap_hash.hash, IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl))); + + if (UNITS_PER_WORD == 8) + fprintf(asm_out_file, "\t.quad %#llx\t%s __rap_hash_call_%s\n", (long long)imprecise_rap_hash.hash, ASM_COMMENT_START, IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl))); + else + fprintf(asm_out_file, "\t.long %#x\t%s __rap_hash_call_%s\n", imprecise_rap_hash.hash, ASM_COMMENT_START, IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl))); +} + +static void rap_emit_hash_symbol(const char *type, const char *asmname, rap_hash_t hash) +{ + char *name = NULL; + + gcc_assert(asprintf(&name, "__rap_hash_%s_%s", type, asmname) != -1); + + fprintf(asm_out_file, "\t.pushsection .text\n"); + + fprintf(asm_out_file, GLOBAL_ASM_OP " %s\n", name); + if (UNITS_PER_WORD == 8) + fprintf(asm_out_file, "\t.offset %#018llx\n", (long long)hash.hash); + else if (UNITS_PER_WORD == 4) + fprintf(asm_out_file, "\t.offset %#010x\n", hash.hash); + else + gcc_unreachable(); + + ASM_OUTPUT_TYPE_DIRECTIVE(asm_out_file, name, "object"); + ASM_OUTPUT_LABEL(asm_out_file, name); + free(name); + + fprintf(asm_out_file, "\t.popsection\n"); +} + +static void rap_emit_hash_symbols(const char *asmname, rap_hash_t hash) +{ +#ifdef TARGET_386 + rap_emit_hash_symbol("call", asmname, hash); +#elif __aarch64__ + /* TODO, if branch is over 128MB? */ + rap_emit_hash_symbol("bl", asmname, hash); +#endif + + hash.hash = -hash.hash; + rap_emit_hash_symbol("ret", asmname, hash); +} + +/* + emit an absolute symbol for each function that may be referenced through the plt + - all externs + - non-static functions + - use visibility instead? + + .globl __rap_hash_call_func + .offset 0xhash_for_func + .type __rap_hash_call_func, @object + __rap_hash_call_func: + .previous +*/ +static void rap_finish_unit(void *gcc_data __unused, void *user_data __unused) +{ + cgraph_node_ptr node; + rap_hash_t hash; + + gcc_assert(debug_hooks == &rap_debug_hooks); + + hash.hash = 0; + FOR_EACH_FUNCTION(node) { + tree fndecl; + const char *asmname; + + if (node->thunk.thunk_p || node->alias) + continue; + if (cgraph_function_body_availability(node) >= AVAIL_INTERPOSABLE) { + if (!rap_cgraph_indirectly_callable(node)) + continue; + } + +#if BUILDING_GCC_VERSION >= 4007 + gcc_assert(cgraph_function_or_thunk_node(node, NULL) == node); +#endif + + fndecl = NODE_DECL(node); + gcc_assert(fndecl); + if (DECL_IS_BUILTIN(fndecl) && DECL_BUILT_IN_CLASS(fndecl) == BUILT_IN_NORMAL) + continue; + + if (!TREE_PUBLIC(fndecl)) + continue; + + if (DECL_ARTIFICIAL(fndecl)) + continue; + + if (DECL_ABSTRACT_ORIGIN(fndecl) && DECL_ABSTRACT_ORIGIN(fndecl) != fndecl) + continue; + + gcc_assert(DECL_ASSEMBLER_NAME(fndecl)); + asmname = IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(fndecl)); + if (strchr(asmname, '.')) + continue; + + if (asmname[0] == '*') + asmname++; + + gcc_assert(asmname[0]); + + hash = rap_hash_function_node_imprecise(node); + if (report_abs_hash) + inform(DECL_SOURCE_LOCATION(fndecl), "abs rap_hash: %x %s", hash.hash, IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(fndecl))); + rap_emit_hash_symbols(asmname, hash); + } +} + +// emit the rap hash as an absolute symbol for all functions seen in the frontend +// this is necessary as later unreferenced nodes will be removed yet we'd like to emit as many hashes as possible +static void rap_emit_hash_symbols_finish_decl(void *event_data, void *data __unused) +{ + tree fndecl = (tree)event_data; + rap_hash_t hash; + const char *asmname; + + if (fndecl == error_mark_node) + return; + + if (TREE_CODE(fndecl) != FUNCTION_DECL) + return; + + if (!TREE_PUBLIC(fndecl)) + return; + + if (DECL_ARTIFICIAL(fndecl)) + return; + + if (DECL_ABSTRACT_ORIGIN(fndecl) && DECL_ABSTRACT_ORIGIN(fndecl) != fndecl) + return; + + asmname = DECL_NAME_POINTER(fndecl); + gcc_assert(asmname[0]); + + if (strchr(asmname, '.')) + return; + // TODO, We need dynamic hash in terms of avail_direct_call set + hash = rap_hash_function_decl(fndecl, imprecise_rap_hash_flags); + rap_emit_hash_symbols(asmname, hash); + if (report_abs_hash) + inform(DECL_SOURCE_LOCATION(fndecl), "abs rap_hash: %x %s", hash.hash, asmname); +} + +static void rap_emit_hash_symbols_finish_decl_attr(void *event_data, void *data) +{ + tree fndecl = (tree)event_data; + + if (fndecl == error_mark_node) + return; + + if (TREE_CODE(fndecl) != FUNCTION_DECL) + return; + + if (!lookup_attribute("rap_hash", TYPE_ATTRIBUTES(TREE_TYPE(fndecl)))) + return; + + rap_emit_hash_symbols_finish_decl(event_data, data); +} + +static void rap_emit_hash_symbols_type(const_tree type, const char *prefix) +{ + const_tree field; + + if (TYPE_FIELDS(type) == NULL_TREE) + return; + + // TODO skip constified types for now + if (TYPE_READONLY(type)) + return; + + // create the prefix if it hasn't been done yet + if (!*prefix) { + const_tree name = type_name(type); + + // skip an anonymous struct embedded inside another one + // we'll see it when we walk the parent later + if (!name) + return; + + prefix = IDENTIFIER_POINTER(name); + gcc_assert(*prefix); + } + + for (field = TYPE_FIELDS(type); field; field = TREE_CHAIN(field)) { + const_tree fieldtype, fieldname; + char *hashname = NULL, *newprefix = NULL; + rap_hash_t hash; + + fieldtype = TREE_TYPE(field); + switch (TREE_CODE(fieldtype)) { + default: + continue; + + case RECORD_TYPE: + case UNION_TYPE: + fieldname = DECL_NAME(field); + if (!fieldname) + continue; + gcc_assert(asprintf(&newprefix, "%s.%s", prefix, IDENTIFIER_POINTER(fieldname)) != -1); + rap_emit_hash_symbols_type(fieldtype, newprefix); + free(newprefix); + continue; + + case POINTER_TYPE: + fieldtype = TREE_TYPE(fieldtype); + if (TREE_CODE(fieldtype) != FUNCTION_TYPE) + continue; + + hash = rap_hash_function_type(fieldtype, imprecise_rap_hash_flags); + fieldname = DECL_NAME(field); + gcc_assert(fieldname); + gcc_assert(asprintf(&hashname, "%s.%s", prefix, IDENTIFIER_POINTER(fieldname)) != -1); + if (report_abs_hash) + inform(DECL_SOURCE_LOCATION(field), "abs rap_hash: %x %s", hash.hash, hashname); + rap_emit_hash_symbols(hashname, hash); + free(hashname); + continue; + } + } +} + +static void rap_emit_hash_symbols_finish_type(void *event_data, void *data __unused) +{ + const_tree type = (const_tree)event_data; + + if (type == NULL_TREE || type == error_mark_node) + return; + + if (!lookup_attribute("rap_hash", TYPE_ATTRIBUTES(type))) + return; + + switch (TREE_CODE(type)) { + default: + debug_tree(type); + gcc_unreachable(); + +#if BUILDING_GCC_VERSION >= 5000 + case ENUMERAL_TYPE: +#endif + case UNION_TYPE: + break; + + case RECORD_TYPE: + rap_emit_hash_symbols_type(type, ""); + break; + } +} + +static void rap_assembly_start(void) +{ + gcc_assert(debug_hooks == &rap_debug_hooks); + + // chain to previous callback + if (old_debug_hooks && old_debug_hooks->assembly_start) + old_debug_hooks->assembly_start(); + +#ifdef TARGET_386 +#define RAP_SUP_TARGETS +#elif __aarch64__ +#define RAP_SUP_TARGETS +#endif + +#ifdef RAP_SUP_TARGETS + if (enable_type_call) { + fprintf(asm_out_file, + "\t.macro rap_indirect_call target hash\n" +#ifdef TARGET_386 + "\t\tjmp 2001f\n" +#elif __aarch64__ + "\t\tb 2001f\n" +#endif + "\t\t%s __rap_hash_ret_\\hash\n" + "\t\t.skip 8-(2002f-2001f),0xcc\n" +#ifdef TARGET_386 + "\t2001: call \\target\n" +#elif __aarch64__ + "\t2001: bl \\target\n" +#endif + "\t2002:\n" + "\t.endm\n", + (UNITS_PER_WORD == 8 ? ".quad" : ".long") + ); + + fprintf(asm_out_file, + "\t.macro rap_direct_call target hash=""\n" + "\t\t.ifb \\hash\n" + "\t\trap_indirect_call \\target \\target\n" + "\t\t.else\n" + "\t\trap_indirect_call \\target \\hash\n" + "\t\t.endif\n" + "\t.endm\n" + ); + } + + if (enable_type_ret) { + fprintf(asm_out_file, + "\t.macro rap_ret target\n" + "\t\tret\n" + "\t.endm\n" + ); + } + +#error unsupported target +#endif + +#undef RAP_SUP_TARGETS +} + +static void (*old_override_options_after_change)(void); + +static void rap_override_options_after_change(void) +{ + if (old_override_options_after_change) + old_override_options_after_change(); + +#if BUILDING_GCC_VERSION >= 5000 + flag_ipa_icf_functions = 0; +#endif + flag_crossjumping = 0; + flag_optimize_sibling_calls = 0; +} + +static void rap_start_unit_common(void *gcc_data __unused, void *user_data __unused) +{ + rap_hash_type_node = long_integer_type_node; + + if (debug_hooks) + rap_debug_hooks = *debug_hooks; + + if (enable_type_call || enable_type_ret) + rap_debug_hooks.assembly_start = rap_assembly_start; + rap_debug_hooks.begin_function = rap_begin_function; + + old_debug_hooks = debug_hooks; + debug_hooks = &rap_debug_hooks; + + old_override_options_after_change = targetm.override_options_after_change; + targetm.override_options_after_change = rap_override_options_after_change; +} + +static bool rap_unignore_gate(void) +{ + if (!DECL_IGNORED_P(current_function_decl)) + return false; + + inform(DECL_SOURCE_LOCATION(current_function_decl), "DECL_IGNORED fixed"); + + DECL_IGNORED_P(current_function_decl) = 0; + return false; +} + +#define PASS_NAME rap_unignore +#define NO_EXECUTE +#define TODO_FLAGS_FINISH TODO_dump_func +#include "gcc-generate-rtl-pass.h" + +static bool rap_version_check(struct plugin_gcc_version *gcc_version, struct plugin_gcc_version *plugin_version) +{ + if (!gcc_version || !plugin_version) + return false; + +#if BUILDING_GCC_VERSION >= 5000 + if (strncmp(gcc_version->basever, plugin_version->basever, 4)) +#else + if (strcmp(gcc_version->basever, plugin_version->basever)) +#endif + return false; + if (strcmp(gcc_version->datestamp, plugin_version->datestamp)) + return false; + if (strcmp(gcc_version->devphase, plugin_version->devphase)) + return false; + if (strcmp(gcc_version->revision, plugin_version->revision)) + return false; +// if (strcmp(gcc_version->configuration_arguments, plugin_version->configuration_arguments)) +// return false; + return true; +} + +static tree handle_rap_hash_attribute(tree *node, tree name, tree args __unused, int flags, bool *no_add_attrs) +{ + *no_add_attrs = true; + + gcc_assert(TYPE_P(*node)); + + switch (TREE_CODE(*node)) { + default: + error("%qE attribute applies to structure and function types only (%qT)", name, *node); + return NULL_TREE; + + case FUNCTION_TYPE: + case RECORD_TYPE: + break; + } + + *no_add_attrs = false; + return NULL_TREE; +} + +static struct attribute_spec rap_hash_attr = { + .name = "rap_hash", + .min_length = 0, + .max_length = 0, + .decl_required = false, + .type_required = true, + .function_type_required = false, + .handler = handle_rap_hash_attribute, +#if BUILDING_GCC_VERSION >= 4007 + .affects_type_identity = true +#endif +}; + +static void register_attributes(void *event_data __unused, void *data __unused) +{ + register_attribute(&rap_hash_attr); +} + +EXPORTED_CONST struct ggc_root_tab gt_ggc_r_gt_rap[] = { + { + .base = &rap_hash_type_node, + .nelt = 1, + .stride = sizeof(rap_hash_type_node), + .cb = >_ggc_mx_tree_node, + .pchw = >_pch_nx_tree_node + }, + LAST_GGC_ROOT_TAB +}; + +__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) +{ + int i; + const char * const plugin_name = plugin_info->base_name; + const int argc = plugin_info->argc; + const struct plugin_argument * const argv = plugin_info->argv; + bool enable_abs = false; + bool enable_abs_finish = false; + bool enable_abs_ops = false; + bool enable_abs_attr = false; + + //==----------------------------------------------------------------==// + // Optimization design for RAP: + // Add many gcc pass for get two set, hash compute may be need change. + // avail_direct_call contains legal destionation procedure of current + // procedure, this set computed after the call graph has been down. + // avail_indirect_call contains all the legal funtion pointer of current + // variable, compute this set need more than one pass, relatied with + // function pointer alias. + // + // Build set for avail_direct_call + PASS_INFO(rap_adc_0, "*build_cgraph_edges", 1, PASS_POS_INSERT_AFTER); + PASS_INFO(rap_adc_1, "*rebuild_cgraph_edges", 1, PASS_POS_INSERT_AFTER); + PASS_INFO(rap_adc_2, "whole_program", 1, PASS_POS_INSERT_AFTER); + PASS_INFO(rap_ret, "optimized", 1, hS_INSERT_AFTER); + // Build set for avail_indirect_call + PASS_INFO(rap_ret, "*all_optimizations_g", 1, PASS_POS_INSERT_AFTER); + PASS_INFO(rap_fptrh "rap_ret", 1, PASS_POS_INSERT_AFTER); + PASS_INFO(rap_mark_retloc, "mach", 1, PASS_POS_INSERT_AFTER); + PASS_INFO(rap_unignore, "final", 1, PASS_POS_INSERT_BEFORE); + + if (!rap_version_check(version, &gcc_version)) { + error_gcc_version(version); + return 1; + } + +#if BUILDING_GCC_VERSION >= 5000 + if (flag_ipa_icf_functions) { +// warning_at(UNKNOWN_LOCATION, 0, G_("-fipa-icf is incompatible with %s, disabling..."), plugin_name); +// inform(UNKNOWN_LOCATION, G_("-fipa-icf is incompatible with %s, disabling..."), plugin_name); + flag_ipa_icf_functions = 0; + } +#endif + + for (i = 0; i < argc; ++i) { + if (!strcmp(argv[i].key, "disable")) + continue; + + if (!strcmp(argv[i].key, "typecheck")) { + char *values, *value, *saveptr; + + if (!argv[i].value) { + error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); + continue; + } + + values = xstrdup(argv[i].value); + value = strtok_r(values, ",", &saveptr); + while (value) { + if (!strcmp(value, "ret")) + enable_type_ret = true; + else if (!strcmp(value, "call")) + enable_type_call = true; + else + error(G_("unknown value supplied for option '-fplugin-arg-%s-%s=%s'"), plugin_name, argv[i].key, value); + value = strtok_r(NULL, ",", &saveptr); + } + free(values); + continue; + } + + if (!strcmp(argv[i].key, "retabort")) { + if (!argv[i].value) { + error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); + continue; + } + + rap_abort_ret = xstrdup(argv[i].value); + continue; + } + + if (!strcmp(argv[i].key, "callabort")) { + if (!argv[i].value) { + error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); + continue; + } + + rap_abort_call = xstrdup(argv[i].value); + continue; + } + + if (!strcmp(argv[i].key, "hash")) { + char *values, *value, *saveptr; + + if (!argv[i].value) { + error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); + continue; + } + + values = xstrdup(argv[i].value); + value = strtok_r(values, ",", &saveptr); + while (value) { + if (!strcmp(value, "abs")) + enable_abs = enable_abs_finish = true; + else if (!strcmp(value, "abs-finish")) + enable_abs_finish = true; + else if (!strcmp(value, "abs-ops")) + enable_abs_ops = true; + else if (!strcmp(value, "abs-attr")) + enable_abs_attr = true; +// else if (!strcmp(value, "const")) +// imprecise_rap_hash_flags.qual_const = 1; +// else if (!strcmp(value, "volatile")) +// imprecise_rap_hash_flags.qual_volatile = 1; + else + error(G_("unknown value supplied for option '-fplugin-arg-%s-%s=%s'"), plugin_name, argv[i].key, value); + value = strtok_r(NULL, ",", &saveptr); + } + free(values); + continue; + } + + if (!strcmp(argv[i].key, "report")) { + char *values, *value, *saveptr; + + if (!argv[i].value) { + error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); + continue; + } + + values = xstrdup(argv[i].value); + value = strtok_r(values, ",", &saveptr); + while (value) { + if (!strcmp(value, "func")) + report_func_hash = true; + else if (!strcmp(value, "fptr")) + report_fptr_hash = true; + else if (!strcmp(value, "abs")) + report_abs_hash = true; + else + error(G_("unknown value supplied for option '-fplugin-arg-%s-%s=%s'"), plugin_name, argv[i].key, value); + value = strtok_r(NULL, ",", &saveptr); + } + free(values); + continue; + } + + error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); + } + + register_callback(plugin_name, PLUGIN_INFO, NULL, &rap_plugin_info); + + if (enable_type_ret) { + flag_crossjumping = 0; + flag_optimize_sibling_calls = 0; + register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &rap_ret_pass_info); + register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &rap_mark_retloc_pass_info); + } + + if (enable_type_call || enable_type_ret) { + if (enable_abs) +#if BUILDING_GCC_VERSION >= 4007 + register_callback(plugin_name, PLUGIN_FINISH_DECL, rap_emit_hash_symbols_finish_decl, NULL); +#else + register_callback(plugin_name, PLUGIN_PRE_GENERICIZE, rap_emit_hash_symbols_finish_decl, NULL); +#endif + if (enable_abs_ops) + register_callback(plugin_name, PLUGIN_FINISH_TYPE, rap_emit_hash_symbols_finish_type, NULL); + if (enable_abs_attr) +#if BUILDING_GCC_VERSION >= 4007 + register_callback(plugin_name, PLUGIN_FINISH_DECL, rap_emit_hash_symbols_finish_decl_attr, NULL); +#else + register_callback(plugin_name, PLUGIN_PRE_GENERICIZE, rap_emit_hash_symbols_finish_decl_attr, NULL); +#endif + register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL); + register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &rap_unignore_pass_info); + register_callback(plugin_name, PLUGIN_START_UNIT, rap_start_unit_common, NULL); + register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL, (void *)>_ggc_r_gt_rap); + if (enable_abs_finish) + register_callback(plugin_name, PLUGIN_FINISH_UNIT, rap_finish_unit, NULL); + register_callback(plugin_name, PLUGIN_ALL_IPA_PASSES_START, rap_calculate_func_hashes, NULL); + + if (!enable_type_ret) + rap_fptr_pass_info.reference_pass_name = "optimized"; + register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &rap_fptr_pass_info); + } + + return 0; +} diff --git a/src/rap_ret_pass.c b/src/rap_ret_pass.c new file mode 100755 index 0000000..a3ab16a --- /dev/null +++ b/src/rap_ret_pass.c @@ -0,0 +1,346 @@ +/* + * Copyright 2012-2017 by PaX Team + * Licensed under the GPL v2 + * + * Homepage: http://pax.grsecurity.net/ + */ + +#include "rap.h" + +bool rap_cmodel_check(void) +{ + tree section; +#ifdef TARGET_386 + + if (!TARGET_64BIT || ix86_cmodel != CM_KERNEL) + return true; + + section = lookup_attribute("section", DECL_ATTRIBUTES(current_function_decl)); + if (!section || !TREE_VALUE(section)) + return true; + + section = TREE_VALUE(TREE_VALUE(section)); + return strncmp(TREE_STRING_POINTER(section), ".vsyscall_", 10); +#elif __aarch64__ + return strncmp(TREE_STRING_POINTER(section), ".vsyscall_", 10); +#error unsupported target +#endif +} + +static bool rap_ret_gate(void) +{ + return rap_cmodel_check(); +} + +tree create_new_var(tree type, const char *name) +{ + tree var; + + var = create_tmp_var(type, name); + add_referenced_var(var); +// mark_sym_for_renaming(var); + return var; +} + +/* + * insert the equivalent of + * return (unsigned long)__builtin_return_address(0); + */ +static tree get_retaddr(gimple_seq *stmts) +{ + gimple stmt; + tree retaddr_ptr; + + stmt = barrier(NULL_TREE, true); + gimple_seq_add_stmt(stmts, stmt); + + // copy the return address into a temporary variable + retaddr_ptr = create_new_var(ptr_type_node, "rap_retaddr_exit_ptr"); + stmt = gimple_build_call(builtin_decl_implicit(BUILT_IN_RETURN_ADDRESS), 1, integer_zero_node); + retaddr_ptr = make_ssa_name(retaddr_ptr, stmt); + gimple_call_set_lhs(stmt, retaddr_ptr); + gimple_seq_add_stmt(stmts, stmt); + + return retaddr_ptr; +} + +/* + * insert the equivalent of + * if (*(long *)((void *)retaddr+N) != (long)-function_hash) abort(); + */ +static void check_retaddr(gimple_stmt_iterator *gsi, tree new_retaddr) +{ + gimple stmt; + location_t loc; + basic_block cond_bb, join_bb, true_bb; + edge e; + + gcc_assert(!gsi_end_p(*gsi)); + loc = gimple_location(gsi_stmt(*gsi)); + + gimple_seq stmts = NULL; + tree target_hash, computed_hash; + rap_hash_t hash; + +#ifdef TARGET_386 + if (TARGET_64BIT) + target_hash = get_rap_hash(&stmts, loc, new_retaddr, -16); + else + target_hash = get_rap_hash(&stmts, loc, new_retaddr, -10); +#else +#error unsupported target +#endif + + if (gsi_end_p(*gsi) || !stmt_ends_bb_p(gsi_stmt(*gsi))) + gsi_insert_seq_after(gsi, stmts, GSI_CONTINUE_LINKING); + else { + gsi_insert_seq_before(gsi, stmts, GSI_SAME_STMT); + gsi_prev(gsi); + } + + hash = rap_hash_function_type(TREE_TYPE(current_function_decl), imprecise_rap_hash_flags); + computed_hash = build_int_cst_type(rap_hash_type_node, -hash.hash); + + stmt = gimple_build_cond(NE_EXPR, target_hash, computed_hash, NULL_TREE, NULL_TREE); + gimple_set_location(stmt, loc); + gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING); + + cond_bb = gimple_bb(gsi_stmt(*gsi)); + e = split_block(cond_bb, gsi_stmt(*gsi)); + cond_bb = e->src; + join_bb = e->dest; + e->flags = EDGE_FALSE_VALUE; + e->probability = REG_BR_PROB_BASE; + + true_bb = create_empty_bb(join_bb); + make_edge(cond_bb, true_bb, EDGE_TRUE_VALUE | EDGE_PRESERVE); + + set_immediate_dominator(CDI_DOMINATORS, true_bb, cond_bb); + set_immediate_dominator(CDI_DOMINATORS, join_bb, cond_bb); + + gcc_assert(cond_bb->loop_father == join_bb->loop_father); + add_bb_to_loop(true_bb, cond_bb->loop_father); + + // insert call to builtin_trap or rap_abort_ret + *gsi = gsi_start_bb(true_bb); + + if (rap_abort_ret) { + stmt = gimple_build_asm_vec(rap_abort_ret, NULL, NULL, NULL, NULL); + gimple_asm_set_volatile(as_a_gasm(stmt), true); + gimple_set_location(stmt, loc); + gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING); + + stmt = gimple_build_call(builtin_decl_implicit(BUILT_IN_UNREACHABLE), 0); + } else + stmt = gimple_build_call(builtin_decl_implicit(BUILT_IN_TRAP), 0); + + gimple_set_location(stmt, loc); + gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING); + + *gsi = gsi_after_labels(join_bb); +} + +static unsigned int rap_ret_execute(void) +{ + edge e; + edge_iterator ei; + + loop_optimizer_init(LOOPS_NORMAL | LOOPS_HAVE_RECORDED_EXITS); + gcc_assert(current_loops); + + calculate_dominance_info(CDI_DOMINATORS); + calculate_dominance_info(CDI_POST_DOMINATORS); + + FOR_EACH_EDGE(e, ei, EXIT_BLOCK_PTR_FOR_FN(cfun)->preds) { + gimple_stmt_iterator gsi; + gimple_seq stmts = NULL; + tree new_retaddr; + + gsi = gsi_last_nondebug_bb(e->src); + gcc_assert(!gsi_end_p(gsi)); + gcc_assert(gimple_code(gsi_stmt(gsi)) == GIMPLE_RETURN); + + new_retaddr = get_retaddr(&stmts); + gsi_insert_seq_before(&gsi, stmts, GSI_SAME_STMT); + gsi_prev(&gsi); + check_retaddr(&gsi, new_retaddr); + } + + free_dominance_info(CDI_DOMINATORS); + free_dominance_info(CDI_POST_DOMINATORS); + loop_optimizer_finalize(); + return 0; +} + +#define PASS_NAME rap_ret +#define PROPERTIES_REQUIRED PROP_cfg +#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func | TODO_remove_unused_locals | TODO_update_ssa | TODO_cleanup_cfg | TODO_ggc_collect | TODO_rebuild_cgraph_edges | TODO_verify_flow +#include "gcc-generate-gimple-pass.h" + +// find and remove the asm mark from the given insn up in its basic block +static tree rap_find_retloc_mark(rtx_insn *insn) +{ + basic_block bb; + rtx_insn *hash; + +#if BUILDING_GCC_VERSION == 4005 + FOR_EACH_BB_FN(bb, cfun) { + rtx_insn *i; + + FOR_BB_INSNS(bb, i) { + if (i == insn) + break; + } + if (i == insn) + break; + } +#else + bb = BLOCK_FOR_INSN(insn); +#endif + gcc_assert(bb); + gcc_assert(BB_HEAD(bb)); + + for (hash = insn; hash && hash != PREV_INSN(BB_HEAD(bb)); hash = PREV_INSN(hash)) { + tree computed_hash; + rtx body; + + if (!INSN_P(hash)) + continue; + + body = PATTERN(hash); + + if (GET_CODE(body) != PARALLEL) + continue; + + body = XVECEXP(body, 0, 0); + if (GET_CODE(body) != ASM_OPERANDS) + continue; + + if (ASM_OPERANDS_INPUT_LENGTH(body) != 1) + continue; + + body = ASM_OPERANDS_INPUT(body, 0); + if (!CONST_INT_P(body)) + continue; + + computed_hash = build_int_cst_type(rap_hash_type_node, INTVAL(body)); + delete_insn_and_edges(hash); + return computed_hash;; + } + + return NULL_TREE; +} + +static tree rap_get_direct_call_retloc_mark(rtx_insn *insn) +{ + rap_hash_t func_hash; + rtx body; + tree fntype; + + body = PATTERN(insn); + if (GET_CODE(body) == SET) + body = SET_SRC(body); + if (GET_CODE(body) != CALL) + return NULL_TREE; + + body = XEXP(body, 0); + gcc_assert(GET_CODE(body) == MEM); + if (GET_CODE(XEXP(body, 0)) != SYMBOL_REF) + return NULL_TREE; + + fntype = SYMBOL_REF_DECL(XEXP(body, 0)); + gcc_assert(TREE_CODE(fntype) == FUNCTION_DECL); + fntype = TREE_TYPE(fntype); + func_hash = rap_hash_function_type(fntype, imprecise_rap_hash_flags); + return build_int_cst_type(rap_hash_type_node, -func_hash.hash); +} + +static unsigned int rap_mark_retloc_execute(void) +{ + rtx_insn *insn; + + for (insn = get_insns(); insn; insn = NEXT_INSN(insn)) { + rtvec argvec, constraintvec, labelvec; + rtx mark, label1, label2; + tree computed_hash = NULL_TREE; + + if (INSN_DELETED_P(insn)) + continue; + + // rtl match (call_insn (set (reg) (call (mem)))) + if (!CALL_P(insn)) + continue; + + gcc_assert(!SIBLING_CALL_P(insn)); + + if (find_reg_note(insn, REG_NORETURN, 0)) + continue; + + argvec = rtvec_alloc(1); + constraintvec = rtvec_alloc(1); + labelvec = rtvec_alloc(2); + +#ifdef TARGET_386 + if (TARGET_64BIT) + mark = gen_rtx_ASM_OPERANDS(VOIDmode, ggc_strdup("jmp %l1 ; .quad %c0 ; .skip 8-(%l2-%l1),0xcc"), empty_string, 0, argvec, constraintvec, labelvec, INSN_LOCATION(insn)); + else + mark = gen_rtx_ASM_OPERANDS(VOIDmode, ggc_strdup("jmp %l1 ; .long %c0 ; .skip 6-(%l2-%l1),0xcc"), empty_string, 0, argvec, constraintvec, labelvec, INSN_LOCATION(insn)); +#elif __aarch64__ + mark = gen_rtx_ASM_OPERANDS(VOIDmode, ggc_strdup("jmp %l1 ; .word %c0 ; .skip 8-(%l2-%l1),0xcc"), + empty_string, 0, argvec, constraintvec, labelvec, INSN_LOCATION(insn)); +#error unsupported target +#endif + MEM_VOLATILE_P(mark) = 1; + + computed_hash = rap_find_retloc_mark(insn); + + // gcc can insert calls to memcpy/memmove/etc in RTL + if (!computed_hash) + computed_hash = rap_get_direct_call_retloc_mark(insn); + + // due to optimizations, the return location mark(s) could have ended up in preceding blocks + if (!computed_hash) { + edge e; + edge_iterator ei; + tree h; + + FOR_EACH_EDGE(e, ei, BLOCK_FOR_INSN(insn)->preds) { + gcc_assert(single_succ_p(e->src)); + h = rap_find_retloc_mark(BB_END(e->src)); + gcc_assert(h); + + if (computed_hash) + gcc_assert(tree_to_shwi(h) == tree_to_shwi(computed_hash)); + else + computed_hash = h; + } + } + + gcc_assert(computed_hash); + ASM_OPERANDS_INPUT(mark, 0) = expand_expr(computed_hash, NULL_RTX, VOIDmode, EXPAND_INITIALIZER); + + ASM_OPERANDS_INPUT_CONSTRAINT_EXP(mark, 0) = gen_rtx_ASM_INPUT_loc(DImode, ggc_strdup("i"), UNKNOWN_LOCATION); + + label1 = gen_label_rtx(); + label2 = gen_label_rtx(); + ASM_OPERANDS_LABEL(mark, 0) = label1; + ASM_OPERANDS_LABEL(mark, 1) = label2; + + emit_insn_before(mark, insn); + + emit_label_before(label1, insn); + LABEL_NUSES(label1)++; + do { + insn = NEXT_INSN(insn); + } while (GET_CODE(insn) == NOTE && NOTE_KIND(insn) == NOTE_INSN_CALL_ARG_LOCATION); + emit_label_before(label2, insn); + LABEL_NUSES(label2)++; + } + + return 0; +} + +#define PASS_NAME rap_mark_retloc +#define NO_GATE +#define TODO_FLAGS_FINISH TODO_dump_func | TODO_verify_rtl_sharing +#include "gcc-generate-rtl-pass.h"