578 lines
16 KiB
C
578 lines
16 KiB
C
|
/*
|
||
|
* Copyright 2011 by Emese Revfy <re.emese@gmail.com>
|
||
|
* Copyright 2011-2017 by PaX Team <pageexec@freemail.hu>
|
||
|
* Licensed under the GPL v2, or (at your option) v3
|
||
|
*
|
||
|
* This gcc plugin constifies all structures which contain only function pointers or are explicitly marked for constification.
|
||
|
*
|
||
|
* Homepage:
|
||
|
* http://www.grsecurity.net/~ephox/const_plugin/
|
||
|
*
|
||
|
* Usage:
|
||
|
* $ gcc -I`gcc -print-file-name=plugin`/include -fPIC -shared -O2 -o constify_plugin.so constify_plugin.c
|
||
|
* $ gcc -fplugin=constify_plugin.so test.c -O2
|
||
|
*/
|
||
|
|
||
|
#include "gcc-common.h"
|
||
|
|
||
|
// unused C type flag in all versions 4.5-6
|
||
|
#define TYPE_CONSTIFY_VISITED(TYPE) TYPE_LANG_FLAG_4(TYPE)
|
||
|
|
||
|
__visible int plugin_is_GPL_compatible;
|
||
|
|
||
|
static bool enabled = true;
|
||
|
|
||
|
static struct plugin_info const_plugin_info = {
|
||
|
.version = "201607241840",
|
||
|
.help = "disable\tturn off constification\n",
|
||
|
};
|
||
|
|
||
|
static struct {
|
||
|
const char *name;
|
||
|
const char *asm_op;
|
||
|
} const_sections[] = {
|
||
|
{".init.rodata", "\t.section\t.init.rodata,\"a\""},
|
||
|
{".ref.rodata", "\t.section\t.ref.rodata,\"a\""},
|
||
|
{".devinit.rodata", "\t.section\t.devinit.rodata,\"a\""},
|
||
|
{".devexit.rodata", "\t.section\t.devexit.rodata,\"a\""},
|
||
|
{".cpuinit.rodata", "\t.section\t.cpuinit.rodata,\"a\""},
|
||
|
{".cpuexit.rodata", "\t.section\t.cpuexit.rodata,\"a\""},
|
||
|
{".meminit.rodata", "\t.section\t.meminit.rodata,\"a\""},
|
||
|
{".memexit.rodata", "\t.section\t.memexit.rodata,\"a\""},
|
||
|
{".data..read_only", "\t.section\t.data..read_only,\"a\""},
|
||
|
};
|
||
|
|
||
|
typedef struct {
|
||
|
bool has_fptr_field;
|
||
|
bool has_writable_field;
|
||
|
bool has_do_const_field;
|
||
|
bool has_no_const_field;
|
||
|
} constify_info;
|
||
|
|
||
|
static const_tree get_field_type(const_tree field)
|
||
|
{
|
||
|
return strip_array_types(TREE_TYPE(field));
|
||
|
}
|
||
|
|
||
|
static bool is_fptr(const_tree field)
|
||
|
{
|
||
|
const_tree ptr = get_field_type(field);
|
||
|
|
||
|
if (TREE_CODE(ptr) != POINTER_TYPE)
|
||
|
return false;
|
||
|
|
||
|
return TREE_CODE(TREE_TYPE(ptr)) == FUNCTION_TYPE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* determine whether the given structure type meets the requirements for automatic constification,
|
||
|
* including the constification attributes on nested structure types
|
||
|
*/
|
||
|
static void constifiable(const_tree node, constify_info *cinfo)
|
||
|
{
|
||
|
const_tree field;
|
||
|
|
||
|
gcc_assert(TREE_CODE(node) == RECORD_TYPE || TREE_CODE(node) == UNION_TYPE);
|
||
|
|
||
|
// e.g., pointer to structure fields while still constructing the structure type
|
||
|
if (TYPE_FIELDS(node) == NULL_TREE)
|
||
|
return;
|
||
|
|
||
|
for (field = TYPE_FIELDS(node); field; field = TREE_CHAIN(field)) {
|
||
|
const_tree type = get_field_type(field);
|
||
|
enum tree_code code = TREE_CODE(type);
|
||
|
|
||
|
if (node == type)
|
||
|
continue;
|
||
|
|
||
|
if (is_fptr(field))
|
||
|
cinfo->has_fptr_field = true;
|
||
|
else if (code == RECORD_TYPE || code == UNION_TYPE) {
|
||
|
if (lookup_attribute("do_const", TYPE_ATTRIBUTES(type)))
|
||
|
cinfo->has_do_const_field = true;
|
||
|
else if (lookup_attribute("no_const", TYPE_ATTRIBUTES(type)))
|
||
|
cinfo->has_no_const_field = true;
|
||
|
else
|
||
|
constifiable(type, cinfo);
|
||
|
} else if (!TREE_READONLY(field))
|
||
|
cinfo->has_writable_field = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool constified(const_tree node)
|
||
|
{
|
||
|
constify_info cinfo = {
|
||
|
.has_fptr_field = false,
|
||
|
.has_writable_field = false,
|
||
|
.has_do_const_field = false,
|
||
|
.has_no_const_field = false
|
||
|
};
|
||
|
|
||
|
gcc_assert(TREE_CODE(node) == RECORD_TYPE || TREE_CODE(node) == UNION_TYPE);
|
||
|
|
||
|
if (lookup_attribute("no_const", TYPE_ATTRIBUTES(node))) {
|
||
|
// gcc_assert(!TYPE_READONLY(node));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (lookup_attribute("do_const", TYPE_ATTRIBUTES(node))) {
|
||
|
gcc_assert(TYPE_READONLY(node));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
constifiable(node, &cinfo);
|
||
|
if ((!cinfo.has_fptr_field || cinfo.has_writable_field || cinfo.has_no_const_field) && !cinfo.has_do_const_field)
|
||
|
return false;
|
||
|
|
||
|
return TYPE_READONLY(node);
|
||
|
}
|
||
|
|
||
|
static void deconstify_tree(tree node);
|
||
|
|
||
|
static void deconstify_type(tree type)
|
||
|
{
|
||
|
tree field;
|
||
|
|
||
|
gcc_assert(TREE_CODE(type) == RECORD_TYPE || TREE_CODE(type) == UNION_TYPE);
|
||
|
|
||
|
for (field = TYPE_FIELDS(type); field; field = TREE_CHAIN(field)) {
|
||
|
const_tree fieldtype = get_field_type(field);
|
||
|
|
||
|
// special case handling of simple ptr-to-same-array-type members
|
||
|
if (TREE_CODE(TREE_TYPE(field)) == POINTER_TYPE) {
|
||
|
tree ptrtype = TREE_TYPE(TREE_TYPE(field));
|
||
|
|
||
|
if (TREE_TYPE(TREE_TYPE(field)) == type)
|
||
|
continue;
|
||
|
if (TREE_CODE(ptrtype) != RECORD_TYPE && TREE_CODE(ptrtype) != UNION_TYPE)
|
||
|
continue;
|
||
|
if (!constified(ptrtype))
|
||
|
continue;
|
||
|
if (TYPE_MAIN_VARIANT(ptrtype) == TYPE_MAIN_VARIANT(type))
|
||
|
TREE_TYPE(field) = build_pointer_type(build_qualified_type(type, TYPE_QUALS(ptrtype) & ~TYPE_QUAL_CONST));
|
||
|
continue;
|
||
|
}
|
||
|
if (TREE_CODE(fieldtype) != RECORD_TYPE && TREE_CODE(fieldtype) != UNION_TYPE)
|
||
|
continue;
|
||
|
if (!constified(fieldtype))
|
||
|
continue;
|
||
|
|
||
|
deconstify_tree(field);
|
||
|
TREE_READONLY(field) = 0;
|
||
|
}
|
||
|
TYPE_READONLY(type) = 0;
|
||
|
C_TYPE_FIELDS_READONLY(type) = 0;
|
||
|
if (lookup_attribute("do_const", TYPE_ATTRIBUTES(type))) {
|
||
|
TYPE_ATTRIBUTES(type) = copy_list(TYPE_ATTRIBUTES(type));
|
||
|
TYPE_ATTRIBUTES(type) = remove_attribute("do_const", TYPE_ATTRIBUTES(type));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void deconstify_tree(tree node)
|
||
|
{
|
||
|
tree old_type, new_type, field;
|
||
|
|
||
|
old_type = TREE_TYPE(node);
|
||
|
while (TREE_CODE(old_type) == ARRAY_TYPE && TREE_CODE(TREE_TYPE(old_type)) != ARRAY_TYPE) {
|
||
|
node = TREE_TYPE(node) = copy_node(old_type);
|
||
|
old_type = TREE_TYPE(old_type);
|
||
|
}
|
||
|
|
||
|
gcc_assert(TREE_CODE(old_type) == RECORD_TYPE || TREE_CODE(old_type) == UNION_TYPE);
|
||
|
gcc_assert(TYPE_READONLY(old_type) && (TYPE_QUALS(old_type) & TYPE_QUAL_CONST));
|
||
|
|
||
|
new_type = build_qualified_type(old_type, TYPE_QUALS(old_type) & ~TYPE_QUAL_CONST);
|
||
|
TYPE_FIELDS(new_type) = copy_list(TYPE_FIELDS(new_type));
|
||
|
for (field = TYPE_FIELDS(new_type); field; field = TREE_CHAIN(field))
|
||
|
DECL_FIELD_CONTEXT(field) = new_type;
|
||
|
|
||
|
deconstify_type(new_type);
|
||
|
|
||
|
TREE_TYPE(node) = new_type;
|
||
|
}
|
||
|
|
||
|
static tree handle_no_const_attribute(tree *node, tree name, tree args __unused, int flags __unused, bool *no_add_attrs)
|
||
|
{
|
||
|
tree type;
|
||
|
constify_info cinfo = {
|
||
|
.has_fptr_field = false,
|
||
|
.has_writable_field = false,
|
||
|
.has_do_const_field = false,
|
||
|
.has_no_const_field = false
|
||
|
};
|
||
|
|
||
|
*no_add_attrs = true;
|
||
|
if (TREE_CODE(*node) == FUNCTION_DECL) {
|
||
|
error("%qE attribute does not apply to functions (%qF)", name, *node);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (TREE_CODE(*node) == PARM_DECL) {
|
||
|
error("%qE attribute does not apply to function parameters (%qD)", name, *node);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (TREE_CODE(*node) == VAR_DECL) {
|
||
|
error("%qE attribute does not apply to variables (%qD)", name, *node);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (TYPE_P(*node)) {
|
||
|
type = *node;
|
||
|
} else {
|
||
|
if (TREE_CODE(*node) != TYPE_DECL) {
|
||
|
error("%qE attribute does not apply to %qD (%qT)", name, *node, TREE_TYPE(*node));
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
type = TREE_TYPE(*node);
|
||
|
}
|
||
|
|
||
|
if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE) {
|
||
|
error("%qE attribute used on %qT applies to struct and union types only", name, type);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (lookup_attribute(IDENTIFIER_POINTER(name), TYPE_ATTRIBUTES(type))) {
|
||
|
error("%qE attribute is already applied to the type %qT", name, type);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (TYPE_P(*node)) {
|
||
|
if (lookup_attribute("do_const", TYPE_ATTRIBUTES(type)))
|
||
|
error("%qE attribute used on type %qT is incompatible with 'do_const'", name, type);
|
||
|
else
|
||
|
*no_add_attrs = false;
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
constifiable(type, &cinfo);
|
||
|
if ((cinfo.has_fptr_field && !cinfo.has_writable_field && !cinfo.has_no_const_field) || lookup_attribute("do_const", TYPE_ATTRIBUTES(type))) {
|
||
|
if (enabled) {
|
||
|
if TYPE_P(*node)
|
||
|
deconstify_type(*node);
|
||
|
else
|
||
|
deconstify_tree(*node);
|
||
|
}
|
||
|
if (TYPE_P(*node))
|
||
|
TYPE_CONSTIFY_VISITED(*node) = 1;
|
||
|
else
|
||
|
TYPE_CONSTIFY_VISITED(TREE_TYPE(*node)) = 1;
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (enabled && TYPE_FIELDS(type))
|
||
|
error("%qE attribute used on type %qT that is not constified", name, type);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
static void constify_type(tree type)
|
||
|
{
|
||
|
gcc_assert(type == TYPE_MAIN_VARIANT(type));
|
||
|
TYPE_READONLY(type) = 1;
|
||
|
C_TYPE_FIELDS_READONLY(type) = 1;
|
||
|
TYPE_CONSTIFY_VISITED(type) = 1;
|
||
|
}
|
||
|
|
||
|
static tree handle_do_const_attribute(tree *node, tree name, tree args __unused, int flags __unused, bool *no_add_attrs)
|
||
|
{
|
||
|
*no_add_attrs = true;
|
||
|
if (!TYPE_P(*node)) {
|
||
|
error("%qE attribute applies to types only (%qD)", name, *node);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (TREE_CODE(*node) != RECORD_TYPE && TREE_CODE(*node) != UNION_TYPE) {
|
||
|
error("%qE attribute used on %qT applies to struct and union types only", name, *node);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (lookup_attribute(IDENTIFIER_POINTER(name), TYPE_ATTRIBUTES(*node))) {
|
||
|
error("%qE attribute used on %qT is already applied to the type", name, *node);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
if (lookup_attribute("no_const", TYPE_ATTRIBUTES(*node))) {
|
||
|
error("%qE attribute used on %qT is incompatible with 'no_const'", name, *node);
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
*no_add_attrs = false;
|
||
|
return NULL_TREE;
|
||
|
}
|
||
|
|
||
|
static struct attribute_spec no_const_attr = {
|
||
|
.name = "no_const",
|
||
|
.min_length = 0,
|
||
|
.max_length = 0,
|
||
|
.decl_required = false,
|
||
|
.type_required = false,
|
||
|
.function_type_required = false,
|
||
|
.handler = handle_no_const_attribute,
|
||
|
#if BUILDING_GCC_VERSION >= 4007
|
||
|
.affects_type_identity = true
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static struct attribute_spec do_const_attr = {
|
||
|
.name = "do_const",
|
||
|
.min_length = 0,
|
||
|
.max_length = 0,
|
||
|
.decl_required = false,
|
||
|
.type_required = false,
|
||
|
.function_type_required = false,
|
||
|
.handler = handle_do_const_attribute,
|
||
|
#if BUILDING_GCC_VERSION >= 4007
|
||
|
.affects_type_identity = true
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static void register_attributes(void *event_data, void *data)
|
||
|
{
|
||
|
register_attribute(&no_const_attr);
|
||
|
register_attribute(&do_const_attr);
|
||
|
}
|
||
|
|
||
|
static void finish_type(void *event_data, void *data __unused)
|
||
|
{
|
||
|
tree type = (tree)event_data;
|
||
|
constify_info cinfo = {
|
||
|
.has_fptr_field = false,
|
||
|
.has_writable_field = false,
|
||
|
.has_do_const_field = false,
|
||
|
.has_no_const_field = false
|
||
|
};
|
||
|
|
||
|
if (type == NULL_TREE || type == error_mark_node)
|
||
|
return;
|
||
|
|
||
|
#if BUILDING_GCC_VERSION >= 5000
|
||
|
if (TREE_CODE(type) == ENUMERAL_TYPE)
|
||
|
return;
|
||
|
#endif
|
||
|
|
||
|
if (TYPE_FIELDS(type) == NULL_TREE || TYPE_CONSTIFY_VISITED(type))
|
||
|
return;
|
||
|
|
||
|
constifiable(type, &cinfo);
|
||
|
|
||
|
if (lookup_attribute("no_const", TYPE_ATTRIBUTES(type))) {
|
||
|
if ((cinfo.has_fptr_field && !cinfo.has_writable_field && !cinfo.has_no_const_field) || cinfo.has_do_const_field) {
|
||
|
deconstify_type(type);
|
||
|
TYPE_CONSTIFY_VISITED(type) = 1;
|
||
|
} else
|
||
|
error("'no_const' attribute used on type %qT that is not constified", type);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (lookup_attribute("do_const", TYPE_ATTRIBUTES(type))) {
|
||
|
if (!cinfo.has_writable_field && !cinfo.has_no_const_field) {
|
||
|
error("'do_const' attribute used on type %qT that is%sconstified", type, cinfo.has_fptr_field ? " " : " not ");
|
||
|
return;
|
||
|
}
|
||
|
constify_type(type);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cinfo.has_fptr_field && !cinfo.has_writable_field && !cinfo.has_no_const_field) {
|
||
|
if (lookup_attribute("do_const", TYPE_ATTRIBUTES(type))) {
|
||
|
error("'do_const' attribute used on type %qT that is constified", type);
|
||
|
return;
|
||
|
}
|
||
|
constify_type(type);
|
||
|
// TYPE_ATTRIBUTES(type) = copy_list(TYPE_ATTRIBUTES(type));
|
||
|
// TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("do_const"), NULL_TREE, TYPE_ATTRIBUTES(type));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
deconstify_type(type);
|
||
|
TYPE_CONSTIFY_VISITED(type) = 1;
|
||
|
}
|
||
|
|
||
|
static bool is_constified_var(varpool_node_ptr node)
|
||
|
{
|
||
|
tree var = NODE_DECL(node);
|
||
|
tree type = TREE_TYPE(var);
|
||
|
|
||
|
if (node->alias)
|
||
|
return false;
|
||
|
|
||
|
if (DECL_EXTERNAL(var))
|
||
|
return false;
|
||
|
|
||
|
// XXX handle more complex nesting of arrays/structs
|
||
|
if (TREE_CODE(type) == ARRAY_TYPE)
|
||
|
type = TREE_TYPE(type);
|
||
|
|
||
|
if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
|
||
|
return false;
|
||
|
|
||
|
if (!TYPE_READONLY(type) || !C_TYPE_FIELDS_READONLY(type))
|
||
|
return false;
|
||
|
|
||
|
if (!TYPE_CONSTIFY_VISITED(type))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void check_section_mismatch(varpool_node_ptr node)
|
||
|
{
|
||
|
tree var, section;
|
||
|
size_t i;
|
||
|
const char *name;
|
||
|
|
||
|
var = NODE_DECL(node);
|
||
|
name = get_decl_section_name(var);
|
||
|
section = lookup_attribute("section", DECL_ATTRIBUTES(var));
|
||
|
if (!section) {
|
||
|
if (name) {
|
||
|
fprintf(stderr, "DECL_SECTION [%s] ", name);
|
||
|
dump_varpool_node(stderr, node);
|
||
|
gcc_unreachable();
|
||
|
}
|
||
|
return;
|
||
|
} else
|
||
|
gcc_assert(name);
|
||
|
|
||
|
//fprintf(stderr, "SECTIONAME: [%s] ", get_decl_section_name(var));
|
||
|
//debug_tree(var);
|
||
|
|
||
|
gcc_assert(!TREE_CHAIN(section));
|
||
|
gcc_assert(TREE_VALUE(section));
|
||
|
|
||
|
section = TREE_VALUE(TREE_VALUE(section));
|
||
|
gcc_assert(!strcmp(TREE_STRING_POINTER(section), name));
|
||
|
//debug_tree(section);
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(const_sections); i++)
|
||
|
if (!strcmp(const_sections[i].name, name))
|
||
|
return;
|
||
|
|
||
|
error_at(DECL_SOURCE_LOCATION(var), "constified variable %qD placed into writable section %E", var, section);
|
||
|
}
|
||
|
|
||
|
// this works around a gcc bug/feature where uninitialized globals
|
||
|
// are moved into the .bss section regardless of any constification
|
||
|
// see gcc/varasm.c:bss_initializer_p()
|
||
|
static void fix_initializer(varpool_node_ptr node)
|
||
|
{
|
||
|
tree var = NODE_DECL(node);
|
||
|
tree type = TREE_TYPE(var);
|
||
|
|
||
|
if (DECL_INITIAL(var))
|
||
|
return;
|
||
|
|
||
|
DECL_INITIAL(var) = build_constructor(type, NULL);
|
||
|
// inform(DECL_SOURCE_LOCATION(var), "constified variable %qE moved into .rodata", var);
|
||
|
}
|
||
|
|
||
|
static void check_global_variables(void *event_data __unused, void *data __unused)
|
||
|
{
|
||
|
varpool_node_ptr node;
|
||
|
|
||
|
FOR_EACH_VARIABLE(node) {
|
||
|
if (!is_constified_var(node))
|
||
|
continue;
|
||
|
|
||
|
check_section_mismatch(node);
|
||
|
fix_initializer(node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static unsigned int check_local_variables_execute(void)
|
||
|
{
|
||
|
unsigned int ret = 0;
|
||
|
tree var;
|
||
|
|
||
|
unsigned int i;
|
||
|
|
||
|
FOR_EACH_LOCAL_DECL(cfun, i, var) {
|
||
|
tree type = TREE_TYPE(var);
|
||
|
|
||
|
gcc_assert(DECL_P(var));
|
||
|
if (is_global_var(var))
|
||
|
continue;
|
||
|
|
||
|
if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
|
||
|
continue;
|
||
|
|
||
|
if (!TYPE_READONLY(type) || !C_TYPE_FIELDS_READONLY(type))
|
||
|
continue;
|
||
|
|
||
|
if (!TYPE_CONSTIFY_VISITED(type))
|
||
|
continue;
|
||
|
|
||
|
error_at(DECL_SOURCE_LOCATION(var), "constified variable %qE cannot be local", var);
|
||
|
ret = 1;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#define PASS_NAME check_local_variables
|
||
|
#define NO_GATE
|
||
|
#include "gcc-generate-gimple-pass.h"
|
||
|
|
||
|
static unsigned int (*old_section_type_flags)(tree decl, const char *name, int reloc);
|
||
|
|
||
|
static unsigned int constify_section_type_flags(tree decl, const char *name, int reloc)
|
||
|
{
|
||
|
size_t i;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(const_sections); i++)
|
||
|
if (!strcmp(const_sections[i].name, name))
|
||
|
return 0;
|
||
|
|
||
|
return old_section_type_flags(decl, name, reloc);
|
||
|
}
|
||
|
|
||
|
static void constify_start_unit(void *gcc_data __unused, void *user_data __unused)
|
||
|
{
|
||
|
// size_t i;
|
||
|
|
||
|
// for (i = 0; i < ARRAY_SIZE(const_sections); i++)
|
||
|
// const_sections[i].section = get_unnamed_section(0, output_section_asm_op, const_sections[i].asm_op);
|
||
|
// const_sections[i].section = get_section(const_sections[i].name, 0, NULL);
|
||
|
|
||
|
old_section_type_flags = targetm.section_type_flags;
|
||
|
targetm.section_type_flags = constify_section_type_flags;
|
||
|
}
|
||
|
|
||
|
__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
|
||
|
{
|
||
|
const char * const plugin_name = plugin_info->base_name;
|
||
|
const int argc = plugin_info->argc;
|
||
|
const struct plugin_argument * const argv = plugin_info->argv;
|
||
|
int i;
|
||
|
|
||
|
PASS_INFO(check_local_variables, "ssa", 1, PASS_POS_INSERT_BEFORE);
|
||
|
|
||
|
if (!plugin_default_version_check(version, &gcc_version)) {
|
||
|
error_gcc_version(version);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < argc; ++i) {
|
||
|
if (!(strcmp(argv[i].key, "disable"))) {
|
||
|
enabled = false;
|
||
|
continue;
|
||
|
}
|
||
|
error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
|
||
|
}
|
||
|
|
||
|
if (strncmp(lang_hooks.name, "GNU C", 5) && !strncmp(lang_hooks.name, "GNU C+", 6)) {
|
||
|
inform(UNKNOWN_LOCATION, G_("%s supports C only, not %s"), plugin_name, lang_hooks.name);
|
||
|
enabled = false;
|
||
|
}
|
||
|
|
||
|
register_callback(plugin_name, PLUGIN_INFO, NULL, &const_plugin_info);
|
||
|
if (enabled) {
|
||
|
register_callback(plugin_name, PLUGIN_ALL_IPA_PASSES_START, check_global_variables, NULL);
|
||
|
register_callback(plugin_name, PLUGIN_FINISH_TYPE, finish_type, NULL);
|
||
|
register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &check_local_variables_pass_info);
|
||
|
register_callback(plugin_name, PLUGIN_START_UNIT, constify_start_unit, NULL);
|
||
|
}
|
||
|
register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL);
|
||
|
|
||
|
return 0;
|
||
|
}
|