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