aboutsummaryrefslogtreecommitdiff
path: root/ld/emultempl/ppc.em
blob: 189c8b5e1581f8bc417b369a05988702a0347363 (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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# This shell script emits a C file. -*- C -*-
# It does some substitutions.
cat >em_${EMULATION_NAME}.c <<EOF
/* This file is is generated by a shell script.  DO NOT EDIT! */

/* PowerPC/POWER emulation for the linker.
   Copyright (C) 1994 Free Software Foundation, Inc.
   Written by Ian Lance Taylor, Cygnus Support

This file is part of GLD, the Gnu Linker.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* This file is similar to generic.em.  However, we need to build
   linker stubs to handle calls to different modules.  A PowerPC call
   is emitted as a two instruction sequence:
     bl .{function}
     cror 31,31,31

   When linking, if the function symbol is defined and in the same
   module, we leave it alone (actually, if it is more than 26 bits
   away we should treat it as though it were in a different module).
   If the function symbol is not defined, we must construct a linker
   stub.  We reroute the call to go to the linker stub instead, and we
   change the cror instruction to reload the TOC register value.  The
   linker stub looks like this
     l     r12,{function}(r2)
     st    r2,20(r1)
     l     r0,0(r12)
     l     r2,4(r12)
     mtctr r0
     bcc

   Since we do not yet support dynamic linking, all function symbols
   are in the same module.  However, we still need to create a stub
   for an undefined symbol.

   This is actually probably not correct for the PowerPC ABI.
   However, I do not have that ABI yet.  */

#define TARGET_IS_${EMULATION_NAME}

#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"

/* FIXME!  */
#include "genlink.h"

#include "ld.h"
#include "config.h"
#include "ldmain.h"
#include "ldemul.h"
#include "ldfile.h"
#include "ldmisc.h"
#include "ldexp.h"
#include "ldlang.h"

static void gld${EMULATION_NAME}_before_parse PARAMS ((void));
static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile));
static void gld${EMULATION_NAME}_create_output_section_statements
  PARAMS ((void));
static void gld${EMULATION_NAME}_before_allocation PARAMS ((void));
static boolean gld${EMULATION_NAME}_check_symbol
  PARAMS ((struct bfd_link_hash_entry *, PTR));

static void
gld${EMULATION_NAME}_before_parse()
{
  ldfile_output_architecture = bfd_arch_${ARCH};
}

/* Section in which we build stubs.  */

static asection *stub_sec;

/* Section in which we build TOC entries for stubs.  */

static asection *stub_toc_sec;

/* This is called before the input files are opened.  We create a new
   fake input file to hold the stub section.  */

static void
gld${EMULATION_NAME}_create_output_section_statements ()
{
  lang_input_statement_type *stub_file;
  
  stub_file = lang_add_input_file ("linker stubs",
				   lang_input_file_is_fake_enum,
				   (char *) NULL);

  stub_file->the_bfd = bfd_create ("linker stubs", output_bfd);
  if (stub_file->the_bfd == (bfd *) NULL
      || ! bfd_set_arch_mach (stub_file->the_bfd,
			      bfd_get_arch (output_bfd),
			      bfd_get_mach (output_bfd)))
    {
      einfo ("%X%P: can not create BFD: %E");
      return;
    }

  stub_sec = bfd_make_section_old_way (stub_file->the_bfd, ".text");
  stub_toc_sec = bfd_make_section_old_way (stub_file->the_bfd, ".toc");
  if (stub_sec == (asection *) NULL
      || stub_toc_sec == (asection *) NULL
      || ! bfd_set_section_flags (stub_file->the_bfd, stub_sec,
				  (SEC_HAS_CONTENTS
				   | SEC_ALLOC
				   | SEC_LOAD
				   | SEC_CODE
				   | SEC_RELOC
				   | SEC_IN_MEMORY))
      || ! bfd_set_section_flags (stub_file->the_bfd, stub_toc_sec,
				  (SEC_HAS_CONTENTS
				   | SEC_ALLOC
				   | SEC_LOAD
				   | SEC_DATA
				   | SEC_RELOC
				   | SEC_IN_MEMORY)))
    {
      einfo ("%X%P: can not create stub sections: %E");
      return;
    }

  ldlang_add_file (stub_file);
}

/* This is called after the input sections have been attached to the
   output sections but before the output section sizes have been set.
   We can identify the required stubs because they are undefined
   symbols beginning with ".".  For each such symbol, we build a stub
   in the .text section.  */

static void
gld${EMULATION_NAME}_before_allocation ()
{
  bfd_link_hash_traverse (link_info.hash,
			  gld${EMULATION_NAME}_check_symbol,
			  (PTR) NULL);
}

/* If a particular symbol is undefined and starts with a ".", then we
   need to make a stub for it.  */

/*ARGSUSED*/
static boolean
gld${EMULATION_NAME}_check_symbol (h, info)
     struct bfd_link_hash_entry *h;
     PTR info;
{
  bfd *stub_bfd;
  bfd_byte *p;
  arelent *r;
  struct bfd_link_hash_entry *hnew;
  asymbol *sym;

  if (h->type != bfd_link_hash_undefined
      || h->root.string[0] != '.')
    return true;

  stub_bfd = stub_sec->owner;

  /* Define this symbol to be the current location in stub_sec.  */
  h->type = bfd_link_hash_defined;
  h->u.def.value = bfd_section_size (stub_bfd, stub_sec);
  h->u.def.section = stub_sec;

  /* We want to add this:
       LONG (0x81820000)	lwz	r12,{TOC index}(r2)
       LONG (0x90410014)	stw	r2,20(r1)
       LONG (0x800c0000)	lwz	r0,0(r12)
       LONG (0x804c0004)	lwz	r2,4(r12)
       LONG (0x7c0903a6)	mtctr	r0
       LONG (0x4e800420)	bctr
       LONG (0)			Traceback table
       LONG (0xc8000)
       LONG (0)
     */

  if (! bfd_set_section_size (stub_bfd, stub_sec,
			      h->u.def.value + 9 * 4))
    {
      einfo ("%P%X: can not set section size: %E");
      return false;
    }

  stub_sec->contents = ((bfd_byte *)
			xrealloc ((PTR) stub_sec->contents,
				  bfd_section_size (stub_bfd, stub_sec)));
  p = stub_sec->contents + h->u.def.value;
  bfd_put_32 (stub_bfd,
	      (bfd_vma) 0x81820000 + bfd_section_size (stub_bfd, stub_toc_sec),
	      p);
  p += 4;
  bfd_put_32 (stub_bfd, (bfd_vma) 0x90410014, p);
  p += 4;
  bfd_put_32 (stub_bfd, (bfd_vma) 0x800c0000, p);
  p += 4;
  bfd_put_32 (stub_bfd, (bfd_vma) 0x804c0004, p);
  p += 4;
  bfd_put_32 (stub_bfd, (bfd_vma) 0x7c0903a6, p);
  p += 4;
  bfd_put_32 (stub_bfd, (bfd_vma) 0x4e800420, p);
  p += 4;
  bfd_put_32 (stub_bfd, (bfd_vma) 0, p);
  p += 4;
  bfd_put_32 (stub_bfd, (bfd_vma) 0xc8000, p);
  p += 4;
  bfd_put_32 (stub_bfd, (bfd_vma) 0, p);

  /* Add an undefined symbol for the TOC reference.  This is the name
     without the leading ".".  */
  hnew = bfd_link_hash_lookup (link_info.hash, h->root.string + 1, true,
			       false, true);
  if (hnew == (struct bfd_link_hash_entry *) NULL)
    einfo ("%P%F: bfd_link_hash_lookup failed: %E");
  if (hnew->type == bfd_link_hash_new)
    {
      hnew->type = bfd_link_hash_undefined;
      hnew->u.undef.abfd = stub_bfd;
      bfd_link_add_undef (link_info.hash, hnew);
    }

  /* Add a relocation entry for the TOC reference in the first
     instruction.  */
  ++stub_sec->reloc_count;
  stub_sec->relocation = ((arelent *)
			  xrealloc ((PTR) stub_sec->relocation,
				    (stub_sec->reloc_count
				     * sizeof (arelent))));
  r = &stub_sec->relocation[stub_sec->reloc_count - 1];
  r->sym_ptr_ptr = stub_toc_sec->symbol_ptr_ptr;
  r->address = h->u.def.value;
  if (stub_bfd->xvec->byteorder_big_p)
    r->address += 2;
  r->addend = 0;
  r->howto = bfd_reloc_type_lookup (stub_bfd, BFD_RELOC_PPC_TOC16);
  if (r->howto == (reloc_howto_type *) NULL)
    einfo ("%P%F: no howto for TOC reference: %E");

  /* Add a relocation entry in the TOC section.  */
  ++stub_toc_sec->reloc_count;
  stub_toc_sec->relocation = ((arelent *)
			      xrealloc ((PTR) stub_toc_sec->relocation,
					(stub_toc_sec->reloc_count
					 * sizeof (arelent))));
  r = &stub_toc_sec->relocation[stub_toc_sec->reloc_count - 1];

  sym = bfd_make_empty_symbol (stub_bfd);
  sym->name = hnew->root.string;
  sym->value = 0;
  sym->flags = BSF_NO_FLAGS;
  sym->section = &bfd_und_section;
  sym->udata = NULL;
  
  /* FIXME! */
  ((struct generic_link_hash_entry *) hnew)->sym = sym;

  r->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
  *r->sym_ptr_ptr = sym;
  r->address = bfd_section_size (stub_bfd, stub_toc_sec);
  r->addend = 0;
  r->howto = bfd_reloc_type_lookup (stub_bfd, BFD_RELOC_32);
  if (r->howto == (reloc_howto_type *) NULL)
    einfo ("%P%F: no howto for TOC entry: %E");

  /* Add more space to the .toc section.  */
  if (! bfd_set_section_size (stub_bfd, stub_toc_sec,
			      bfd_section_size (stub_bfd, stub_toc_sec) + 4))
    {
      einfo ("%P%X: can not set section size: %E");
      return false;
    }

  stub_toc_sec->contents = ((bfd_byte *)
			    xrealloc ((PTR) stub_toc_sec->contents,
				      bfd_section_size (stub_bfd,
							stub_toc_sec)));
  bfd_put_32 (stub_bfd, (bfd_vma) 0,
	      (stub_toc_sec->contents
	       + bfd_section_size (stub_bfd, stub_toc_sec)
	       - 4));

  return true;
}

static char *
gld${EMULATION_NAME}_get_script(isfile)
     int *isfile;
EOF

if test -n "$COMPILE_IN"
then
# Scripts compiled in.

# sed commands to quote an ld script as a C string.
sc='s/["\\]/\\&/g
s/$/\\n\\/
1s/^/"/
$s/$/n"/
'

cat >>em_${EMULATION_NAME}.c <<EOF
{			     
  *isfile = 0;

  if (link_info.relocateable == true && config.build_constructors == true)
    return `sed "$sc" ldscripts/${EMULATION_NAME}.xu`;
  else if (link_info.relocateable == true)
    return `sed "$sc" ldscripts/${EMULATION_NAME}.xr`;
  else if (!config.text_read_only)
    return `sed "$sc" ldscripts/${EMULATION_NAME}.xbn`;
  else if (!config.magic_demand_paged)
    return `sed "$sc" ldscripts/${EMULATION_NAME}.xn`;
  else
    return `sed "$sc" ldscripts/${EMULATION_NAME}.x`;
}
EOF

else
# Scripts read from the filesystem.

cat >>em_${EMULATION_NAME}.c <<EOF
{			     
  *isfile = 1;

  if (link_info.relocateable == true && config.build_constructors == true)
    return "ldscripts/${EMULATION_NAME}.xu";
  else if (link_info.relocateable == true)
    return "ldscripts/${EMULATION_NAME}.xr";
  else if (!config.text_read_only)
    return "ldscripts/${EMULATION_NAME}.xbn";
  else if (!config.magic_demand_paged)
    return "ldscripts/${EMULATION_NAME}.xn";
  else
    return "ldscripts/${EMULATION_NAME}.x";
}
EOF

fi

cat >>em_${EMULATION_NAME}.c <<EOF

struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = 
{
  gld${EMULATION_NAME}_before_parse,
  syslib_default,
  hll_default,
  after_parse_default,
  after_allocation_default,
  set_output_arch_default,
  ldemul_default_target,
  gld${EMULATION_NAME}_before_allocation,
  gld${EMULATION_NAME}_get_script,
  "${EMULATION_NAME}",
  "${OUTPUT_FORMAT}",
  0,
  gld${EMULATION_NAME}_create_output_section_statements
};
EOF