ia32_pic.c 6.26 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*
 * This file is part of libFirm.
 * Copyright (C) 2013 University of Karlsruhe.
 */

/**
 * @file
 * @brief       position independent code adjustments
 * @author      Matthias Braun
 */
11
#include "ia32_bearch_t.h"
12
13

#include "adt/pmap.h"
14
#include "be_t.h"
15
16
#include "begnuas.h"
#include "beirg.h"
17
#include "beutil.h"
18
#include "entity_t.h"
19
#include "ia32_new_nodes.h"
20
21
22
23
#include "ident_t.h"
#include "ircons_t.h"
#include "irgwalk.h"
#include "irnode_t.h"
24
#include "x86_node.h"
25
26
27
28
29
30
31
32

/**
 * Create a trampoline entity for the given method.
 */
static ir_entity *create_trampoline(be_main_env_t *be, ir_entity *method)
{
	ir_type   *type   = get_entity_type(method);
	ident     *old_id = get_entity_ld_ident(method);
33
	ident     *id     = new_id_fmt("%s$stub", old_id);
34
	ir_type   *parent = be->pic_trampolines_type;
35
36
37
	ir_entity *ent    = new_global_entity(parent, id, type,
	                                      ir_visibility_private,
	                                      IR_LINKAGE_DEFAULT);
Matthias Braun's avatar
Matthias Braun committed
38
39
	/* We misuse the ident field to point to the old entity */
	set_entity_ident(ent, old_id);
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
	return ent;
}

/**
 * Returns the trampoline entity for the given method.
 */
static ir_entity *get_trampoline(be_main_env_t *env, ir_entity *method)
{
	ir_entity *result = pmap_get(ir_entity, env->ent_trampoline_map, method);
	if (result == NULL) {
		result = create_trampoline(env, method);
		pmap_insert(env->ent_trampoline_map, method, result);
	}

	return result;
}

57
static ir_entity *create_nonlazyptr(be_main_env_t *be, ir_entity *entity)
58
59
{
	ident     *old_id = get_entity_ld_ident(entity);
60
	ident     *id     = new_id_fmt("%s$non_lazy_ptr", old_id);
61
62
63
	ir_type   *e_type = get_entity_type(entity);
	ir_type   *type   = new_type_pointer(e_type);
	ir_type   *parent = be->pic_symbols_type;
64
65
66
	ir_entity *ent    = new_global_entity(parent, id, type,
	                                      ir_visibility_private,
	                                      IR_LINKAGE_DEFAULT);
Matthias Braun's avatar
Matthias Braun committed
67
	set_entity_ident(ent, old_id);
68
69
70
71

	return ent;
}

72
static ir_entity *get_nonlazyptr(be_main_env_t *env, ir_entity *entity)
73
74
75
{
	ir_entity *result = pmap_get(ir_entity, env->ent_pic_symbol_map, entity);
	if (result == NULL) {
76
		result = create_nonlazyptr(env, entity);
77
78
79
80
81
		pmap_insert(env->ent_pic_symbol_map, entity, result);
	}
	return result;
}

82
83
84
static ir_node *get_eip_relative(ir_graph *const irg,
                                 x86_immediate_kind_t const kind,
								 ir_entity *const entity)
85
{
86
87
	/* Everything else is accessed relative to EIP. */
	ir_node *const pic_base = ia32_get_pic_base(irg);
88
89
	/* cheat a bit and set pic_base node to mode_P for now */
	set_irn_mode(pic_base, mode_P);
90
	ir_node *const block    = get_irg_start_block(irg);
91
92
	ir_mode *const offset_mode = get_reference_offset_mode(mode_P);
	ir_node *reloc = be_new_Relocation(irg, (unsigned)kind, entity, offset_mode);
93
	/* All ok now for locally constructed stuff. */
94
	ir_node *const add      = new_rd_Add(NULL, block, pic_base, reloc);
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
	/* Make sure the walker doesn't visit this add again. */
	mark_irn_visited(add);
	return add;
}

static ir_node *get_table_load(ir_graph *irg, x86_immediate_kind_t const kind,
                               ir_entity *const entity)
{
	ir_node *const addr  = get_eip_relative(irg, kind, entity);
	ir_type *const type  = get_entity_type(entity);
	ir_node *const nomem = get_irg_no_mem(irg);
	ir_node *const block = get_irg_start_block(irg);
	ir_node *const load  = new_rd_Load(NULL, block, nomem, addr, mode_P, type,
	                                   cons_floats);
	return new_r_Proj(load, mode_P, pn_Load_res);
}

static void fix_address_elf(ir_node *const node, void *const data)
{
	(void)data;
	foreach_irn_in(node, i, pred) {
		if (!is_Address(pred))
			continue;
		ir_entity *const entity = get_Address_entity(pred);
		if (is_tls_entity(entity))
			continue;

		ir_graph *const irg = get_irn_irg(node);
		ir_node  *      res;
		if (i == n_Call_ptr && is_Call(node)) {
			/* Calls can jump to relative addresses, so we can directly jump to
			 * the (relatively) known call address or the trampoline */
			if (entity_has_definition(entity)
			 && !(get_entity_linkage(entity) & IR_LINKAGE_MERGE))
				continue;

131
			if (be_options.pic_style == BE_PIC_ELF_PLT) {
132
				res = be_new_Relocation(irg, X86_IMM_PLT, entity, mode_P);
133
			} else {
134
				assert(be_options.pic_style == BE_PIC_ELF_NO_PLT);
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
				res = get_table_load(irg, X86_IMM_GOT, entity);
			}
		} else {
			ir_visibility const visibility = get_entity_visibility(entity);
			if (visibility != ir_visibility_external
			 && visibility != ir_visibility_external_private) {
				assert(visibility == ir_visibility_local
				    || visibility == ir_visibility_private);
				res = get_eip_relative(irg, X86_IMM_GOTOFF, entity);
			} else {
				res = get_table_load(irg, X86_IMM_GOT, entity);
			}
		}
		set_irn_n(node, i, res);
	}
150
151
}

152
/** patches Addresses to work in position independent code */
153
static void fix_address_macho(ir_node *const node, void *const data)
154
{
155
	(void)data;
156

157
	foreach_irn_in(node, i, pred) {
158
		if (!is_Address(pred))
159
			continue;
160
		ir_entity *const entity = get_Address_entity(pred);
161
162
163
164
165
166
		if (is_tls_entity(entity))
			continue;

		ir_graph      *const irg = get_irn_irg(node);
		be_main_env_t *const be  = be_get_irg_main_env(irg);
		ir_node             *res;
167
168
169
		if (i == n_Call_ptr && is_Call(node)) {
			/* Calls can jump to relative addresses, so we can directly jump to
			 * the (relatively) known call address or the trampoline */
170
171
			if (entity_has_definition(entity)
			 && !(get_entity_linkage(entity) & IR_LINKAGE_MERGE))
172
173
				continue;

174
			ir_entity *const trampoline = get_trampoline(be, entity);
175
			res = be_new_Relocation(irg, X86_IMM_ADDR, trampoline, mode_P);
176
177
		} else {
			/* Everything else is accessed relative to EIP. */
178
179
180
			if (entity_has_definition(entity)
			 && !(get_entity_linkage(entity) & IR_LINKAGE_MERGE)) {
				res = get_eip_relative(irg, X86_IMM_PICBASE_REL, entity);
181
			} else {
182
183
				ir_entity *const nonlazyptr = get_nonlazyptr(be, entity);
				res = get_table_load(irg, X86_IMM_PICBASE_REL, nonlazyptr);
184
			}
185
		}
186
		set_irn_n(node, i, res);
187
188
189
190
191
	}
}

void ia32_adjust_pic(ir_graph *irg)
{
192
193
	switch (be_options.pic_style) {
	case BE_PIC_NONE:
194
		return;
195
196
	case BE_PIC_ELF_PLT:
	case BE_PIC_ELF_NO_PLT:
197
		irg_walk_graph(irg, fix_address_elf, NULL, NULL);
198
		return;
199
	case BE_PIC_MACH_O:
200
201
202
		irg_walk_graph(irg, fix_address_macho, NULL, NULL);
		return;
	}
203
}