Commit f345dbf8 authored by Matthias Braun's avatar Matthias Braun
Browse files

ia32: PIC rewrite, handles most of elf PIC now

This uses the newly introduced be_Relocation node to perform most PIC
transformations in ia32_pic.c. This introduces the elf and elf-no-plt
PIC modes. The "efl" mode does not work yet because the additional ebx
input makes calls register pressure unfaithful.
parent 84b2a7e3
......@@ -201,7 +201,7 @@ typedef enum amd64_emit_mod_t {
ENUM_BITSET(amd64_emit_mod_t)
static void emit_relocation_no_offset(x86_immediate_kind_t const kind,
ir_entity const *const entity)
ir_entity const *const entity)
{
be_gas_emit_entity(entity);
switch (kind) {
......@@ -212,6 +212,9 @@ static void emit_relocation_no_offset(x86_immediate_kind_t const kind,
case X86_IMM_TLS_LE:
case X86_IMM_PICBASE_REL:
case X86_IMM_FRAMEOFFSET:
case X86_IMM_GOT:
case X86_IMM_GOTOFF:
case X86_IMM_PLT:
break;
}
panic("unexpected or invalid immediate kind");
......
......@@ -1609,6 +1609,8 @@ static const backend_params *ia32_get_libfirm_params(void)
static const lc_opt_enum_int_items_t pic_style_items[] = {
{ "none", IA32_PIC_NONE },
{ "mach-o", IA32_PIC_MACH_O },
{ "elf", IA32_PIC_ELF_PLT },
{ "elf-noplt", IA32_PIC_ELF_NO_PLT },
{ NULL, IA32_PIC_NONE },
};
static lc_opt_enum_int_var_t pic_style_var = {
......
......@@ -26,6 +26,8 @@
typedef enum ia32_pic_style_t {
IA32_PIC_NONE,
IA32_PIC_MACH_O,
IA32_PIC_ELF_PLT,
IA32_PIC_ELF_NO_PLT,
} ia32_pic_style_t;
typedef struct ia32_irg_data_t {
......
......@@ -183,12 +183,11 @@ static void ia32_emit_relocation(x86_imm32_t const *const imm)
switch (imm->kind) {
case X86_IMM_ADDR:
return;
case X86_IMM_TLS_IE:
be_emit_cstring("@INDNTPOFF");
return;
case X86_IMM_TLS_LE:
be_emit_cstring("@NTPOFF");
return;
case X86_IMM_TLS_IE: be_emit_cstring("@INDNTPOFF"); return;
case X86_IMM_TLS_LE: be_emit_cstring("@NTPOFF"); return;
case X86_IMM_GOT: be_emit_cstring("@GOT"); return;
case X86_IMM_GOTOFF: be_emit_cstring("@GOTOFF"); return;
case X86_IMM_PLT: be_emit_cstring("@PLT"); return;
case X86_IMM_PICBASE_REL:
be_emit_char('-');
be_emit_string(pic_base_label);
......@@ -1234,8 +1233,19 @@ static void emit_ia32_GetEIP(const ir_node *node)
}
ia32_emitf(node, "call %E", thunk);
be_emit_irprintf("%s:\n", pic_base_label);
be_emit_write_line();
switch (ia32_pic_style) {
case IA32_PIC_MACH_O:
be_emit_irprintf("%s:\n", pic_base_label);
be_emit_write_line();
return;
case IA32_PIC_ELF_PLT:
case IA32_PIC_ELF_NO_PLT:
ia32_emitf(node, "addl $_GLOBAL_OFFSET_TABLE_, %D0");
return;
case IA32_PIC_NONE:
break;
}
panic("invalid pic_style");
}
static void emit_ia32_ClimbFrame(const ir_node *node)
......
......@@ -11,14 +11,17 @@
#include "bearch_ia32_t.h"
#include "adt/pmap.h"
#include "be_t.h"
#include "begnuas.h"
#include "beirg.h"
#include "be_t.h"
#include "beutil.h"
#include "entity_t.h"
#include "ia32_new_nodes.h"
#include "ident_t.h"
#include "ircons_t.h"
#include "irgwalk.h"
#include "irnode_t.h"
#include "x86_imm.h"
ia32_pic_style_t ia32_pic_style = IA32_PIC_NONE;
......@@ -52,7 +55,7 @@ static ir_entity *get_trampoline(be_main_env_t *env, ir_entity *method)
return result;
}
static ir_entity *create_pic_symbol(be_main_env_t *be, ir_entity *entity)
static ir_entity *create_nonlazyptr(be_main_env_t *be, ir_entity *entity)
{
ident *old_id = get_entity_ld_ident(entity);
ident *id = new_id_fmt("%s$non_lazy_ptr", old_id);
......@@ -66,75 +69,115 @@ static ir_entity *create_pic_symbol(be_main_env_t *be, ir_entity *entity)
return ent;
}
static ir_entity *get_pic_symbol(be_main_env_t *env, ir_entity *entity)
static ir_entity *get_nonlazyptr(be_main_env_t *env, ir_entity *entity)
{
ir_entity *result = pmap_get(ir_entity, env->ent_pic_symbol_map, entity);
if (result == NULL) {
result = create_pic_symbol(env, entity);
result = create_nonlazyptr(env, entity);
pmap_insert(env->ent_pic_symbol_map, entity, result);
}
return result;
}
/**
* Returns non-zero if a given entity can be accessed using a relative address.
*/
static int can_address_relative(ir_entity *entity)
static ir_node *get_eip_relative(ir_graph *const irg,
x86_immediate_kind_t const kind,
ir_entity *const entity)
{
return entity_has_definition(entity) && !(get_entity_linkage(entity) & IR_LINKAGE_MERGE);
/* Everything else is accessed relative to EIP. */
ir_node *const pic_base = ia32_get_pic_base(irg);
ir_node *const block = get_irg_start_block(irg);
ir_node *reloc = be_new_Relocation(irg, (unsigned)kind, entity);
/* All ok now for locally constructed stuff. */
ir_node *add = new_rd_Add(NULL, block, pic_base, reloc, mode_P);
/* 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;
if (ia32_pic_style == IA32_PIC_ELF_PLT) {
res = be_new_Relocation(irg, X86_IMM_PLT, entity);
} else {
assert(ia32_pic_style == IA32_PIC_ELF_NO_PLT);
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);
}
}
/** patches Addresses to work in position independent code */
static void fix_pic_addresses(ir_node *const node, void *const data)
static void fix_address_macho(ir_node *const node, void *const data)
{
(void)data;
ir_graph *const irg = get_irn_irg(node);
be_main_env_t *const be = be_get_irg_main_env(irg);
foreach_irn_in(node, i, pred) {
if (!is_Address(pred))
continue;
ir_node *res;
ir_entity *const entity = get_Address_entity(pred);
dbg_info *const dbgi = get_irn_dbg_info(pred);
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;
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 (can_address_relative(entity))
if (entity_has_definition(entity)
&& !(get_entity_linkage(entity) & IR_LINKAGE_MERGE))
continue;
ir_entity *const trampoline = get_trampoline(be, entity);
res = new_rd_Address(dbgi, irg, trampoline);
} else if (get_entity_type(entity) == get_code_type()) {
/* Block labels can always be addressed directly. */
continue;
res = be_new_Relocation(irg, X86_IMM_ADDR, trampoline);
} else {
/* Everything else is accessed relative to EIP. */
ir_node *const block = get_nodes_block(pred);
ir_mode *const mode = get_irn_mode(pred);
ir_node *const pic_base = ia32_get_pic_base(irg);
if (can_address_relative(entity)) {
/* All ok now for locally constructed stuff. */
res = new_rd_Add(dbgi, block, pic_base, pred, mode);
/* Make sure the walker doesn't visit this add again. */
mark_irn_visited(res);
if (entity_has_definition(entity)
&& !(get_entity_linkage(entity) & IR_LINKAGE_MERGE)) {
res = get_eip_relative(irg, X86_IMM_PICBASE_REL, entity);
} else {
/* Get entry from pic symbol segment. */
ir_entity *const pic_symbol = get_pic_symbol(be, entity);
ir_node *const pic_address = new_rd_Address(dbgi, irg, pic_symbol);
ir_node *const add = new_rd_Add(dbgi, block, pic_base, pic_address, mode);
mark_irn_visited(add);
/* We need an extra indirection for global data outside our current
* module. The loads are always safe and can therefore float and
* need no memory input */
ir_type *const type = get_entity_type(entity);
ir_node *const nomem = get_irg_no_mem(irg);
ir_node *const load = new_rd_Load(dbgi, block, nomem, add, mode, type, cons_floats);
res = new_r_Proj(load, mode, pn_Load_res);
ir_entity *const nonlazyptr = get_nonlazyptr(be, entity);
res = get_table_load(irg, X86_IMM_PICBASE_REL, nonlazyptr);
}
}
set_irn_n(node, i, res);
......@@ -143,7 +186,15 @@ static void fix_pic_addresses(ir_node *const node, void *const data)
void ia32_adjust_pic(ir_graph *irg)
{
if (ia32_pic_style == IA32_PIC_NONE)
switch (ia32_pic_style) {
case IA32_PIC_NONE:
return;
case IA32_PIC_ELF_PLT:
case IA32_PIC_ELF_NO_PLT:
irg_walk_graph(irg, fix_address_elf, NULL, NULL);
return;
irg_walk_graph(irg, fix_pic_addresses, NULL, NULL);
case IA32_PIC_MACH_O:
irg_walk_graph(irg, fix_address_macho, NULL, NULL);
return;
}
}
......@@ -279,12 +279,10 @@ static void adjust_relocation(x86_imm32_t *imm)
if (imm->kind != X86_IMM_ADDR)
return;
ir_entity *entity = imm->entity;
if (ia32_pic_style == IA32_PIC_MACH_O &&
get_entity_type(entity) != get_code_type()) {
imm->kind = X86_IMM_PICBASE_REL;
} else if (is_tls_entity(entity)) {
if (is_tls_entity(entity)) {
imm->kind = entity_has_definition(entity) ? X86_IMM_TLS_LE
: X86_IMM_TLS_IE;
return;
}
}
......@@ -4941,6 +4939,12 @@ static ir_node *gen_CopyB(ir_node *node)
return projm;
}
static bool callee_is_plt(ir_node *callee)
{
return be_is_Relocation(callee)
&& be_get_Relocation_kind(callee) == X86_IMM_PLT;
}
static ir_node *gen_Call(ir_node *node)
{
arch_register_req_t const *const req_gp = ia32_reg_classes[CLASS_ia32_gp].class_req;
......@@ -4958,7 +4962,9 @@ static ir_node *gen_Call(ir_node *node)
x86_cconv_t *const cconv = ia32_decide_calling_convention(type, NULL);
ir_graph *const irg = get_irn_irg(node);
unsigned in_arity = n_ia32_Call_first_argument;
unsigned const n_ins = in_arity + cconv->n_param_regs;
bool const is_plt = callee_is_plt(callee);
unsigned const n_ins
= in_arity + cconv->n_param_regs + is_plt;
ir_node **const in = ALLOCAN(ir_node*, n_ins);
arch_register_req_t const **const in_req = be_allocate_in_reqs(irg, n_ins);
......@@ -4970,13 +4976,6 @@ static ir_node *gen_Call(ir_node *node)
in[n_ia32_Call_callee] = am.new_op2;
in_req[n_ia32_Call_callee] = req_gp;
/* We have trampolines for our calls and need no PIC adjustments */
if (ia32_pic_style != IA32_PIC_NONE && is_ia32_Immediate(am.new_op2)) {
ia32_immediate_attr_t *const attr = get_ia32_immediate_attr(am.new_op2);
if (attr->imm.kind == X86_IMM_PICBASE_REL)
attr->imm.kind = X86_IMM_ADDR;
}
ir_node *const block = be_transform_node(old_block);
ir_node *const new_frame = get_stack_pointer_for(node);
unsigned const po2_stack_alignment = ia32_cg_config.po2_stack_alignment;
......@@ -5030,6 +5029,13 @@ static ir_node *gen_Call(ir_node *node)
sync_ins[sync_arity++] = create_proj_for_store(store, pn_Store_M);
}
}
/* PIC calls need the GOT address in ebx */
if (is_plt) {
unsigned goti = in_arity++;
in[goti] = ia32_get_pic_base(irg);
in_req[goti] = ia32_registers[REG_EBX].single_req;
panic("Need code to make this register pressure faithful");
}
assert(in_arity == n_ins);
assert(sync_arity <= n_params + 1);
......@@ -5928,11 +5934,11 @@ void ia32_transform_graph(ir_graph *irg)
start_mem.irn = NULL;
memset(&start_val, 0, sizeof(start_val));
if (ia32_pic_style == IA32_PIC_MACH_O) {
lconst_imm_kind = X86_IMM_PICBASE_REL;
} else {
assert(ia32_pic_style == IA32_PIC_NONE);
lconst_imm_kind = X86_IMM_ADDR;
switch (ia32_pic_style) {
case IA32_PIC_NONE: lconst_imm_kind = X86_IMM_ADDR; break;
case IA32_PIC_MACH_O: lconst_imm_kind = X86_IMM_PICBASE_REL; break;
case IA32_PIC_ELF_PLT:
case IA32_PIC_ELF_NO_PLT: lconst_imm_kind = X86_IMM_GOTOFF; break;
}
register_transformers();
......
......@@ -26,6 +26,9 @@ typedef enum x86_immediate_kind_t {
X86_IMM_TLS_LE, /**< thread local storage, load exec */
X86_IMM_FRAMEOFFSET, /**< offset to entity on stackframe */
X86_IMM_GOTPCREL, /**< global offset table entry PIC relative (elf64) */
X86_IMM_GOTOFF, /**< address relative to global offset table */
X86_IMM_GOT, /**< global offset table entry offset */
X86_IMM_PLT, /**< address to entry in procedure linkage table */
} x86_immediate_kind_t;
typedef struct x86_imm32_t {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment