@@ -59,6 +59,12 @@ package main
59
59
func main() {}
60
60
`
61
61
62
+ var goSourceWithData = `
63
+ package main
64
+ var globalVar = 42
65
+ func main() { println(&globalVar) }
66
+ `
67
+
62
68
// The linker used to crash if an ELF input file had multiple text sections
63
69
// with the same name.
64
70
func TestSectionsWithSameName (t * testing.T ) {
@@ -569,3 +575,150 @@ func TestFlagR(t *testing.T) {
569
575
t .Errorf ("executable failed to run: %v\n %s" , err , out )
570
576
}
571
577
}
578
+
579
+ func TestFlagD (t * testing.T ) {
580
+ // Test that using the -D flag to specify data section address generates
581
+ // a working binary with data at the specified address.
582
+ // (Test only on ELF for now.)
583
+ testenv .MustHaveGoBuild (t )
584
+ t .Parallel ()
585
+ tmpdir := t .TempDir ()
586
+ src := filepath .Join (tmpdir , "x.go" )
587
+ if err := os .WriteFile (src , []byte (goSourceWithData ), 0444 ); err != nil {
588
+ t .Fatal (err )
589
+ }
590
+ exe := filepath .Join (tmpdir , "x.exe" )
591
+
592
+ dataAddr := "0x10000000"
593
+ expectedAddr := uint64 (0x10000000 )
594
+
595
+ cmd := testenv .Command (t , testenv .GoToolPath (t ), "build" , "-ldflags=-D=" + dataAddr , "-o" , exe , src )
596
+ if out , err := cmd .CombinedOutput (); err != nil {
597
+ t .Fatalf ("build failed: %v, output:\n %s" , err , out )
598
+ }
599
+
600
+ cmd = testenv .Command (t , exe )
601
+ if out , err := cmd .CombinedOutput (); err != nil {
602
+ t .Errorf ("executable failed to run: %v\n %s" , err , out )
603
+ }
604
+
605
+ ef , err := elf .Open (exe )
606
+ if err != nil {
607
+ t .Fatalf ("open elf file failed: %v" , err )
608
+ }
609
+ defer ef .Close ()
610
+
611
+ // Find the first data-related section to verify segment placement
612
+ var firstDataSectionAddr uint64
613
+ var found bool = false
614
+ for _ , sec := range ef .Sections {
615
+ if sec .Type == elf .SHT_PROGBITS || sec .Type == elf .SHT_NOBITS {
616
+
617
+ // These sections are writable, allocated at runtime, but not executable
618
+ isWrite := sec .Flags & elf .SHF_WRITE != 0
619
+ isExec := sec .Flags & elf .SHF_EXECINSTR != 0
620
+ isAlloc := sec .Flags & elf .SHF_ALLOC != 0
621
+
622
+ if isWrite && ! isExec && isAlloc {
623
+ addrLower := sec .Addr < firstDataSectionAddr
624
+ if ! found || addrLower {
625
+ firstDataSectionAddr = sec .Addr
626
+ found = true
627
+ }
628
+ }
629
+ }
630
+ }
631
+
632
+ if ! found {
633
+ t .Fatalf ("can't find any writable data sections" )
634
+ }
635
+ if firstDataSectionAddr != expectedAddr {
636
+ t .Errorf ("data section starts at 0x%x, expected 0x%x" , firstDataSectionAddr , expectedAddr )
637
+ }
638
+ }
639
+
640
+ func TestFlagDWithR (t * testing.T ) {
641
+ // Test that using the -D flag with -R flag works together.
642
+ // Note: The data address gets rounded to the specified alignment quantum.
643
+ testenv .MustHaveGoBuild (t )
644
+ t .Parallel ()
645
+ tmpdir := t .TempDir ()
646
+ src := filepath .Join (tmpdir , "x.go" )
647
+ if err := os .WriteFile (src , []byte (goSourceWithData ), 0444 ); err != nil {
648
+ t .Fatal (err )
649
+ }
650
+ exe := filepath .Join (tmpdir , "x.exe" )
651
+
652
+ // Use an unaligned address that tests -D and -R rounding
653
+ dataAddr := "0x30001234"
654
+ roundQuantum := "8192"
655
+ expectedAddr := uint64 (0x30002000 ) // Should be rounded up to next 8KB boundary
656
+
657
+ cmd := testenv .Command (t , testenv .GoToolPath (t ), "build" , "-ldflags=-D=" + dataAddr + " -R=" + roundQuantum , "-o" , exe , src )
658
+ if out , err := cmd .CombinedOutput (); err != nil {
659
+ t .Fatalf ("build failed: %v, output:\n %s" , err , out )
660
+ }
661
+
662
+ cmd = testenv .Command (t , exe )
663
+ if out , err := cmd .CombinedOutput (); err != nil {
664
+ t .Errorf ("executable failed to run: %v\n %s" , err , out )
665
+ }
666
+
667
+ ef , err := elf .Open (exe )
668
+ if err != nil {
669
+ t .Fatalf ("open elf file failed: %v" , err )
670
+ }
671
+ defer ef .Close ()
672
+
673
+ // Find the first data-related section to verify segment placement
674
+ var firstDataSectionAddr uint64
675
+ var found bool = false
676
+ for _ , sec := range ef .Sections {
677
+ if sec .Type == elf .SHT_PROGBITS || sec .Type == elf .SHT_NOBITS {
678
+
679
+ // These sections are writable, allocated at runtime, but not executable
680
+ isWrite := sec .Flags & elf .SHF_WRITE != 0
681
+ isExec := sec .Flags & elf .SHF_EXECINSTR != 0
682
+ isAlloc := sec .Flags & elf .SHF_ALLOC != 0
683
+
684
+ if isWrite && ! isExec && isAlloc {
685
+ addrLower := sec .Addr < firstDataSectionAddr
686
+ if ! found || addrLower {
687
+ firstDataSectionAddr = sec .Addr
688
+ found = true
689
+ }
690
+ }
691
+ }
692
+ }
693
+
694
+ if ! found {
695
+ t .Fatalf ("can't find any writable data sections" )
696
+ }
697
+ if firstDataSectionAddr != expectedAddr {
698
+ t .Errorf ("data section starts at 0x%x, expected 0x%x" ,
699
+ firstDataSectionAddr , expectedAddr )
700
+ }
701
+ }
702
+
703
+ // TODO: sanity check, delete this
704
+ func TestRounding (t * testing.T ) {
705
+ testCases := []struct {
706
+ input int64
707
+ quantum int64
708
+ expected int64
709
+ }{
710
+ {0x30000000 , 0x2000 , 0x30000000 }, // Already aligned
711
+ {0x30002000 , 0x2000 , 0x30002000 }, // Exactly on boundary
712
+ {0x30001234 , 0x2000 , 0x30002000 },
713
+ {0x30001000 , 0x2000 , 0x30002000 },
714
+ {0x30001fff , 0x2000 , 0x30002000 },
715
+ }
716
+
717
+ for _ , tc := range testCases {
718
+ result := ld .Rnd (tc .input , tc .quantum )
719
+ if result != tc .expected {
720
+ t .Errorf ("Rnd(0x%x, 0x%x) = 0x%x, expected 0x%x" ,
721
+ tc .input , tc .quantum , result , tc .expected )
722
+ }
723
+ }
724
+ }
0 commit comments