alpha version rap code
This commit is contained in:
parent
c9d13e9104
commit
ebb9b54ddb
|
@ -0,0 +1,6 @@
|
|||
$(HOSTLIBS)-$(CONFIG_PAX_RAP) += rap_plugin.so
|
||||
always := $($(HOSTLIBS)-y)
|
||||
|
||||
rap_plugin-objs := $(patsubst $(srctree)/$(src)/%.c,%.o,$(wildcard $(srctree)/$(src)/*.c))
|
||||
|
||||
clean-files += *.so
|
|
@ -0,0 +1,916 @@
|
|||
#ifndef GCC_COMMON_H_INCLUDED
|
||||
#define GCC_COMMON_H_INCLUDED
|
||||
|
||||
#include "bversion.h"
|
||||
#if BUILDING_GCC_VERSION >= 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 <sys/mman.h>
|
||||
#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<gasm>(stmt);
|
||||
}
|
||||
|
||||
static inline const gasm *as_a_const_gasm(const_gimple stmt)
|
||||
{
|
||||
return as_a<const gasm>(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<gcall>(stmt);
|
||||
}
|
||||
|
||||
static inline const gcall *as_a_const_gcall(const_gimple stmt)
|
||||
{
|
||||
return as_a<const gcall>(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<gphi>(stmt);
|
||||
}
|
||||
|
||||
static inline const gphi *as_a_const_gphi(const_gimple stmt)
|
||||
{
|
||||
return as_a<const gphi>(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<const gassign *>::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<const ggoto *>::test(const_gimple gs)
|
||||
{
|
||||
return gs->code == GIMPLE_GOTO;
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
inline bool is_a_helper<const greturn *>::test(const_gimple gs)
|
||||
{
|
||||
return gs->code == GIMPLE_RETURN;
|
||||
}
|
||||
|
||||
static inline gasm *as_a_gasm(gimple stmt)
|
||||
{
|
||||
return as_a<gasm *>(stmt);
|
||||
}
|
||||
|
||||
static inline const gasm *as_a_const_gasm(const_gimple stmt)
|
||||
{
|
||||
return as_a<const gasm *>(stmt);
|
||||
}
|
||||
|
||||
static inline gassign *as_a_gassign(gimple stmt)
|
||||
{
|
||||
return as_a<gassign *>(stmt);
|
||||
}
|
||||
|
||||
static inline const gassign *as_a_const_gassign(const_gimple stmt)
|
||||
{
|
||||
return as_a<const gassign *>(stmt);
|
||||
}
|
||||
|
||||
static inline gcall *as_a_gcall(gimple stmt)
|
||||
{
|
||||
return as_a<gcall *>(stmt);
|
||||
}
|
||||
|
||||
static inline const gcall *as_a_const_gcall(const_gimple stmt)
|
||||
{
|
||||
return as_a<const gcall *>(stmt);
|
||||
}
|
||||
|
||||
static inline ggoto *as_a_ggoto(gimple stmt)
|
||||
{
|
||||
return as_a<ggoto *>(stmt);
|
||||
}
|
||||
|
||||
static inline const ggoto *as_a_const_ggoto(const_gimple stmt)
|
||||
{
|
||||
return as_a<const ggoto *>(stmt);
|
||||
}
|
||||
|
||||
static inline gphi *as_a_gphi(gimple stmt)
|
||||
{
|
||||
return as_a<gphi *>(stmt);
|
||||
}
|
||||
|
||||
static inline const gphi *as_a_const_gphi(const_gimple stmt)
|
||||
{
|
||||
return as_a<const gphi *>(stmt);
|
||||
}
|
||||
|
||||
static inline greturn *as_a_greturn(gimple stmt)
|
||||
{
|
||||
return as_a<greturn *>(stmt);
|
||||
}
|
||||
|
||||
static inline const greturn *as_a_const_greturn(const_gimple stmt)
|
||||
{
|
||||
return as_a<const greturn *>(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<cgraph_node_ptr>(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
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright 2012-2017 by PaX Team <pageexec@freemail.hu>
|
||||
* 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"
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* Copyright 2012-2016 by PaX Team <pageexec@freemail.hu>
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,876 @@
|
|||
/*
|
||||
* Copyright 2012-2017 by PaX Team <pageexec@freemail.hu>
|
||||
* 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<tree, va_gc> *insns = NULL;
|
||||
vec<tree, va_gc> *inputs = NULL;
|
||||
vec<tree, va_gc> *outputs = NULL;
|
||||
vec<tree, va_gc> *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;
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* Copyright 2012-2017 by PaX Team <pageexec@freemail.hu>
|
||||
* 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"
|
Loading…
Reference in New Issue