summaryrefslogtreecommitdiff
path: root/ldso/ldso/powerpc/dl-sysdep.h
blob: 768482272995273692b6d1c5d485ff8b78414ab2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * Various assembly language/system dependent hacks that are required
 * so that we can minimize the amount of platform specific code.
 */

/*
 * Define this if the system uses RELOCA.
 */
#define ELF_USES_RELOCA
#include <elf.h>
/*
 * Initialization sequence for a GOT.
 */
#define INIT_GOT(GOT_BASE,MODULE)  _dl_init_got(GOT_BASE,MODULE)

/* Stuff for the PLT.  */
#define PLT_INITIAL_ENTRY_WORDS 18
#define PLT_LONGBRANCH_ENTRY_WORDS 0
#define PLT_TRAMPOLINE_ENTRY_WORDS 6
#define PLT_DOUBLE_SIZE (1<<13)
#define PLT_ENTRY_START_WORDS(entry_number) \
  (PLT_INITIAL_ENTRY_WORDS + (entry_number)*2				\
   + ((entry_number) > PLT_DOUBLE_SIZE					\
      ? ((entry_number) - PLT_DOUBLE_SIZE)*2				\
      : 0))
#define PLT_DATA_START_WORDS(num_entries) PLT_ENTRY_START_WORDS(num_entries)

/* Macros to build PowerPC opcode words.  */
#define OPCODE_ADDI(rd,ra,simm) \
  (0x38000000 | (rd) << 21 | (ra) << 16 | ((simm) & 0xffff))
#define OPCODE_ADDIS(rd,ra,simm) \
  (0x3c000000 | (rd) << 21 | (ra) << 16 | ((simm) & 0xffff))
#define OPCODE_ADD(rd,ra,rb) \
  (0x7c000214 | (rd) << 21 | (ra) << 16 | (rb) << 11)
#define OPCODE_B(target) (0x48000000 | ((target) & 0x03fffffc))
#define OPCODE_BA(target) (0x48000002 | ((target) & 0x03fffffc))
#define OPCODE_BCTR() 0x4e800420
#define OPCODE_LWZ(rd,d,ra) \
  (0x80000000 | (rd) << 21 | (ra) << 16 | ((d) & 0xffff))
#define OPCODE_LWZU(rd,d,ra) \
  (0x84000000 | (rd) << 21 | (ra) << 16 | ((d) & 0xffff))
#define OPCODE_MTCTR(rd) (0x7C0903A6 | (rd) << 21)
#define OPCODE_RLWINM(ra,rs,sh,mb,me) \
  (0x54000000 | (rs) << 21 | (ra) << 16 | (sh) << 11 | (mb) << 6 | (me) << 1)

#define OPCODE_LI(rd,simm)    OPCODE_ADDI(rd,0,simm)
#define OPCODE_ADDIS_HI(rd,ra,value) \
  OPCODE_ADDIS(rd,ra,((value) + 0x8000) >> 16)
#define OPCODE_LIS_HI(rd,value) OPCODE_ADDIS_HI(rd,0,value)
#define OPCODE_SLWI(ra,rs,sh) OPCODE_RLWINM(ra,rs,sh,0,31-sh)


#define PPC_DCBST(where) __asm__ __volatile__ ("dcbst 0,%0" : : "r"(where) : "memory")
#define PPC_SYNC __asm__ __volatile__ ("sync" : : : "memory")
#define PPC_ISYNC __asm__ __volatile__ ("sync; isync" : : : "memory")
#define PPC_ICBI(where) __asm__ __volatile__ ("icbi 0,%0" : : "r"(where) : "memory")
#define PPC_DIE __asm__ __volatile__ ("tweq 0,0")

/* Here we define the magic numbers that this dynamic loader should accept */

#define MAGIC1 EM_PPC
#undef  MAGIC2
/* Used for error messages */
#define ELF_TARGET "powerpc"

struct elf_resolve;
extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry);
void _dl_init_got(unsigned long *lpnt,struct elf_resolve *tpnt);

/* 4096 bytes alignment */
#define PAGE_ALIGN 0xfffff000
#define ADDR_ALIGN 0xfff
#define OFFS_ALIGN 0x7ffff000

/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
   PLT entries should not be allowed to define the value.
   ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
   of the main executable's symbols, as for a COPY reloc.  */
/* We never want to use a PLT entry as the destination of a
   reloc, when what is being relocated is a branch. This is
   partly for efficiency, but mostly so we avoid loops.  */
#define elf_machine_type_class(type) \
  ((((type) == R_PPC_JMP_SLOT				\
    || (type) == R_PPC_REL24				\
    || (type) == R_PPC_ADDR24) * ELF_RTYPE_CLASS_PLT)	\
   | (((type) == R_PPC_COPY) * ELF_RTYPE_CLASS_COPY))

/* The SVR4 ABI specifies that the JMPREL relocs must be inside the
   DT_RELA table.  */
#define ELF_MACHINE_PLTREL_OVERLAP 1

/* Return the value of the GOT pointer.  */
static __inline__ Elf32_Addr * __attribute__ ((const))
ppc_got (void)
{
	Elf32_Addr *got;
#ifdef HAVE_ASM_PPC_REL16
	__asm__ ("	bcl 20,31,1f\n"
	     "1:mflr %0\n"
	     "	addis %0,%0,_GLOBAL_OFFSET_TABLE_-1b@ha\n"
	     "	addi %0,%0,_GLOBAL_OFFSET_TABLE_-1b@l\n"
	     : "=b" (got) : : "lr");
#else
	__asm__ (" bl _GLOBAL_OFFSET_TABLE_-4@local"
	     : "=l" (got));
#endif
	return got;
}

/* Return the link-time address of _DYNAMIC, stored as
   the first value in the GOT. */
static __inline__ Elf32_Addr __attribute__ ((const))
elf_machine_dynamic (void)
{
	return *ppc_got();
}

/* Return the run-time load address of the shared object.  */
static __inline__ Elf32_Addr __attribute__ ((const))
elf_machine_load_address (void)
{
  Elf32_Addr *branchaddr;
  Elf32_Addr runtime_dynamic;

  /* This is much harder than you'd expect.  Possibly I'm missing something.
     The 'obvious' way:

       Apparently, "bcl 20,31,$+4" is what should be used to load LR
       with the address of the next instruction.
       I think this is so that machines that do bl/blr pairing don't
       get confused.

     __asm__ ("bcl 20,31,0f ;"
	  "0: mflr 0 ;"
	  "lis %0,0b@ha;"
	  "addi %0,%0,0b@l;"
	  "subf %0,%0,0"
	  : "=b" (addr) : : "r0", "lr");

     doesn't work, because the linker doesn't have to (and in fact doesn't)
     update the @ha and @l references; the loader (which runs after this
     code) will do that.

     Instead, we use the following trick:

     The linker puts the _link-time_ address of _DYNAMIC at the first
     word in the GOT. We could branch to that address, if we wanted,
     by using an @local reloc; the linker works this out, so it's safe
     to use now. We can't, of course, actually branch there, because
     we'd cause an illegal instruction exception; so we need to compute
     the address ourselves. That gives us the following code: */

  /* Get address of the 'b _DYNAMIC@local'...  */
  __asm__ ("bcl 20,31,0f;"
       "b _DYNAMIC@local;"
       "0:"
       : "=l"(branchaddr));

  /* So now work out the difference between where the branch actually points,
     and the offset of that location in memory from the start of the file.  */
  runtime_dynamic = ((Elf32_Addr) branchaddr
		     + ((Elf32_Sword) (*branchaddr << 6 & 0xffffff00) >> 6));

  return runtime_dynamic - elf_machine_dynamic ();
}

static __inline__ void
elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr,
		      Elf32_Word relative_count)
{
	 Elf32_Rela * rpnt = (void *)rel_addr;
	--rpnt;
	do {     /* PowerPC handles pre increment/decrement better */
		Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset);

		*reloc_addr = load_off + rpnt->r_addend;
	} while (--relative_count);
}

#define ARCH_NUM 1
#define DT_PPC_GOT_IDX	(DT_NUM + OS_NUM)

#define ARCH_DYNAMIC_INFO(dpnt,  dynamic, debug_addr) \
do { \
if (dpnt->d_tag == DT_PPC_GOT) \
     dynamic[DT_PPC_GOT_IDX] = dpnt->d_un.d_ptr; \
} while (0)