alpha version rap code

This commit is contained in:
zet 2017-11-29 15:46:24 +08:00
parent c9d13e9104
commit ebb9b54ddb
12 changed files with 3661 additions and 0 deletions

BIN
.DS_Store vendored

Binary file not shown.

6
src/Makefile Executable file
View File

@ -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

916
src/gcc-common.h Executable file
View File

@ -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

175
src/gcc-generate-gimple-pass.h Executable file
View File

@ -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 */

289
src/gcc-generate-ipa-pass.h Executable file
View File

@ -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 */

175
src/gcc-generate-rtl-pass.h Executable file
View File

@ -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 */

View File

@ -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 */

45
src/rap.h Executable file
View File

@ -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

278
src/rap_fptr_pass.c Executable file
View File

@ -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"

380
src/rap_hash.c Executable file
View File

@ -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(&dividend, 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);
}
}

876
src/rap_plugin.c Executable file
View File

@ -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 = &gt_ggc_mx_tree_node,
.pchw = &gt_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 *)&gt_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;
}

346
src/rap_ret_pass.c Executable file
View File

@ -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"