-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathaarch64.cc
More file actions
8319 lines (7230 loc) · 257 KB
/
aarch64.cc
File metadata and controls
8319 lines (7230 loc) · 257 KB
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
// aarch64.cc -- aarch64 target support for gold.
// Copyright (C) 2014-2016 Free Software Foundation, Inc.
// Written by Jing Yu <[email protected]> and Han Shen <[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 <cstring>
#include <map>
#include <set>
#include "elfcpp.h"
#include "dwarf.h"
#include "parameters.h"
#include "reloc.h"
#include "aarch64.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 "freebsd.h"
#include "nacl.h"
#include "gc.h"
#include "icf.h"
#include "aarch64-reloc-property.h"
// The first three .got.plt entries are reserved.
const int32_t AARCH64_GOTPLT_RESERVE_COUNT = 3;
namespace
{
using namespace gold;
template<int size, bool big_endian>
class Output_data_plt_aarch64;
template<int size, bool big_endian>
class Output_data_plt_aarch64_standard;
template<int size, bool big_endian>
class Target_aarch64;
template<int size, bool big_endian>
class AArch64_relocate_functions;
// Utility class dealing with insns. This is ported from macros in
// bfd/elfnn-aarch64.cc, but wrapped inside a class as static members. This
// class is used in erratum sequence scanning.
template<bool big_endian>
class AArch64_insn_utilities
{
public:
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
static const int BYTES_PER_INSN;
// Zero register encoding - 31.
static const unsigned int AARCH64_ZR;
static unsigned int
aarch64_bit(Insntype insn, int pos)
{ return ((1 << pos) & insn) >> pos; }
static unsigned int
aarch64_bits(Insntype insn, int pos, int l)
{ return (insn >> pos) & ((1 << l) - 1); }
// Get the encoding field "op31" of 3-source data processing insns. "op31" is
// the name defined in armv8 insn manual C3.5.9.
static unsigned int
aarch64_op31(Insntype insn)
{ return aarch64_bits(insn, 21, 3); }
// Get the encoding field "ra" of 3-source data processing insns. "ra" is the
// third source register. See armv8 insn manual C3.5.9.
static unsigned int
aarch64_ra(Insntype insn)
{ return aarch64_bits(insn, 10, 5); }
static bool
is_adr(const Insntype insn)
{ return (insn & 0x9F000000) == 0x10000000; }
static bool
is_adrp(const Insntype insn)
{ return (insn & 0x9F000000) == 0x90000000; }
static unsigned int
aarch64_rm(const Insntype insn)
{ return aarch64_bits(insn, 16, 5); }
static unsigned int
aarch64_rn(const Insntype insn)
{ return aarch64_bits(insn, 5, 5); }
static unsigned int
aarch64_rd(const Insntype insn)
{ return aarch64_bits(insn, 0, 5); }
static unsigned int
aarch64_rt(const Insntype insn)
{ return aarch64_bits(insn, 0, 5); }
static unsigned int
aarch64_rt2(const Insntype insn)
{ return aarch64_bits(insn, 10, 5); }
// Encode imm21 into adr. Signed imm21 is in the range of [-1M, 1M).
static Insntype
aarch64_adr_encode_imm(Insntype adr, int imm21)
{
gold_assert(is_adr(adr));
gold_assert(-(1 << 20) <= imm21 && imm21 < (1 << 20));
const int mask19 = (1 << 19) - 1;
const int mask2 = 3;
adr &= ~((mask19 << 5) | (mask2 << 29));
adr |= ((imm21 & mask2) << 29) | (((imm21 >> 2) & mask19) << 5);
return adr;
}
// Retrieve encoded adrp 33-bit signed imm value. This value is obtained by
// 21-bit signed imm encoded in the insn multiplied by 4k (page size) and
// 64-bit sign-extended, resulting in [-4G, 4G) with 12-lsb being 0.
static int64_t
aarch64_adrp_decode_imm(const Insntype adrp)
{
const int mask19 = (1 << 19) - 1;
const int mask2 = 3;
gold_assert(is_adrp(adrp));
// 21-bit imm encoded in adrp.
uint64_t imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2);
// Retrieve msb of 21-bit-signed imm for sign extension.
uint64_t msbt = (imm >> 20) & 1;
// Real value is imm multipled by 4k. Value now has 33-bit information.
int64_t value = imm << 12;
// Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it
// with value.
return ((((uint64_t)(1) << 32) - msbt) << 33) | value;
}
static bool
aarch64_b(const Insntype insn)
{ return (insn & 0xFC000000) == 0x14000000; }
static bool
aarch64_bl(const Insntype insn)
{ return (insn & 0xFC000000) == 0x94000000; }
static bool
aarch64_blr(const Insntype insn)
{ return (insn & 0xFFFFFC1F) == 0xD63F0000; }
static bool
aarch64_br(const Insntype insn)
{ return (insn & 0xFFFFFC1F) == 0xD61F0000; }
// All ld/st ops. See C4-182 of the ARM ARM. The encoding space for
// LD_PCREL, LDST_RO, LDST_UI and LDST_UIMM cover prefetch ops.
static bool
aarch64_ld(Insntype insn) { return aarch64_bit(insn, 22) == 1; }
static bool
aarch64_ldst(Insntype insn)
{ return (insn & 0x0a000000) == 0x08000000; }
static bool
aarch64_ldst_ex(Insntype insn)
{ return (insn & 0x3f000000) == 0x08000000; }
static bool
aarch64_ldst_pcrel(Insntype insn)
{ return (insn & 0x3b000000) == 0x18000000; }
static bool
aarch64_ldst_nap(Insntype insn)
{ return (insn & 0x3b800000) == 0x28000000; }
static bool
aarch64_ldstp_pi(Insntype insn)
{ return (insn & 0x3b800000) == 0x28800000; }
static bool
aarch64_ldstp_o(Insntype insn)
{ return (insn & 0x3b800000) == 0x29000000; }
static bool
aarch64_ldstp_pre(Insntype insn)
{ return (insn & 0x3b800000) == 0x29800000; }
static bool
aarch64_ldst_ui(Insntype insn)
{ return (insn & 0x3b200c00) == 0x38000000; }
static bool
aarch64_ldst_piimm(Insntype insn)
{ return (insn & 0x3b200c00) == 0x38000400; }
static bool
aarch64_ldst_u(Insntype insn)
{ return (insn & 0x3b200c00) == 0x38000800; }
static bool
aarch64_ldst_preimm(Insntype insn)
{ return (insn & 0x3b200c00) == 0x38000c00; }
static bool
aarch64_ldst_ro(Insntype insn)
{ return (insn & 0x3b200c00) == 0x38200800; }
static bool
aarch64_ldst_uimm(Insntype insn)
{ return (insn & 0x3b000000) == 0x39000000; }
static bool
aarch64_ldst_simd_m(Insntype insn)
{ return (insn & 0xbfbf0000) == 0x0c000000; }
static bool
aarch64_ldst_simd_m_pi(Insntype insn)
{ return (insn & 0xbfa00000) == 0x0c800000; }
static bool
aarch64_ldst_simd_s(Insntype insn)
{ return (insn & 0xbf9f0000) == 0x0d000000; }
static bool
aarch64_ldst_simd_s_pi(Insntype insn)
{ return (insn & 0xbf800000) == 0x0d800000; }
// Classify an INSN if it is indeed a load/store. Return true if INSN is a
// LD/ST instruction otherwise return false. For scalar LD/ST instructions
// PAIR is FALSE, RT is returned and RT2 is set equal to RT. For LD/ST pair
// instructions PAIR is TRUE, RT and RT2 are returned.
static bool
aarch64_mem_op_p(Insntype insn, unsigned int *rt, unsigned int *rt2,
bool *pair, bool *load)
{
uint32_t opcode;
unsigned int r;
uint32_t opc = 0;
uint32_t v = 0;
uint32_t opc_v = 0;
/* Bail out quickly if INSN doesn't fall into the the load-store
encoding space. */
if (!aarch64_ldst (insn))
return false;
*pair = false;
*load = false;
if (aarch64_ldst_ex (insn))
{
*rt = aarch64_rt (insn);
*rt2 = *rt;
if (aarch64_bit (insn, 21) == 1)
{
*pair = true;
*rt2 = aarch64_rt2 (insn);
}
*load = aarch64_ld (insn);
return true;
}
else if (aarch64_ldst_nap (insn)
|| aarch64_ldstp_pi (insn)
|| aarch64_ldstp_o (insn)
|| aarch64_ldstp_pre (insn))
{
*pair = true;
*rt = aarch64_rt (insn);
*rt2 = aarch64_rt2 (insn);
*load = aarch64_ld (insn);
return true;
}
else if (aarch64_ldst_pcrel (insn)
|| aarch64_ldst_ui (insn)
|| aarch64_ldst_piimm (insn)
|| aarch64_ldst_u (insn)
|| aarch64_ldst_preimm (insn)
|| aarch64_ldst_ro (insn)
|| aarch64_ldst_uimm (insn))
{
*rt = aarch64_rt (insn);
*rt2 = *rt;
if (aarch64_ldst_pcrel (insn))
*load = true;
opc = aarch64_bits (insn, 22, 2);
v = aarch64_bit (insn, 26);
opc_v = opc | (v << 2);
*load = (opc_v == 1 || opc_v == 2 || opc_v == 3
|| opc_v == 5 || opc_v == 7);
return true;
}
else if (aarch64_ldst_simd_m (insn)
|| aarch64_ldst_simd_m_pi (insn))
{
*rt = aarch64_rt (insn);
*load = aarch64_bit (insn, 22);
opcode = (insn >> 12) & 0xf;
switch (opcode)
{
case 0:
case 2:
*rt2 = *rt + 3;
break;
case 4:
case 6:
*rt2 = *rt + 2;
break;
case 7:
*rt2 = *rt;
break;
case 8:
case 10:
*rt2 = *rt + 1;
break;
default:
return false;
}
return true;
}
else if (aarch64_ldst_simd_s (insn)
|| aarch64_ldst_simd_s_pi (insn))
{
*rt = aarch64_rt (insn);
r = (insn >> 21) & 1;
*load = aarch64_bit (insn, 22);
opcode = (insn >> 13) & 0x7;
switch (opcode)
{
case 0:
case 2:
case 4:
*rt2 = *rt + r;
break;
case 1:
case 3:
case 5:
*rt2 = *rt + (r == 0 ? 2 : 3);
break;
case 6:
*rt2 = *rt + r;
break;
case 7:
*rt2 = *rt + (r == 0 ? 2 : 3);
break;
default:
return false;
}
return true;
}
return false;
} // End of "aarch64_mem_op_p".
// Return true if INSN is mac insn.
static bool
aarch64_mac(Insntype insn)
{ return (insn & 0xff000000) == 0x9b000000; }
// Return true if INSN is multiply-accumulate.
// (This is similar to implementaton in elfnn-aarch64.c.)
static bool
aarch64_mlxl(Insntype insn)
{
uint32_t op31 = aarch64_op31(insn);
if (aarch64_mac(insn)
&& (op31 == 0 || op31 == 1 || op31 == 5)
/* Exclude MUL instructions which are encoded as a multiple-accumulate
with RA = XZR. */
&& aarch64_ra(insn) != AARCH64_ZR)
{
return true;
}
return false;
}
}; // End of "AArch64_insn_utilities".
// Insn length in byte.
template<bool big_endian>
const int AArch64_insn_utilities<big_endian>::BYTES_PER_INSN = 4;
// Zero register encoding - 31.
template<bool big_endian>
const unsigned int AArch64_insn_utilities<big_endian>::AARCH64_ZR = 0x1f;
// Output_data_got_aarch64 class.
template<int size, bool big_endian>
class Output_data_got_aarch64 : public Output_data_got<size, big_endian>
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Valtype;
Output_data_got_aarch64(Symbol_table* symtab, Layout* layout)
: Output_data_got<size, big_endian>(),
symbol_table_(symtab), layout_(layout)
{ }
// Add a static entry for the GOT entry at OFFSET. GSYM is a global
// symbol and R_TYPE is the code of a dynamic relocation that needs to be
// applied in a static link.
void
add_static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
{ this->static_relocs_.push_back(Static_reloc(got_offset, r_type, gsym)); }
// Add a static reloc for the GOT entry at OFFSET. RELOBJ is an object
// defining a local symbol with INDEX. R_TYPE is the code of a dynamic
// relocation that needs to be applied in a static link.
void
add_static_reloc(unsigned int got_offset, unsigned int r_type,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int index)
{
this->static_relocs_.push_back(Static_reloc(got_offset, r_type, relobj,
index));
}
protected:
// Write out the GOT table.
void
do_write(Output_file* of) {
// The first entry in the GOT is the address of the .dynamic section.
gold_assert(this->data_size() >= size / 8);
Output_section* dynamic = this->layout_->dynamic_section();
Valtype dynamic_addr = dynamic == NULL ? 0 : dynamic->address();
this->replace_constant(0, dynamic_addr);
Output_data_got<size, big_endian>::do_write(of);
// Handling static relocs
if (this->static_relocs_.empty())
return;
typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
gold_assert(parameters->doing_static_link());
const off_t offset = this->offset();
const section_size_type oview_size =
convert_to_section_size_type(this->data_size());
unsigned char* const oview = of->get_output_view(offset, oview_size);
Output_segment* tls_segment = this->layout_->tls_segment();
gold_assert(tls_segment != NULL);
AArch64_address aligned_tcb_address =
align_address(Target_aarch64<size, big_endian>::TCB_SIZE,
tls_segment->maximum_alignment());
for (size_t i = 0; i < this->static_relocs_.size(); ++i)
{
Static_reloc& reloc(this->static_relocs_[i]);
AArch64_address value;
if (!reloc.symbol_is_global())
{
Sized_relobj_file<size, big_endian>* object = reloc.relobj();
const Symbol_value<size>* psymval =
reloc.relobj()->local_symbol(reloc.index());
// We are doing static linking. Issue an error and skip this
// relocation if the symbol is undefined or in a discarded_section.
bool is_ordinary;
unsigned int shndx = psymval->input_shndx(&is_ordinary);
if ((shndx == elfcpp::SHN_UNDEF)
|| (is_ordinary
&& shndx != elfcpp::SHN_UNDEF
&& !object->is_section_included(shndx)
&& !this->symbol_table_->is_section_folded(object, shndx)))
{
gold_error(_("undefined or discarded local symbol %u from "
" object %s in GOT"),
reloc.index(), reloc.relobj()->name().c_str());
continue;
}
value = psymval->value(object, 0);
}
else
{
const Symbol* gsym = reloc.symbol();
gold_assert(gsym != NULL);
if (gsym->is_forwarder())
gsym = this->symbol_table_->resolve_forwards(gsym);
// We are doing static linking. Issue an error and skip this
// relocation if the symbol is undefined or in a discarded_section
// unless it is a weakly_undefined symbol.
if ((gsym->is_defined_in_discarded_section()
|| gsym->is_undefined())
&& !gsym->is_weak_undefined())
{
gold_error(_("undefined or discarded symbol %s in GOT"),
gsym->name());
continue;
}
if (!gsym->is_weak_undefined())
{
const Sized_symbol<size>* sym =
static_cast<const Sized_symbol<size>*>(gsym);
value = sym->value();
}
else
value = 0;
}
unsigned got_offset = reloc.got_offset();
gold_assert(got_offset < oview_size);
typedef typename elfcpp::Swap<size, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(oview + got_offset);
Valtype x;
switch (reloc.r_type())
{
case elfcpp::R_AARCH64_TLS_DTPREL64:
x = value;
break;
case elfcpp::R_AARCH64_TLS_TPREL64:
x = value + aligned_tcb_address;
break;
default:
gold_unreachable();
}
elfcpp::Swap<size, big_endian>::writeval(wv, x);
}
of->write_output_view(offset, oview_size, oview);
}
private:
// Symbol table of the output object.
Symbol_table* symbol_table_;
// A pointer to the Layout class, so that we can find the .dynamic
// section when we write out the GOT section.
Layout* layout_;
// This class represent dynamic relocations that need to be applied by
// gold because we are using TLS relocations in a static link.
class Static_reloc
{
public:
Static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym)
: got_offset_(got_offset), r_type_(r_type), symbol_is_global_(true)
{ this->u_.global.symbol = gsym; }
Static_reloc(unsigned int got_offset, unsigned int r_type,
Sized_relobj_file<size, big_endian>* relobj, unsigned int index)
: got_offset_(got_offset), r_type_(r_type), symbol_is_global_(false)
{
this->u_.local.relobj = relobj;
this->u_.local.index = index;
}
// Return the GOT offset.
unsigned int
got_offset() const
{ return this->got_offset_; }
// Relocation type.
unsigned int
r_type() const
{ return this->r_type_; }
// Whether the symbol is global or not.
bool
symbol_is_global() const
{ return this->symbol_is_global_; }
// For a relocation against a global symbol, the global symbol.
Symbol*
symbol() const
{
gold_assert(this->symbol_is_global_);
return this->u_.global.symbol;
}
// For a relocation against a local symbol, the defining object.
Sized_relobj_file<size, big_endian>*
relobj() const
{
gold_assert(!this->symbol_is_global_);
return this->u_.local.relobj;
}
// For a relocation against a local symbol, the local symbol index.
unsigned int
index() const
{
gold_assert(!this->symbol_is_global_);
return this->u_.local.index;
}
private:
// GOT offset of the entry to which this relocation is applied.
unsigned int got_offset_;
// Type of relocation.
unsigned int r_type_;
// Whether this relocation is against a global symbol.
bool symbol_is_global_;
// A global or local symbol.
union
{
struct
{
// For a global symbol, the symbol itself.
Symbol* symbol;
} global;
struct
{
// For a local symbol, the object defining the symbol.
Sized_relobj_file<size, big_endian>* relobj;
// For a local symbol, the symbol index.
unsigned int index;
} local;
} u_;
}; // End of inner class Static_reloc
std::vector<Static_reloc> static_relocs_;
}; // End of Output_data_got_aarch64
template<int size, bool big_endian>
class AArch64_input_section;
template<int size, bool big_endian>
class AArch64_output_section;
template<int size, bool big_endian>
class AArch64_relobj;
// Stub type enum constants.
enum
{
ST_NONE = 0,
// Using adrp/add pair, 4 insns (including alignment) without mem access,
// the fastest stub. This has a limited jump distance, which is tested by
// aarch64_valid_for_adrp_p.
ST_ADRP_BRANCH = 1,
// Using ldr-absolute-address/br-register, 4 insns with 1 mem access,
// unlimited in jump distance.
ST_LONG_BRANCH_ABS = 2,
// Using ldr/calculate-pcrel/jump, 8 insns (including alignment) with 1
// mem access, slowest one. Only used in position independent executables.
ST_LONG_BRANCH_PCREL = 3,
// Stub for erratum 843419 handling.
ST_E_843419 = 4,
// Stub for erratum 835769 handling.
ST_E_835769 = 5,
// Number of total stub types.
ST_NUMBER = 6
};
// Struct that wraps insns for a particular stub. All stub templates are
// created/initialized as constants by Stub_template_repertoire.
template<bool big_endian>
struct Stub_template
{
const typename AArch64_insn_utilities<big_endian>::Insntype* insns;
const int insn_num;
};
// Simple singleton class that creates/initializes/stores all types of stub
// templates.
template<bool big_endian>
class Stub_template_repertoire
{
public:
typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
// Single static method to get stub template for a given stub type.
static const Stub_template<big_endian>*
get_stub_template(int type)
{
static Stub_template_repertoire<big_endian> singleton;
return singleton.stub_templates_[type];
}
private:
// Constructor - creates/initializes all stub templates.
Stub_template_repertoire();
~Stub_template_repertoire()
{ }
// Disallowing copy ctor and copy assignment operator.
Stub_template_repertoire(Stub_template_repertoire&);
Stub_template_repertoire& operator=(Stub_template_repertoire&);
// Data that stores all insn templates.
const Stub_template<big_endian>* stub_templates_[ST_NUMBER];
}; // End of "class Stub_template_repertoire".
// Constructor - creates/initilizes all stub templates.
template<bool big_endian>
Stub_template_repertoire<big_endian>::Stub_template_repertoire()
{
// Insn array definitions.
const static Insntype ST_NONE_INSNS[] = {};
const static Insntype ST_ADRP_BRANCH_INSNS[] =
{
0x90000010, /* adrp ip0, X */
/* ADR_PREL_PG_HI21(X) */
0x91000210, /* add ip0, ip0, :lo12:X */
/* ADD_ABS_LO12_NC(X) */
0xd61f0200, /* br ip0 */
0x00000000, /* alignment padding */
};
const static Insntype ST_LONG_BRANCH_ABS_INSNS[] =
{
0x58000050, /* ldr ip0, 0x8 */
0xd61f0200, /* br ip0 */
0x00000000, /* address field */
0x00000000, /* address fields */
};
const static Insntype ST_LONG_BRANCH_PCREL_INSNS[] =
{
0x58000090, /* ldr ip0, 0x10 */
0x10000011, /* adr ip1, #0 */
0x8b110210, /* add ip0, ip0, ip1 */
0xd61f0200, /* br ip0 */
0x00000000, /* address field */
0x00000000, /* address field */
0x00000000, /* alignment padding */
0x00000000, /* alignment padding */
};
const static Insntype ST_E_843419_INSNS[] =
{
0x00000000, /* Placeholder for erratum insn. */
0x14000000, /* b <label> */
};
// ST_E_835769 has the same stub template as ST_E_843419.
const static Insntype* ST_E_835769_INSNS = ST_E_843419_INSNS;
#define install_insn_template(T) \
const static Stub_template<big_endian> template_##T = { \
T##_INSNS, sizeof(T##_INSNS) / sizeof(T##_INSNS[0]) }; \
this->stub_templates_[T] = &template_##T
install_insn_template(ST_NONE);
install_insn_template(ST_ADRP_BRANCH);
install_insn_template(ST_LONG_BRANCH_ABS);
install_insn_template(ST_LONG_BRANCH_PCREL);
install_insn_template(ST_E_843419);
install_insn_template(ST_E_835769);
#undef install_insn_template
}
// Base class for stubs.
template<int size, bool big_endian>
class Stub_base
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
static const AArch64_address invalid_address =
static_cast<AArch64_address>(-1);
static const section_offset_type invalid_offset =
static_cast<section_offset_type>(-1);
Stub_base(int type)
: destination_address_(invalid_address),
offset_(invalid_offset),
type_(type)
{}
~Stub_base()
{}
// Get stub type.
int
type() const
{ return this->type_; }
// Get stub template that provides stub insn information.
const Stub_template<big_endian>*
stub_template() const
{
return Stub_template_repertoire<big_endian>::
get_stub_template(this->type());
}
// Get destination address.
AArch64_address
destination_address() const
{
gold_assert(this->destination_address_ != this->invalid_address);
return this->destination_address_;
}
// Set destination address.
void
set_destination_address(AArch64_address address)
{
gold_assert(address != this->invalid_address);
this->destination_address_ = address;
}
// Reset the destination address.
void
reset_destination_address()
{ this->destination_address_ = this->invalid_address; }
// Get offset of code stub. For Reloc_stub, it is the offset from the
// beginning of its containing stub table; for Erratum_stub, it is the offset
// from the end of reloc_stubs.
section_offset_type
offset() const
{
gold_assert(this->offset_ != this->invalid_offset);
return this->offset_;
}
// Set stub offset.
void
set_offset(section_offset_type offset)
{ this->offset_ = offset; }
// Return the stub insn.
const Insntype*
insns() const
{ return this->stub_template()->insns; }
// Return num of stub insns.
unsigned int
insn_num() const
{ return this->stub_template()->insn_num; }
// Get size of the stub.
int
stub_size() const
{
return this->insn_num() *
AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
}
// Write stub to output file.
void
write(unsigned char* view, section_size_type view_size)
{ this->do_write(view, view_size); }
protected:
// Abstract method to be implemented by sub-classes.
virtual void
do_write(unsigned char*, section_size_type) = 0;
private:
// The last insn of a stub is a jump to destination insn. This field records
// the destination address.
AArch64_address destination_address_;
// The stub offset. Note this has difference interpretations between an
// Reloc_stub and an Erratum_stub. For Reloc_stub this is the offset from the
// beginning of the containing stub_table, whereas for Erratum_stub, this is
// the offset from the end of reloc_stubs.
section_offset_type offset_;
// Stub type.
const int type_;
}; // End of "Stub_base".
// Erratum stub class. An erratum stub differs from a reloc stub in that for
// each erratum occurrence, we generate an erratum stub. We never share erratum
// stubs, whereas for reloc stubs, different branches insns share a single reloc
// stub as long as the branch targets are the same. (More to the point, reloc
// stubs can be shared because they're used to reach a specific target, whereas
// erratum stubs branch back to the original control flow.)
template<int size, bool big_endian>
class Erratum_stub : public Stub_base<size, big_endian>
{
public:
typedef AArch64_relobj<size, big_endian> The_aarch64_relobj;
typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
typedef AArch64_insn_utilities<big_endian> Insn_utilities;
typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
static const int STUB_ADDR_ALIGN;
static const Insntype invalid_insn = static_cast<Insntype>(-1);
Erratum_stub(The_aarch64_relobj* relobj, int type,
unsigned shndx, unsigned int sh_offset)
: Stub_base<size, big_endian>(type), relobj_(relobj),
shndx_(shndx), sh_offset_(sh_offset),
erratum_insn_(invalid_insn),
erratum_address_(this->invalid_address)
{}
~Erratum_stub() {}
// Return the object that contains the erratum.
The_aarch64_relobj*
relobj()
{ return this->relobj_; }
// Get section index of the erratum.
unsigned int
shndx() const
{ return this->shndx_; }
// Get section offset of the erratum.
unsigned int
sh_offset() const
{ return this->sh_offset_; }
// Get the erratum insn. This is the insn located at erratum_insn_address.
Insntype
erratum_insn() const
{
gold_assert(this->erratum_insn_ != this->invalid_insn);
return this->erratum_insn_;
}
// Set the insn that the erratum happens to.
void
set_erratum_insn(Insntype insn)
{ this->erratum_insn_ = insn; }
// For 843419, the erratum insn is ld/st xt, [xn, #uimm], which may be a
// relocation spot, in this case, the erratum_insn_ recorded at scanning phase
// is no longer the one we want to write out to the stub, update erratum_insn_
// with relocated version. Also note that in this case xn must not be "PC", so
// it is safe to move the erratum insn from the origin place to the stub. For
// 835769, the erratum insn is multiply-accumulate insn, which could not be a
// relocation spot (assertion added though).
void
update_erratum_insn(Insntype insn)
{
gold_assert(this->erratum_insn_ != this->invalid_insn);
switch (this->type())
{
case ST_E_843419:
gold_assert(Insn_utilities::aarch64_ldst_uimm(insn));
gold_assert(Insn_utilities::aarch64_ldst_uimm(this->erratum_insn()));
gold_assert(Insn_utilities::aarch64_rd(insn) ==
Insn_utilities::aarch64_rd(this->erratum_insn()));
gold_assert(Insn_utilities::aarch64_rn(insn) ==
Insn_utilities::aarch64_rn(this->erratum_insn()));
// Update plain ld/st insn with relocated insn.
this->erratum_insn_ = insn;