forked from pulp-platform/pulp-riscv-binutils-gdb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
powerpc.cc
9422 lines (8605 loc) · 279 KB
/
powerpc.cc
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
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// powerpc.cc -- powerpc target support for gold.
// Copyright (C) 2008-2017 Free Software Foundation, Inc.
// Written by David S. Miller <[email protected]>
// and David Edelsohn <[email protected]>
// This file is part of gold.
// 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 3 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., 51 Franklin Street - Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "gold.h"
#include <set>
#include <algorithm>
#include "elfcpp.h"
#include "dwarf.h"
#include "parameters.h"
#include "reloc.h"
#include "powerpc.h"
#include "object.h"
#include "symtab.h"
#include "layout.h"
#include "output.h"
#include "copy-relocs.h"
#include "target.h"
#include "target-reloc.h"
#include "target-select.h"
#include "tls.h"
#include "errors.h"
#include "gc.h"
namespace
{
using namespace gold;
template<int size, bool big_endian>
class Output_data_plt_powerpc;
template<int size, bool big_endian>
class Output_data_brlt_powerpc;
template<int size, bool big_endian>
class Output_data_got_powerpc;
template<int size, bool big_endian>
class Output_data_glink;
template<int size, bool big_endian>
class Stub_table;
template<int size, bool big_endian>
class Output_data_save_res;
template<int size, bool big_endian>
class Target_powerpc;
struct Stub_table_owner
{
Stub_table_owner()
: output_section(NULL), owner(NULL)
{ }
Output_section* output_section;
const Output_section::Input_section* owner;
};
inline bool
is_branch_reloc(unsigned int r_type);
template<int size, bool big_endian>
class Powerpc_relobj : public Sized_relobj_file<size, big_endian>
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
typedef Unordered_set<Section_id, Section_id_hash> Section_refs;
typedef Unordered_map<Address, Section_refs> Access_from;
Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
: Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
special_(0), relatoc_(0), toc_(0), no_toc_opt_(),
has_small_toc_reloc_(false), opd_valid_(false), opd_ent_(),
access_from_map_(), has14_(), stub_table_index_(),
e_flags_(ehdr.get_e_flags()), st_other_()
{
this->set_abiversion(0);
}
~Powerpc_relobj()
{ }
// Read the symbols then set up st_other vector.
void
do_read_symbols(Read_symbols_data*);
// Arrange to always relocate .toc first.
virtual void
do_relocate_sections(
const Symbol_table* symtab, const Layout* layout,
const unsigned char* pshdrs, Output_file* of,
typename Sized_relobj_file<size, big_endian>::Views* pviews);
// The .toc section index.
unsigned int
toc_shndx() const
{
return this->toc_;
}
// Mark .toc entry at OFF as not optimizable.
void
set_no_toc_opt(Address off)
{
if (this->no_toc_opt_.empty())
this->no_toc_opt_.resize(this->section_size(this->toc_shndx())
/ (size / 8));
off /= size / 8;
if (off < this->no_toc_opt_.size())
this->no_toc_opt_[off] = true;
}
// Mark the entire .toc as not optimizable.
void
set_no_toc_opt()
{
this->no_toc_opt_.resize(1);
this->no_toc_opt_[0] = true;
}
// Return true if code using the .toc entry at OFF should not be edited.
bool
no_toc_opt(Address off) const
{
if (this->no_toc_opt_.empty())
return false;
off /= size / 8;
if (off >= this->no_toc_opt_.size())
return true;
return this->no_toc_opt_[off];
}
// The .got2 section shndx.
unsigned int
got2_shndx() const
{
if (size == 32)
return this->special_;
else
return 0;
}
// The .opd section shndx.
unsigned int
opd_shndx() const
{
if (size == 32)
return 0;
else
return this->special_;
}
// Init OPD entry arrays.
void
init_opd(size_t opd_size)
{
size_t count = this->opd_ent_ndx(opd_size);
this->opd_ent_.resize(count);
}
// Return section and offset of function entry for .opd + R_OFF.
unsigned int
get_opd_ent(Address r_off, Address* value = NULL) const
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_.size());
gold_assert(this->opd_ent_[ndx].shndx != 0);
if (value != NULL)
*value = this->opd_ent_[ndx].off;
return this->opd_ent_[ndx].shndx;
}
// Set section and offset of function entry for .opd + R_OFF.
void
set_opd_ent(Address r_off, unsigned int shndx, Address value)
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_.size());
this->opd_ent_[ndx].shndx = shndx;
this->opd_ent_[ndx].off = value;
}
// Return discard flag for .opd + R_OFF.
bool
get_opd_discard(Address r_off) const
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_.size());
return this->opd_ent_[ndx].discard;
}
// Set discard flag for .opd + R_OFF.
void
set_opd_discard(Address r_off)
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_.size());
this->opd_ent_[ndx].discard = true;
}
bool
opd_valid() const
{ return this->opd_valid_; }
void
set_opd_valid()
{ this->opd_valid_ = true; }
// Examine .rela.opd to build info about function entry points.
void
scan_opd_relocs(size_t reloc_count,
const unsigned char* prelocs,
const unsigned char* plocal_syms);
// Returns true if a code sequence loading a TOC entry can be
// converted into code calculating a TOC pointer relative offset.
bool
make_toc_relative(Target_powerpc<size, big_endian>* target,
Address* value);
// Perform the Sized_relobj_file method, then set up opd info from
// .opd relocs.
void
do_read_relocs(Read_relocs_data*);
bool
do_find_special_sections(Read_symbols_data* sd);
// Adjust this local symbol value. Return false if the symbol
// should be discarded from the output file.
bool
do_adjust_local_symbol(Symbol_value<size>* lv) const
{
if (size == 64 && this->opd_shndx() != 0)
{
bool is_ordinary;
if (lv->input_shndx(&is_ordinary) != this->opd_shndx())
return true;
if (this->get_opd_discard(lv->input_value()))
return false;
}
return true;
}
Access_from*
access_from_map()
{ return &this->access_from_map_; }
// Add a reference from SRC_OBJ, SRC_INDX to this object's .opd
// section at DST_OFF.
void
add_reference(Relobj* src_obj,
unsigned int src_indx,
typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
{
Section_id src_id(src_obj, src_indx);
this->access_from_map_[dst_off].insert(src_id);
}
// Add a reference to the code section specified by the .opd entry
// at DST_OFF
void
add_gc_mark(typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
{
size_t ndx = this->opd_ent_ndx(dst_off);
if (ndx >= this->opd_ent_.size())
this->opd_ent_.resize(ndx + 1);
this->opd_ent_[ndx].gc_mark = true;
}
void
process_gc_mark(Symbol_table* symtab)
{
for (size_t i = 0; i < this->opd_ent_.size(); i++)
if (this->opd_ent_[i].gc_mark)
{
unsigned int shndx = this->opd_ent_[i].shndx;
symtab->gc()->worklist().push_back(Section_id(this, shndx));
}
}
// Return offset in output GOT section that this object will use
// as a TOC pointer. Won't be just a constant with multi-toc support.
Address
toc_base_offset() const
{ return 0x8000; }
void
set_has_small_toc_reloc()
{ has_small_toc_reloc_ = true; }
bool
has_small_toc_reloc() const
{ return has_small_toc_reloc_; }
void
set_has_14bit_branch(unsigned int shndx)
{
if (shndx >= this->has14_.size())
this->has14_.resize(shndx + 1);
this->has14_[shndx] = true;
}
bool
has_14bit_branch(unsigned int shndx) const
{ return shndx < this->has14_.size() && this->has14_[shndx]; }
void
set_stub_table(unsigned int shndx, unsigned int stub_index)
{
if (shndx >= this->stub_table_index_.size())
this->stub_table_index_.resize(shndx + 1, -1);
this->stub_table_index_[shndx] = stub_index;
}
Stub_table<size, big_endian>*
stub_table(unsigned int shndx)
{
if (shndx < this->stub_table_index_.size())
{
Target_powerpc<size, big_endian>* target
= static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
unsigned int indx = this->stub_table_index_[shndx];
if (indx < target->stub_tables().size())
return target->stub_tables()[indx];
}
return NULL;
}
void
clear_stub_table()
{
this->stub_table_index_.clear();
}
int
abiversion() const
{ return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
// Set ABI version for input and output
void
set_abiversion(int ver);
unsigned int
ppc64_local_entry_offset(const Symbol* sym) const
{ return elfcpp::ppc64_decode_local_entry(sym->nonvis() >> 3); }
unsigned int
ppc64_local_entry_offset(unsigned int symndx) const
{ return elfcpp::ppc64_decode_local_entry(this->st_other_[symndx] >> 5); }
private:
struct Opd_ent
{
unsigned int shndx;
bool discard : 1;
bool gc_mark : 1;
Address off;
};
// Return index into opd_ent_ array for .opd entry at OFF.
// .opd entries are 24 bytes long, but they can be spaced 16 bytes
// apart when the language doesn't use the last 8-byte word, the
// environment pointer. Thus dividing the entry section offset by
// 16 will give an index into opd_ent_ that works for either layout
// of .opd. (It leaves some elements of the vector unused when .opd
// entries are spaced 24 bytes apart, but we don't know the spacing
// until relocations are processed, and in any case it is possible
// for an object to have some entries spaced 16 bytes apart and
// others 24 bytes apart.)
size_t
opd_ent_ndx(size_t off) const
{ return off >> 4;}
// For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
unsigned int special_;
// For 64-bit the .rela.toc and .toc section shdnx.
unsigned int relatoc_;
unsigned int toc_;
// For 64-bit, an array with one entry per 64-bit word in the .toc
// section, set if accesses using that word cannot be optimised.
std::vector<bool> no_toc_opt_;
// For 64-bit, whether this object uses small model relocs to access
// the toc.
bool has_small_toc_reloc_;
// Set at the start of gc_process_relocs, when we know opd_ent_
// vector is valid. The flag could be made atomic and set in
// do_read_relocs with memory_order_release and then tested with
// memory_order_acquire, potentially resulting in fewer entries in
// access_from_map_.
bool opd_valid_;
// The first 8-byte word of an OPD entry gives the address of the
// entry point of the function. Relocatable object files have a
// relocation on this word. The following vector records the
// section and offset specified by these relocations.
std::vector<Opd_ent> opd_ent_;
// References made to this object's .opd section when running
// gc_process_relocs for another object, before the opd_ent_ vector
// is valid for this object.
Access_from access_from_map_;
// Whether input section has a 14-bit branch reloc.
std::vector<bool> has14_;
// The stub table to use for a given input section.
std::vector<unsigned int> stub_table_index_;
// Header e_flags
elfcpp::Elf_Word e_flags_;
// ELF st_other field for local symbols.
std::vector<unsigned char> st_other_;
};
template<int size, bool big_endian>
class Powerpc_dynobj : public Sized_dynobj<size, big_endian>
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
: Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr),
opd_shndx_(0), opd_ent_(), e_flags_(ehdr.get_e_flags())
{
this->set_abiversion(0);
}
~Powerpc_dynobj()
{ }
// Call Sized_dynobj::do_read_symbols to read the symbols then
// read .opd from a dynamic object, filling in opd_ent_ vector,
void
do_read_symbols(Read_symbols_data*);
// The .opd section shndx.
unsigned int
opd_shndx() const
{
return this->opd_shndx_;
}
// The .opd section address.
Address
opd_address() const
{
return this->opd_address_;
}
// Init OPD entry arrays.
void
init_opd(size_t opd_size)
{
size_t count = this->opd_ent_ndx(opd_size);
this->opd_ent_.resize(count);
}
// Return section and offset of function entry for .opd + R_OFF.
unsigned int
get_opd_ent(Address r_off, Address* value = NULL) const
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_.size());
gold_assert(this->opd_ent_[ndx].shndx != 0);
if (value != NULL)
*value = this->opd_ent_[ndx].off;
return this->opd_ent_[ndx].shndx;
}
// Set section and offset of function entry for .opd + R_OFF.
void
set_opd_ent(Address r_off, unsigned int shndx, Address value)
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_.size());
this->opd_ent_[ndx].shndx = shndx;
this->opd_ent_[ndx].off = value;
}
int
abiversion() const
{ return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
// Set ABI version for input and output.
void
set_abiversion(int ver);
private:
// Used to specify extent of executable sections.
struct Sec_info
{
Sec_info(Address start_, Address len_, unsigned int shndx_)
: start(start_), len(len_), shndx(shndx_)
{ }
bool
operator<(const Sec_info& that) const
{ return this->start < that.start; }
Address start;
Address len;
unsigned int shndx;
};
struct Opd_ent
{
unsigned int shndx;
Address off;
};
// Return index into opd_ent_ array for .opd entry at OFF.
size_t
opd_ent_ndx(size_t off) const
{ return off >> 4;}
// For 64-bit the .opd section shndx and address.
unsigned int opd_shndx_;
Address opd_address_;
// The first 8-byte word of an OPD entry gives the address of the
// entry point of the function. Records the section and offset
// corresponding to the address. Note that in dynamic objects,
// offset is *not* relative to the section.
std::vector<Opd_ent> opd_ent_;
// Header e_flags
elfcpp::Elf_Word e_flags_;
};
// Powerpc_copy_relocs class. Needed to peek at dynamic relocs the
// base class will emit.
template<int sh_type, int size, bool big_endian>
class Powerpc_copy_relocs : public Copy_relocs<sh_type, size, big_endian>
{
public:
Powerpc_copy_relocs()
: Copy_relocs<sh_type, size, big_endian>(elfcpp::R_POWERPC_COPY)
{ }
// Emit any saved relocations which turn out to be needed. This is
// called after all the relocs have been scanned.
void
emit(Output_data_reloc<sh_type, true, size, big_endian>*);
};
template<int size, bool big_endian>
class Target_powerpc : public Sized_target<size, big_endian>
{
public:
typedef
Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian> Reloc_section;
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
typedef typename elfcpp::Elf_types<size>::Elf_Swxword Signed_address;
static const Address invalid_address = static_cast<Address>(0) - 1;
// Offset of tp and dtp pointers from start of TLS block.
static const Address tp_offset = 0x7000;
static const Address dtp_offset = 0x8000;
Target_powerpc()
: Sized_target<size, big_endian>(&powerpc_info),
got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
tlsld_got_offset_(-1U),
stub_tables_(), branch_lookup_table_(), branch_info_(),
plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0),
stub_group_size_(0), savres_section_(0)
{
}
// Process the relocations to determine unreferenced sections for
// garbage collection.
void
gc_process_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols);
// Scan the relocations to look for symbol adjustments.
void
scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols);
// Map input .toc section to output .got section.
const char*
do_output_section_name(const Relobj*, const char* name, size_t* plen) const
{
if (size == 64 && strcmp(name, ".toc") == 0)
{
*plen = 4;
return ".got";
}
return NULL;
}
// Provide linker defined save/restore functions.
void
define_save_restore_funcs(Layout*, Symbol_table*);
// No stubs unless a final link.
bool
do_may_relax() const
{ return !parameters->options().relocatable(); }
bool
do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
void
do_plt_fde_location(const Output_data*, unsigned char*,
uint64_t*, off_t*) const;
// Stash info about branches, for stub generation.
void
push_branch(Powerpc_relobj<size, big_endian>* ppc_object,
unsigned int data_shndx, Address r_offset,
unsigned int r_type, unsigned int r_sym, Address addend)
{
Branch_info info(ppc_object, data_shndx, r_offset, r_type, r_sym, addend);
this->branch_info_.push_back(info);
if (r_type == elfcpp::R_POWERPC_REL14
|| r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
|| r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
ppc_object->set_has_14bit_branch(data_shndx);
}
void
do_define_standard_symbols(Symbol_table*, Layout*);
// Finalize the sections.
void
do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
// Return the value to use for a dynamic which requires special
// treatment.
uint64_t
do_dynsym_value(const Symbol*) const;
// Return the PLT address to use for a local symbol.
uint64_t
do_plt_address_for_local(const Relobj*, unsigned int) const;
// Return the PLT address to use for a global symbol.
uint64_t
do_plt_address_for_global(const Symbol*) const;
// Return the offset to use for the GOT_INDX'th got entry which is
// for a local tls symbol specified by OBJECT, SYMNDX.
int64_t
do_tls_offset_for_local(const Relobj* object,
unsigned int symndx,
unsigned int got_indx) const;
// Return the offset to use for the GOT_INDX'th got entry which is
// for global tls symbol GSYM.
int64_t
do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const;
void
do_function_location(Symbol_location*) const;
bool
do_can_check_for_function_pointers() const
{ return true; }
// Adjust -fsplit-stack code which calls non-split-stack code.
void
do_calls_non_split(Relobj* object, unsigned int shndx,
section_offset_type fnoffset, section_size_type fnsize,
const unsigned char* prelocs, size_t reloc_count,
unsigned char* view, section_size_type view_size,
std::string* from, std::string* to) const;
// Relocate a section.
void
relocate_section(const Relocate_info<size, big_endian>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
unsigned char* view,
Address view_address,
section_size_type view_size,
const Reloc_symbol_changes*);
// Scan the relocs during a relocatable link.
void
scan_relocatable_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols,
Relocatable_relocs*);
// Scan the relocs for --emit-relocs.
void
emit_relocs_scan(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_syms,
Relocatable_relocs* rr);
// Emit relocations for a section.
void
relocate_relocs(const Relocate_info<size, big_endian>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off
offset_in_output_section,
unsigned char*,
Address view_address,
section_size_type,
unsigned char* reloc_view,
section_size_type reloc_view_size);
// Return whether SYM is defined by the ABI.
bool
do_is_defined_by_abi(const Symbol* sym) const
{
return strcmp(sym->name(), "__tls_get_addr") == 0;
}
// Return the size of the GOT section.
section_size_type
got_size() const
{
gold_assert(this->got_ != NULL);
return this->got_->data_size();
}
// Get the PLT section.
const Output_data_plt_powerpc<size, big_endian>*
plt_section() const
{
gold_assert(this->plt_ != NULL);
return this->plt_;
}
// Get the IPLT section.
const Output_data_plt_powerpc<size, big_endian>*
iplt_section() const
{
gold_assert(this->iplt_ != NULL);
return this->iplt_;
}
// Get the .glink section.
const Output_data_glink<size, big_endian>*
glink_section() const
{
gold_assert(this->glink_ != NULL);
return this->glink_;
}
Output_data_glink<size, big_endian>*
glink_section()
{
gold_assert(this->glink_ != NULL);
return this->glink_;
}
bool has_glink() const
{ return this->glink_ != NULL; }
// Get the GOT section.
const Output_data_got_powerpc<size, big_endian>*
got_section() const
{
gold_assert(this->got_ != NULL);
return this->got_;
}
// Get the GOT section, creating it if necessary.
Output_data_got_powerpc<size, big_endian>*
got_section(Symbol_table*, Layout*);
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<size, big_endian>&);
// Return the number of entries in the GOT.
unsigned int
got_entry_count() const
{
if (this->got_ == NULL)
return 0;
return this->got_size() / (size / 8);
}
// Return the number of entries in the PLT.
unsigned int
plt_entry_count() const;
// Return the offset of the first non-reserved PLT entry.
unsigned int
first_plt_entry_offset() const
{
if (size == 32)
return 0;
if (this->abiversion() >= 2)
return 16;
return 24;
}
// Return the size of each PLT entry.
unsigned int
plt_entry_size() const
{
if (size == 32)
return 4;
if (this->abiversion() >= 2)
return 8;
return 24;
}
Output_data_save_res<size, big_endian>*
savres_section() const
{
return this->savres_section_;
}
// Add any special sections for this symbol to the gc work list.
// For powerpc64, this adds the code section of a function
// descriptor.
void
do_gc_mark_symbol(Symbol_table* symtab, Symbol* sym) const;
// Handle target specific gc actions when adding a gc reference from
// SRC_OBJ, SRC_SHNDX to a location specified by DST_OBJ, DST_SHNDX
// and DST_OFF. For powerpc64, this adds a referenc to the code
// section of a function descriptor.
void
do_gc_add_reference(Symbol_table* symtab,
Relobj* src_obj,
unsigned int src_shndx,
Relobj* dst_obj,
unsigned int dst_shndx,
Address dst_off) const;
typedef std::vector<Stub_table<size, big_endian>*> Stub_tables;
const Stub_tables&
stub_tables() const
{ return this->stub_tables_; }
const Output_data_brlt_powerpc<size, big_endian>*
brlt_section() const
{ return this->brlt_section_; }
void
add_branch_lookup_table(Address to)
{
unsigned int off = this->branch_lookup_table_.size() * (size / 8);
this->branch_lookup_table_.insert(std::make_pair(to, off));
}
Address
find_branch_lookup_table(Address to)
{
typename Branch_lookup_table::const_iterator p
= this->branch_lookup_table_.find(to);
return p == this->branch_lookup_table_.end() ? invalid_address : p->second;
}
void
write_branch_lookup_table(unsigned char *oview)
{
for (typename Branch_lookup_table::const_iterator p
= this->branch_lookup_table_.begin();
p != this->branch_lookup_table_.end();
++p)
{
elfcpp::Swap<size, big_endian>::writeval(oview + p->second, p->first);
}
}
bool
plt_thread_safe() const
{ return this->plt_thread_safe_; }
int
abiversion () const
{ return this->processor_specific_flags() & elfcpp::EF_PPC64_ABI; }
void
set_abiversion (int ver)
{
elfcpp::Elf_Word flags = this->processor_specific_flags();
flags &= ~elfcpp::EF_PPC64_ABI;
flags |= ver & elfcpp::EF_PPC64_ABI;
this->set_processor_specific_flags(flags);
}
// Offset to to save stack slot
int
stk_toc () const
{ return this->abiversion() < 2 ? 40 : 24; }
private:
class Track_tls
{
public:
enum Tls_get_addr
{
NOT_EXPECTED = 0,
EXPECTED = 1,
SKIP = 2,
NORMAL = 3
};
Track_tls()
: tls_get_addr_(NOT_EXPECTED),
relinfo_(NULL), relnum_(0), r_offset_(0)
{ }
~Track_tls()
{
if (this->tls_get_addr_ != NOT_EXPECTED)
this->missing();
}
void
missing(void)
{
if (this->relinfo_ != NULL)
gold_error_at_location(this->relinfo_, this->relnum_, this->r_offset_,
_("missing expected __tls_get_addr call"));
}
void
expect_tls_get_addr_call(
const Relocate_info<size, big_endian>* relinfo,
size_t relnum,
Address r_offset)
{
this->tls_get_addr_ = EXPECTED;
this->relinfo_ = relinfo;
this->relnum_ = relnum;