@@ -416,6 +416,17 @@ func signext24(x int64) int32 {
416
416
return (int32 (x ) << 8 ) >> 8
417
417
}
418
418
419
+ // encode an immediate in ARM's imm12 format. copied from ../../../internal/obj/arm/asm5.go
420
+ func immrot (v uint32 ) uint32 {
421
+ for i := 0 ; i < 16 ; i ++ {
422
+ if v &^0xff == 0 {
423
+ return uint32 (i << 8 ) | v | 1 << 25
424
+ }
425
+ v = v << 2 | v >> 30
426
+ }
427
+ return 0
428
+ }
429
+
419
430
// Convert the direct jump relocation r to refer to a trampoline if the target is too far
420
431
func trampoline (ctxt * ld.Link , r * ld.Reloc , s * ld.Symbol ) {
421
432
switch r .Type {
@@ -424,12 +435,18 @@ func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
424
435
// low 24-bit encodes the target address
425
436
t := (ld .Symaddr (r .Sym ) + int64 (signext24 (r .Add & 0xffffff )* 4 ) - (s .Value + int64 (r .Off ))) / 4
426
437
if t > 0x7fffff || t < - 0x800000 || (* ld .FlagDebugTramp > 1 && s .File != r .Sym .File ) {
427
- // direct call too far, need to insert trampoline
438
+ // direct call too far, need to insert trampoline.
439
+ // look up existing trampolines first. if we found one within the range
440
+ // of direct call, we can reuse it. otherwise create a new one.
428
441
offset := (signext24 (r .Add & 0xffffff ) + 2 ) * 4
429
442
var tramp * ld.Symbol
430
443
for i := 0 ; ; i ++ {
431
444
name := r .Sym .Name + fmt .Sprintf ("%+d-tramp%d" , offset , i )
432
445
tramp = ctxt .Syms .Lookup (name , int (r .Sym .Version ))
446
+ if tramp .Type == obj .SDYNIMPORT {
447
+ // don't reuse trampoline defined in other module
448
+ continue
449
+ }
433
450
if tramp .Value == 0 {
434
451
// either the trampoline does not exist -- we need to create one,
435
452
// or found one the address which is not assigned -- this will be
@@ -447,15 +464,16 @@ func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
447
464
if tramp .Type == 0 {
448
465
// trampoline does not exist, create one
449
466
ctxt .AddTramp (tramp )
450
- tramp .Size = 12 // 3 instructions
451
- tramp .P = make ([]byte , tramp .Size )
452
- t = ld .Symaddr (r .Sym ) + int64 (offset )
453
- o1 := uint32 (0xe5900000 | 11 << 12 | 15 << 16 ) // MOVW (R15), R11 // R15 is actual pc + 8
454
- o2 := uint32 (0xe12fff10 | 11 ) // JMP (R11)
455
- o3 := uint32 (t ) // WORD $target
456
- ld .SysArch .ByteOrder .PutUint32 (tramp .P , o1 )
457
- ld .SysArch .ByteOrder .PutUint32 (tramp .P [4 :], o2 )
458
- ld .SysArch .ByteOrder .PutUint32 (tramp .P [8 :], o3 )
467
+ if ctxt .DynlinkingGo () {
468
+ if immrot (uint32 (offset )) == 0 {
469
+ ld .Errorf (s , "odd offset in dynlink direct call: %v+%d" , r .Sym , offset )
470
+ }
471
+ gentrampdyn (tramp , r .Sym , int64 (offset ))
472
+ } else if ld .Buildmode == ld .BuildmodeCArchive || ld .Buildmode == ld .BuildmodeCShared || ld .Buildmode == ld .BuildmodePIE {
473
+ gentramppic (tramp , r .Sym , int64 (offset ))
474
+ } else {
475
+ gentramp (tramp , r .Sym , int64 (offset ))
476
+ }
459
477
}
460
478
// modify reloc to point to tramp, which will be resolved later
461
479
r .Sym = tramp
@@ -467,6 +485,89 @@ func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
467
485
}
468
486
}
469
487
488
+ // generate a trampoline to target+offset
489
+ func gentramp (tramp , target * ld.Symbol , offset int64 ) {
490
+ tramp .Size = 12 // 3 instructions
491
+ tramp .P = make ([]byte , tramp .Size )
492
+ t := ld .Symaddr (target ) + int64 (offset )
493
+ o1 := uint32 (0xe5900000 | 11 << 12 | 15 << 16 ) // MOVW (R15), R11 // R15 is actual pc + 8
494
+ o2 := uint32 (0xe12fff10 | 11 ) // JMP (R11)
495
+ o3 := uint32 (t ) // WORD $target
496
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P , o1 )
497
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [4 :], o2 )
498
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [8 :], o3 )
499
+
500
+ if ld .Linkmode == ld .LinkExternal {
501
+ r := ld .Addrel (tramp )
502
+ r .Off = 8
503
+ r .Type = obj .R_ADDR
504
+ r .Siz = 4
505
+ r .Sym = target
506
+ r .Add = offset
507
+ }
508
+ }
509
+
510
+ // generate a trampoline to target+offset in position independent code
511
+ func gentramppic (tramp , target * ld.Symbol , offset int64 ) {
512
+ tramp .Size = 16 // 4 instructions
513
+ tramp .P = make ([]byte , tramp .Size )
514
+ o1 := uint32 (0xe5900000 | 11 << 12 | 15 << 16 | 4 ) // MOVW 4(R15), R11 // R15 is actual pc + 8
515
+ o2 := uint32 (0xe0800000 | 11 << 12 | 15 << 16 | 11 ) // ADD R15, R11, R11
516
+ o3 := uint32 (0xe12fff10 | 11 ) // JMP (R11)
517
+ o4 := uint32 (0 ) // WORD $(target-pc) // filled in with relocation
518
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P , o1 )
519
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [4 :], o2 )
520
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [8 :], o3 )
521
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [12 :], o4 )
522
+
523
+ r := ld .Addrel (tramp )
524
+ r .Off = 12
525
+ r .Type = obj .R_PCREL
526
+ r .Siz = 4
527
+ r .Sym = target
528
+ r .Add = offset + 4
529
+ }
530
+
531
+ // generate a trampoline to target+offset in dynlink mode (using GOT)
532
+ func gentrampdyn (tramp , target * ld.Symbol , offset int64 ) {
533
+ tramp .Size = 20 // 5 instructions
534
+ o1 := uint32 (0xe5900000 | 11 << 12 | 15 << 16 | 8 ) // MOVW 8(R15), R11 // R15 is actual pc + 8
535
+ o2 := uint32 (0xe0800000 | 11 << 12 | 15 << 16 | 11 ) // ADD R15, R11, R11
536
+ o3 := uint32 (0xe5900000 | 11 << 12 | 11 << 16 ) // MOVW (R11), R11
537
+ o4 := uint32 (0xe12fff10 | 11 ) // JMP (R11)
538
+ o5 := uint32 (0 ) // WORD $target@GOT // filled in with relocation
539
+ o6 := uint32 (0 )
540
+ if offset != 0 {
541
+ // insert an instruction to add offset
542
+ tramp .Size = 24 // 6 instructions
543
+ o6 = o5
544
+ o5 = o4
545
+ o4 = uint32 (0xe2800000 | 11 << 12 | 11 << 16 | immrot (uint32 (offset ))) // ADD $offset, R11, R11
546
+ o1 = uint32 (0xe5900000 | 11 << 12 | 15 << 16 | 12 ) // MOVW 12(R15), R11
547
+ }
548
+ tramp .P = make ([]byte , tramp .Size )
549
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P , o1 )
550
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [4 :], o2 )
551
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [8 :], o3 )
552
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [12 :], o4 )
553
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [16 :], o5 )
554
+ if offset != 0 {
555
+ ld .SysArch .ByteOrder .PutUint32 (tramp .P [20 :], o6 )
556
+ }
557
+
558
+ r := ld .Addrel (tramp )
559
+ r .Off = 16
560
+ r .Type = obj .R_GOTPCREL
561
+ r .Siz = 4
562
+ r .Sym = target
563
+ r .Add = 8
564
+ if offset != 0 {
565
+ // increase reloc offset by 4 as we inserted an ADD instruction
566
+ r .Off = 20
567
+ r .Add = 12
568
+ }
569
+ }
570
+
470
571
func archreloc (ctxt * ld.Link , r * ld.Reloc , s * ld.Symbol , val * int64 ) int {
471
572
if ld .Linkmode == ld .LinkExternal {
472
573
switch r .Type {
0 commit comments