/* * Copyright 2012-2017 by PaX Team * Licensed under the GPL v2 * * Homepage: http://pax.grsecurity.net/ * * Usage: * $ # for 4.5/4.6/C based 4.7 * $ gcc -I`gcc -print-file-name=plugin`/include -I`gcc -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o rap_plugin.so rap_plugin.c * $ # for C++ based 4.7/4.8+ * $ g++ -I`g++ -print-file-name=plugin`/include -I`g++ -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o rap_plugin.so rap_plugin.c * $ gcc -fplugin=./rap_plugin.so -fplugin-arg-rap_plugin-check=call test.c -O2 */ #include "rap.h" __visible int plugin_is_GPL_compatible; static struct plugin_info rap_plugin_info = { .version = "201612091515", .help = "typecheck=ret,call\tenable the corresponding type hash checking based features\n" "retabort=ud2\t\t\toverride __builtin_trap with specified asm for both kinds of return address checking\n" "callabort=ud2\t\t\toverride __builtin_trap with specified asm for indirect call checking\n" "hash=abs,abs-finish,abs-ops,abs-attr,const,volatile\n" "report=func,fptr,abs\n" }; rap_hash_flags_t imprecise_rap_hash_flags = { .qual_const = 1, .qual_volatile = 1, }; tree rap_hash_type_node; static bool report_func_hash, report_abs_hash; const char *rap_abort_ret; const char *rap_abort_call; bool enable_type_ret = false; bool enable_type_call = false; #define RAP_TARGET_IA 1 #define RAP_TARGET_ARM 2 #ifdef TARGET_386 int rap_target_current = RAP_TARGET_IA; #elif __aarch64__ int rap_target_current = RAP_TARGET_ARM; #endif //==------------------------------------------------------------------------==// // ARM64 is a weak memory model cpu, has more effecient code sequence for // synchronized. // asm("isb"); // asm("dmb"); // asm("ldar": "+rm"(var)); // asm("stlr": :"rm"(var)); // // create the equivalent of // asm volatile("" : : : "memory"); // or // asm("" : "+rm"(var)); // or // asm("" : : "rm"(var)); gimple barrier(tree var, bool full) { gimple stmt; gasm *asm_stmt; #if BUILDING_GCC_VERSION <= 4007 VEC(tree, gc) *insns = NULL; VEC(tree, gc) *inputs = NULL; VEC(tree, gc) *outputs = NULL; VEC(tree, gc) *clobbers = NULL; #else vec *insns = NULL; vec *inputs = NULL; vec *outputs = NULL; vec *clobbers = NULL; #endif tree insn = NULL; if (!var && full) { tree clobber; if (rap_target_current == RAP_TARGET_IA) clobber = build_tree_list(NULL_TREE, build_const_char_string(7, "memory")); else if (rap_target_current == RAP_TARGET_ARM) insn = build_tree_list(NULL_TREE, build_const_char_string(4, "dmb")); else assert(!"rap_target_current broken"); #if BUILDING_GCC_VERSION <= 4007 if (rap_target_current == RAP_TARGET_IA) VEC_safe_push(tree, gc, clobbers, clobber); else if (rap_target_current = RAP_TARGET_ARM) VEC_safe_push(tree, gc, insns, insn); else assert(!"rap_target_current broken"); #else if (rap_target_current == RAP_TARGET_IA) vec_safe_push(clobbers, clobber); else if (rap_target_current == RAP_TARGET_ARM) vec_safe_push(insns, insn); else assert(!"rap_target_current broken"); #endif } else if (full) { tree input, output; input = build_tree_list(NULL_TREE, build_const_char_string(2, "0")); input = chainon(NULL_TREE, build_tree_list(input, var)); #if BUILDING_GCC_VERSION <= 4007 VEC_safe_push(tree, gc, inputs, input); #else vec_safe_push(inputs, input); #endif if (rap_target_current == RAP_TARGET_ARM) insn = build_tree_list(NULL_TREE, build_const_char_string(5, "ldar")); output = build_tree_list(NULL_TREE, build_const_char_string(4, "=rm")); gcc_assert(SSA_NAME_VAR(var)); var = make_ssa_name(SSA_NAME_VAR(var), NULL); output = chainon(NULL_TREE, build_tree_list(output, var)); #if BUILDING_GCC_VERSION <= 4007 if (rap_target_current == RAP_TARGET_ARM) VEC_safe_push(tree, gc, insns, insn); else { assert(rap_target_current == RAP_TARGET_IA && !"rap_target_current broken"); VEC_safe_push(tree, gc, outputs, output); } #else if (rap_target_current == RAP_TARGET_ARM) vec_safe_push(insns, insn); else { assert(rap_target_current == RAP_TARGET_IA && !"rap_target_current broken"); vec_safe_push(outputs, output); } #endif } else { tree insn, input; if (rap_target_current == RAP_TARGET_ARM) insn = build_tree_list(NULL_TREE, build_const_char_string(5, "stlr")); input = build_tree_list(NULL_TREE, build_const_char_string(3, "rm")); input = chainon(NULL_TREE, build_tree_list(input, var)); #if BUILDING_GCC_VERSION <= 4007 if (rap_target_current == RAP_TARGET_ARM) VEC_safe_push(tree, gc, insns, insn); else { assert(rap_target_current == RAP_TARGET_IA && !"rap_target_current broken"); VEC_safe_push(tree, gc, inputs, input); } #else if (rap_target_current == RAP_TARGET_ARM) VEC_safe_push(tree, gc, insns, insn); else { assert(rap_target_current == RAP_TARGET_IA && !"rap_target_current broken"); VEC_safe_push(tree, gc, inputs, input); #endif } stmt = gimple_build_asm_vec("", inputs, outputs, clobbers, NULL); asm_stmt = as_a_gasm(stmt); if (!var && full) gimple_asm_set_volatile(asm_stmt, true); else if (full) SSA_NAME_DEF_STMT(var) = stmt; return stmt; } static const struct gcc_debug_hooks *old_debug_hooks; static struct gcc_debug_hooks rap_debug_hooks; static bool __rap_cgraph_indirectly_callable(cgraph_node_ptr node, void *data __unused) { #if BUILDING_GCC_VERSION >= 4008 if (NODE_SYMBOL(node)->externally_visible) #else if (node->local.externally_visible) #endif return true; if (NODE_SYMBOL(node)->address_taken) return true; return false; } static bool rap_cgraph_indirectly_callable(cgraph_node_ptr node) { return cgraph_for_node_and_aliases(node, __rap_cgraph_indirectly_callable, NULL, true); } static void rap_hash_align(const_tree decl) { unsigned HOST_WIDE_INT rap_hash_offset; unsigned HOST_WIDE_INT skip; skip = 1ULL << align_functions_log; if (DECL_USER_ALIGN(decl)) return; if (!optimize_function_for_speed_p(cfun)) return; if (UNITS_PER_WORD == 8) rap_hash_offset = 2 * sizeof(rap_hash_t); else if (UNITS_PER_WORD == 4) rap_hash_offset = sizeof(rap_hash_t); else gcc_unreachable(); if (skip <= rap_hash_offset) return; #ifdef TARGET_386 { char padding[skip - rap_hash_offset]; // this byte sequence helps disassemblers not trip up on the following rap hash memset(padding, 0xcc, sizeof padding - 1); padding[sizeof padding - 1] = 0xb8; if (TARGET_64BIT && sizeof padding > 1) padding[sizeof padding - 2] = 0x48; ASM_OUTPUT_ASCII(asm_out_file, padding, sizeof padding); } #else ASM_OUTPUT_SKIP(asm_out_file, skip - rap_hash_offset); #endif } static void rap_begin_function(tree decl) { cgraph_node_ptr node; rap_hash_t imprecise_rap_hash; gcc_assert(debug_hooks == &rap_debug_hooks); // chain to previous callback if (old_debug_hooks && old_debug_hooks->begin_function) old_debug_hooks->begin_function(decl); // align the rap hash if necessary rap_hash_align(decl); // don't compute hash for functions called only directly node = cgraph_get_node(decl); gcc_assert(node); if (!rap_cgraph_indirectly_callable(node)) { imprecise_rap_hash.hash = 0; } else { imprecise_rap_hash = rap_hash_function_node_imprecise(node); } if (report_func_hash) inform(DECL_SOURCE_LOCATION(decl), "func rap_hash: %x %s", imprecise_rap_hash.hash, IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl))); if (UNITS_PER_WORD == 8) fprintf(asm_out_file, "\t.quad %#llx\t%s __rap_hash_call_%s\n", (long long)imprecise_rap_hash.hash, ASM_COMMENT_START, IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl))); else fprintf(asm_out_file, "\t.long %#x\t%s __rap_hash_call_%s\n", imprecise_rap_hash.hash, ASM_COMMENT_START, IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(decl))); } static void rap_emit_hash_symbol(const char *type, const char *asmname, rap_hash_t hash) { char *name = NULL; gcc_assert(asprintf(&name, "__rap_hash_%s_%s", type, asmname) != -1); fprintf(asm_out_file, "\t.pushsection .text\n"); fprintf(asm_out_file, GLOBAL_ASM_OP " %s\n", name); if (UNITS_PER_WORD == 8) fprintf(asm_out_file, "\t.offset %#018llx\n", (long long)hash.hash); else if (UNITS_PER_WORD == 4) fprintf(asm_out_file, "\t.offset %#010x\n", hash.hash); else gcc_unreachable(); ASM_OUTPUT_TYPE_DIRECTIVE(asm_out_file, name, "object"); ASM_OUTPUT_LABEL(asm_out_file, name); free(name); fprintf(asm_out_file, "\t.popsection\n"); } static void rap_emit_hash_symbols(const char *asmname, rap_hash_t hash) { #ifdef TARGET_386 rap_emit_hash_symbol("call", asmname, hash); #elif __aarch64__ /* TODO, if branch is over 128MB? */ rap_emit_hash_symbol("bl", asmname, hash); #endif hash.hash = -hash.hash; rap_emit_hash_symbol("ret", asmname, hash); } /* emit an absolute symbol for each function that may be referenced through the plt - all externs - non-static functions - use visibility instead? .globl __rap_hash_call_func .offset 0xhash_for_func .type __rap_hash_call_func, @object __rap_hash_call_func: .previous */ static void rap_finish_unit(void *gcc_data __unused, void *user_data __unused) { cgraph_node_ptr node; rap_hash_t hash; gcc_assert(debug_hooks == &rap_debug_hooks); hash.hash = 0; FOR_EACH_FUNCTION(node) { tree fndecl; const char *asmname; if (node->thunk.thunk_p || node->alias) continue; if (cgraph_function_body_availability(node) >= AVAIL_INTERPOSABLE) { if (!rap_cgraph_indirectly_callable(node)) continue; } #if BUILDING_GCC_VERSION >= 4007 gcc_assert(cgraph_function_or_thunk_node(node, NULL) == node); #endif fndecl = NODE_DECL(node); gcc_assert(fndecl); if (DECL_IS_BUILTIN(fndecl) && DECL_BUILT_IN_CLASS(fndecl) == BUILT_IN_NORMAL) continue; if (!TREE_PUBLIC(fndecl)) continue; if (DECL_ARTIFICIAL(fndecl)) continue; if (DECL_ABSTRACT_ORIGIN(fndecl) && DECL_ABSTRACT_ORIGIN(fndecl) != fndecl) continue; gcc_assert(DECL_ASSEMBLER_NAME(fndecl)); asmname = IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(fndecl)); if (strchr(asmname, '.')) continue; if (asmname[0] == '*') asmname++; gcc_assert(asmname[0]); hash = rap_hash_function_node_imprecise(node); if (report_abs_hash) inform(DECL_SOURCE_LOCATION(fndecl), "abs rap_hash: %x %s", hash.hash, IDENTIFIER_POINTER(DECL_ASSEMBLER_NAME(fndecl))); rap_emit_hash_symbols(asmname, hash); } } // emit the rap hash as an absolute symbol for all functions seen in the frontend // this is necessary as later unreferenced nodes will be removed yet we'd like to emit as many hashes as possible static void rap_emit_hash_symbols_finish_decl(void *event_data, void *data __unused) { tree fndecl = (tree)event_data; rap_hash_t hash; const char *asmname; if (fndecl == error_mark_node) return; if (TREE_CODE(fndecl) != FUNCTION_DECL) return; if (!TREE_PUBLIC(fndecl)) return; if (DECL_ARTIFICIAL(fndecl)) return; if (DECL_ABSTRACT_ORIGIN(fndecl) && DECL_ABSTRACT_ORIGIN(fndecl) != fndecl) return; asmname = DECL_NAME_POINTER(fndecl); gcc_assert(asmname[0]); if (strchr(asmname, '.')) return; // TODO, We need dynamic hash in terms of avail_direct_call set hash = rap_hash_function_decl(fndecl, imprecise_rap_hash_flags); rap_emit_hash_symbols(asmname, hash); if (report_abs_hash) inform(DECL_SOURCE_LOCATION(fndecl), "abs rap_hash: %x %s", hash.hash, asmname); } static void rap_emit_hash_symbols_finish_decl_attr(void *event_data, void *data) { tree fndecl = (tree)event_data; if (fndecl == error_mark_node) return; if (TREE_CODE(fndecl) != FUNCTION_DECL) return; if (!lookup_attribute("rap_hash", TYPE_ATTRIBUTES(TREE_TYPE(fndecl)))) return; rap_emit_hash_symbols_finish_decl(event_data, data); } static void rap_emit_hash_symbols_type(const_tree type, const char *prefix) { const_tree field; if (TYPE_FIELDS(type) == NULL_TREE) return; // TODO skip constified types for now if (TYPE_READONLY(type)) return; // create the prefix if it hasn't been done yet if (!*prefix) { const_tree name = type_name(type); // skip an anonymous struct embedded inside another one // we'll see it when we walk the parent later if (!name) return; prefix = IDENTIFIER_POINTER(name); gcc_assert(*prefix); } for (field = TYPE_FIELDS(type); field; field = TREE_CHAIN(field)) { const_tree fieldtype, fieldname; char *hashname = NULL, *newprefix = NULL; rap_hash_t hash; fieldtype = TREE_TYPE(field); switch (TREE_CODE(fieldtype)) { default: continue; case RECORD_TYPE: case UNION_TYPE: fieldname = DECL_NAME(field); if (!fieldname) continue; gcc_assert(asprintf(&newprefix, "%s.%s", prefix, IDENTIFIER_POINTER(fieldname)) != -1); rap_emit_hash_symbols_type(fieldtype, newprefix); free(newprefix); continue; case POINTER_TYPE: fieldtype = TREE_TYPE(fieldtype); if (TREE_CODE(fieldtype) != FUNCTION_TYPE) continue; hash = rap_hash_function_type(fieldtype, imprecise_rap_hash_flags); fieldname = DECL_NAME(field); gcc_assert(fieldname); gcc_assert(asprintf(&hashname, "%s.%s", prefix, IDENTIFIER_POINTER(fieldname)) != -1); if (report_abs_hash) inform(DECL_SOURCE_LOCATION(field), "abs rap_hash: %x %s", hash.hash, hashname); rap_emit_hash_symbols(hashname, hash); free(hashname); continue; } } } static void rap_emit_hash_symbols_finish_type(void *event_data, void *data __unused) { const_tree type = (const_tree)event_data; if (type == NULL_TREE || type == error_mark_node) return; if (!lookup_attribute("rap_hash", TYPE_ATTRIBUTES(type))) return; switch (TREE_CODE(type)) { default: debug_tree(type); gcc_unreachable(); #if BUILDING_GCC_VERSION >= 5000 case ENUMERAL_TYPE: #endif case UNION_TYPE: break; case RECORD_TYPE: rap_emit_hash_symbols_type(type, ""); break; } } static void rap_assembly_start(void) { gcc_assert(debug_hooks == &rap_debug_hooks); // chain to previous callback if (old_debug_hooks && old_debug_hooks->assembly_start) old_debug_hooks->assembly_start(); #ifdef TARGET_386 #define RAP_SUP_TARGETS #elif __aarch64__ #define RAP_SUP_TARGETS #endif #ifdef RAP_SUP_TARGETS if (enable_type_call) { fprintf(asm_out_file, "\t.macro rap_indirect_call target hash\n" #ifdef TARGET_386 "\t\tjmp 2001f\n" #elif __aarch64__ "\t\tb 2001f\n" #endif "\t\t%s __rap_hash_ret_\\hash\n" "\t\t.skip 8-(2002f-2001f),0xcc\n" #ifdef TARGET_386 "\t2001: call \\target\n" #elif __aarch64__ "\t2001: bl \\target\n" #endif "\t2002:\n" "\t.endm\n", (UNITS_PER_WORD == 8 ? ".quad" : ".long") ); fprintf(asm_out_file, "\t.macro rap_direct_call target hash=""\n" "\t\t.ifb \\hash\n" "\t\trap_indirect_call \\target \\target\n" "\t\t.else\n" "\t\trap_indirect_call \\target \\hash\n" "\t\t.endif\n" "\t.endm\n" ); } if (enable_type_ret) { fprintf(asm_out_file, "\t.macro rap_ret target\n" "\t\tret\n" "\t.endm\n" ); } #error unsupported target #endif #undef RAP_SUP_TARGETS } static void (*old_override_options_after_change)(void); static void rap_override_options_after_change(void) { if (old_override_options_after_change) old_override_options_after_change(); #if BUILDING_GCC_VERSION >= 5000 flag_ipa_icf_functions = 0; #endif flag_crossjumping = 0; flag_optimize_sibling_calls = 0; } static void rap_start_unit_common(void *gcc_data __unused, void *user_data __unused) { rap_hash_type_node = long_integer_type_node; if (debug_hooks) rap_debug_hooks = *debug_hooks; if (enable_type_call || enable_type_ret) rap_debug_hooks.assembly_start = rap_assembly_start; rap_debug_hooks.begin_function = rap_begin_function; old_debug_hooks = debug_hooks; debug_hooks = &rap_debug_hooks; old_override_options_after_change = targetm.override_options_after_change; targetm.override_options_after_change = rap_override_options_after_change; } static bool rap_unignore_gate(void) { if (!DECL_IGNORED_P(current_function_decl)) return false; inform(DECL_SOURCE_LOCATION(current_function_decl), "DECL_IGNORED fixed"); DECL_IGNORED_P(current_function_decl) = 0; return false; } #define PASS_NAME rap_unignore #define NO_EXECUTE #define TODO_FLAGS_FINISH TODO_dump_func #include "gcc-generate-rtl-pass.h" static bool rap_version_check(struct plugin_gcc_version *gcc_version, struct plugin_gcc_version *plugin_version) { if (!gcc_version || !plugin_version) return false; #if BUILDING_GCC_VERSION >= 5000 if (strncmp(gcc_version->basever, plugin_version->basever, 4)) #else if (strcmp(gcc_version->basever, plugin_version->basever)) #endif return false; if (strcmp(gcc_version->datestamp, plugin_version->datestamp)) return false; if (strcmp(gcc_version->devphase, plugin_version->devphase)) return false; if (strcmp(gcc_version->revision, plugin_version->revision)) return false; // if (strcmp(gcc_version->configuration_arguments, plugin_version->configuration_arguments)) // return false; return true; } static tree handle_rap_hash_attribute(tree *node, tree name, tree args __unused, int flags, bool *no_add_attrs) { *no_add_attrs = true; gcc_assert(TYPE_P(*node)); switch (TREE_CODE(*node)) { default: error("%qE attribute applies to structure and function types only (%qT)", name, *node); return NULL_TREE; case FUNCTION_TYPE: case RECORD_TYPE: break; } *no_add_attrs = false; return NULL_TREE; } static struct attribute_spec rap_hash_attr = { .name = "rap_hash", .min_length = 0, .max_length = 0, .decl_required = false, .type_required = true, .function_type_required = false, .handler = handle_rap_hash_attribute, #if BUILDING_GCC_VERSION >= 4007 .affects_type_identity = true #endif }; static void register_attributes(void *event_data __unused, void *data __unused) { register_attribute(&rap_hash_attr); } EXPORTED_CONST struct ggc_root_tab gt_ggc_r_gt_rap[] = { { .base = &rap_hash_type_node, .nelt = 1, .stride = sizeof(rap_hash_type_node), .cb = >_ggc_mx_tree_node, .pchw = >_pch_nx_tree_node }, LAST_GGC_ROOT_TAB }; __visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) { int i; const char * const plugin_name = plugin_info->base_name; const int argc = plugin_info->argc; const struct plugin_argument * const argv = plugin_info->argv; bool enable_abs = false; bool enable_abs_finish = false; bool enable_abs_ops = false; bool enable_abs_attr = false; //==----------------------------------------------------------------==// // Optimization design for RAP: // Add many gcc pass for get two set, hash compute may be need change. // avail_direct_call contains legal destionation procedure of current // procedure, this set computed after the call graph has been down. // avail_indirect_call contains all the legal funtion pointer of current // variable, compute this set need more than one pass, relatied with // function pointer alias. // // Build set for avail_direct_call PASS_INFO(rap_adc_0, "*build_cgraph_edges", 1, PASS_POS_INSERT_AFTER); PASS_INFO(rap_adc_1, "*rebuild_cgraph_edges", 1, PASS_POS_INSERT_AFTER); PASS_INFO(rap_adc_2, "whole_program", 1, PASS_POS_INSERT_AFTER); PASS_INFO(rap_ret, "optimized", 1, hS_INSERT_AFTER); // Build set for avail_indirect_call PASS_INFO(rap_ret, "*all_optimizations_g", 1, PASS_POS_INSERT_AFTER); PASS_INFO(rap_fptrh "rap_ret", 1, PASS_POS_INSERT_AFTER); PASS_INFO(rap_mark_retloc, "mach", 1, PASS_POS_INSERT_AFTER); PASS_INFO(rap_unignore, "final", 1, PASS_POS_INSERT_BEFORE); if (!rap_version_check(version, &gcc_version)) { error_gcc_version(version); return 1; } #if BUILDING_GCC_VERSION >= 5000 if (flag_ipa_icf_functions) { // warning_at(UNKNOWN_LOCATION, 0, G_("-fipa-icf is incompatible with %s, disabling..."), plugin_name); // inform(UNKNOWN_LOCATION, G_("-fipa-icf is incompatible with %s, disabling..."), plugin_name); flag_ipa_icf_functions = 0; } #endif for (i = 0; i < argc; ++i) { if (!strcmp(argv[i].key, "disable")) continue; if (!strcmp(argv[i].key, "typecheck")) { char *values, *value, *saveptr; if (!argv[i].value) { error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); continue; } values = xstrdup(argv[i].value); value = strtok_r(values, ",", &saveptr); while (value) { if (!strcmp(value, "ret")) enable_type_ret = true; else if (!strcmp(value, "call")) enable_type_call = true; else error(G_("unknown value supplied for option '-fplugin-arg-%s-%s=%s'"), plugin_name, argv[i].key, value); value = strtok_r(NULL, ",", &saveptr); } free(values); continue; } if (!strcmp(argv[i].key, "retabort")) { if (!argv[i].value) { error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); continue; } rap_abort_ret = xstrdup(argv[i].value); continue; } if (!strcmp(argv[i].key, "callabort")) { if (!argv[i].value) { error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); continue; } rap_abort_call = xstrdup(argv[i].value); continue; } if (!strcmp(argv[i].key, "hash")) { char *values, *value, *saveptr; if (!argv[i].value) { error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); continue; } values = xstrdup(argv[i].value); value = strtok_r(values, ",", &saveptr); while (value) { if (!strcmp(value, "abs")) enable_abs = enable_abs_finish = true; else if (!strcmp(value, "abs-finish")) enable_abs_finish = true; else if (!strcmp(value, "abs-ops")) enable_abs_ops = true; else if (!strcmp(value, "abs-attr")) enable_abs_attr = true; // else if (!strcmp(value, "const")) // imprecise_rap_hash_flags.qual_const = 1; // else if (!strcmp(value, "volatile")) // imprecise_rap_hash_flags.qual_volatile = 1; else error(G_("unknown value supplied for option '-fplugin-arg-%s-%s=%s'"), plugin_name, argv[i].key, value); value = strtok_r(NULL, ",", &saveptr); } free(values); continue; } if (!strcmp(argv[i].key, "report")) { char *values, *value, *saveptr; if (!argv[i].value) { error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); continue; } values = xstrdup(argv[i].value); value = strtok_r(values, ",", &saveptr); while (value) { if (!strcmp(value, "func")) report_func_hash = true; else if (!strcmp(value, "fptr")) report_fptr_hash = true; else if (!strcmp(value, "abs")) report_abs_hash = true; else error(G_("unknown value supplied for option '-fplugin-arg-%s-%s=%s'"), plugin_name, argv[i].key, value); value = strtok_r(NULL, ",", &saveptr); } free(values); continue; } error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); } register_callback(plugin_name, PLUGIN_INFO, NULL, &rap_plugin_info); if (enable_type_ret) { flag_crossjumping = 0; flag_optimize_sibling_calls = 0; register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &rap_ret_pass_info); register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &rap_mark_retloc_pass_info); } if (enable_type_call || enable_type_ret) { if (enable_abs) #if BUILDING_GCC_VERSION >= 4007 register_callback(plugin_name, PLUGIN_FINISH_DECL, rap_emit_hash_symbols_finish_decl, NULL); #else register_callback(plugin_name, PLUGIN_PRE_GENERICIZE, rap_emit_hash_symbols_finish_decl, NULL); #endif if (enable_abs_ops) register_callback(plugin_name, PLUGIN_FINISH_TYPE, rap_emit_hash_symbols_finish_type, NULL); if (enable_abs_attr) #if BUILDING_GCC_VERSION >= 4007 register_callback(plugin_name, PLUGIN_FINISH_DECL, rap_emit_hash_symbols_finish_decl_attr, NULL); #else register_callback(plugin_name, PLUGIN_PRE_GENERICIZE, rap_emit_hash_symbols_finish_decl_attr, NULL); #endif register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL); register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &rap_unignore_pass_info); register_callback(plugin_name, PLUGIN_START_UNIT, rap_start_unit_common, NULL); register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL, (void *)>_ggc_r_gt_rap); if (enable_abs_finish) register_callback(plugin_name, PLUGIN_FINISH_UNIT, rap_finish_unit, NULL); register_callback(plugin_name, PLUGIN_ALL_IPA_PASSES_START, rap_calculate_func_hashes, NULL); if (!enable_type_ret) rap_fptr_pass_info.reference_pass_name = "optimized"; register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &rap_fptr_pass_info); } return 0; }