cmd/link/internal/ld: add s390x support

Introduces the new relocation variant RV_390_DBL which indicates
that the relocation value should be shifted right by 1 (to make
it 2-byte aligned).

Change-Id: I03fa96b4759ee19330c5298c3720746622fb1a03
Reviewed-on: https://go-review.googlesource.com/20878
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/cmd/link/internal/ld/arch.go b/src/cmd/link/internal/ld/arch.go
index 306e2df..d28f37f 100644
--- a/src/cmd/link/internal/ld/arch.go
+++ b/src/cmd/link/internal/ld/arch.go
@@ -86,3 +86,12 @@
 	Ptrsize:   8,
 	Regsize:   8,
 }
+
+var Links390x = LinkArch{
+	ByteOrder: binary.BigEndian,
+	Name:      "s390x",
+	Thechar:   'z',
+	Minlc:     2,
+	Ptrsize:   8,
+	Regsize:   8,
+}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index cacec8f..91f0107 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -149,6 +149,9 @@
 	r.Add = add
 	r.Type = obj.R_PCREL
 	r.Siz = 4
+	if Thearch.Thechar == 'z' {
+		r.Variant = RV_390_DBL
+	}
 	return i + int64(r.Siz)
 }
 
@@ -347,6 +350,17 @@
 			Diag("unreachable sym in relocation: %s %s", s.Name, r.Sym.Name)
 		}
 
+		// TODO(mundaym): remove this special case - see issue 14218.
+		if Thearch.Thechar == 'z' {
+			switch r.Type {
+			case obj.R_PCRELDBL:
+				r.Type = obj.R_PCREL
+				r.Variant = RV_390_DBL
+			case obj.R_CALL:
+				r.Variant = RV_390_DBL
+			}
+		}
+
 		switch r.Type {
 		default:
 			switch siz {
@@ -1020,7 +1034,7 @@
 	if strings.HasPrefix(s.Name, "go.string.") && !strings.HasPrefix(s.Name, "go.string.hdr.") {
 		// String data is just bytes.
 		// If we align it, we waste a lot of space to padding.
-		return 1
+		return min
 	}
 	align := int32(Thearch.Maxalign)
 	for int64(align) > s.Size && align > min {
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 8e0394b..fd177cf 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -2170,7 +2170,7 @@
 	elfstrdbg[ElfStrGDBScripts] = Addstring(shstrtab, ".debug_gdb_scripts")
 	if Linkmode == LinkExternal {
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rela.debug_info")
 			elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rela.debug_aranges")
 			elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rela.debug_line")
@@ -2223,7 +2223,7 @@
 func dwarfaddelfrelocheader(elfstr int, shdata *ElfShdr, off int64, size int64) {
 	sh := newElfShdr(elfstrdbg[elfstr])
 	switch Thearch.Thechar {
-	case '0', '6', '7', '9':
+	case '0', '6', '7', '9', 'z':
 		sh.type_ = SHT_RELA
 	default:
 		sh.type_ = SHT_REL
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index 6db7898..cf518a7 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -647,6 +647,68 @@
 	R_SPARC_UA64     = 54
 	R_SPARC_UA16     = 55
 
+	R_390_NONE        = 0
+	R_390_8           = 1
+	R_390_12          = 2
+	R_390_16          = 3
+	R_390_32          = 4
+	R_390_PC32        = 5
+	R_390_GOT12       = 6
+	R_390_GOT32       = 7
+	R_390_PLT32       = 8
+	R_390_COPY        = 9
+	R_390_GLOB_DAT    = 10
+	R_390_JMP_SLOT    = 11
+	R_390_RELATIVE    = 12
+	R_390_GOTOFF      = 13
+	R_390_GOTPC       = 14
+	R_390_GOT16       = 15
+	R_390_PC16        = 16
+	R_390_PC16DBL     = 17
+	R_390_PLT16DBL    = 18
+	R_390_PC32DBL     = 19
+	R_390_PLT32DBL    = 20
+	R_390_GOTPCDBL    = 21
+	R_390_64          = 22
+	R_390_PC64        = 23
+	R_390_GOT64       = 24
+	R_390_PLT64       = 25
+	R_390_GOTENT      = 26
+	R_390_GOTOFF16    = 27
+	R_390_GOTOFF64    = 28
+	R_390_GOTPLT12    = 29
+	R_390_GOTPLT16    = 30
+	R_390_GOTPLT32    = 31
+	R_390_GOTPLT64    = 32
+	R_390_GOTPLTENT   = 33
+	R_390_GOTPLTOFF16 = 34
+	R_390_GOTPLTOFF32 = 35
+	R_390_GOTPLTOFF64 = 36
+	R_390_TLS_LOAD    = 37
+	R_390_TLS_GDCALL  = 38
+	R_390_TLS_LDCALL  = 39
+	R_390_TLS_GD32    = 40
+	R_390_TLS_GD64    = 41
+	R_390_TLS_GOTIE12 = 42
+	R_390_TLS_GOTIE32 = 43
+	R_390_TLS_GOTIE64 = 44
+	R_390_TLS_LDM32   = 45
+	R_390_TLS_LDM64   = 46
+	R_390_TLS_IE32    = 47
+	R_390_TLS_IE64    = 48
+	R_390_TLS_IEENT   = 49
+	R_390_TLS_LE32    = 50
+	R_390_TLS_LE64    = 51
+	R_390_TLS_LDO32   = 52
+	R_390_TLS_LDO64   = 53
+	R_390_TLS_DTPMOD  = 54
+	R_390_TLS_DTPOFF  = 55
+	R_390_TLS_TPOFF   = 56
+	R_390_20          = 57
+	R_390_GOT20       = 58
+	R_390_GOTPLT20    = 59
+	R_390_TLS_GOTIE20 = 60
+
 	ARM_MAGIC_TRAMP_NUMBER = 0x5c000003
 )
 
@@ -804,7 +866,7 @@
 
 	switch Thearch.Thechar {
 	// 64-bit architectures
-	case '9':
+	case '9', 'z':
 		if Ctxt.Arch.ByteOrder == binary.BigEndian {
 			ehdr.flags = 1 /* Version 1 ABI */
 		} else {
@@ -1360,13 +1422,25 @@
 		buckets[b] = uint32(sy.Dynid)
 	}
 
-	Adduint32(Ctxt, s, uint32(nbucket))
-	Adduint32(Ctxt, s, uint32(nsym))
-	for i := 0; i < nbucket; i++ {
-		Adduint32(Ctxt, s, buckets[i])
-	}
-	for i := 0; i < nsym; i++ {
-		Adduint32(Ctxt, s, chain[i])
+	// s390x (ELF64) hash table entries are 8 bytes
+	if Thearch.Thechar == 'z' {
+		Adduint64(Ctxt, s, uint64(nbucket))
+		Adduint64(Ctxt, s, uint64(nsym))
+		for i := 0; i < nbucket; i++ {
+			Adduint64(Ctxt, s, uint64(buckets[i]))
+		}
+		for i := 0; i < nsym; i++ {
+			Adduint64(Ctxt, s, uint64(chain[i]))
+		}
+	} else {
+		Adduint32(Ctxt, s, uint32(nbucket))
+		Adduint32(Ctxt, s, uint32(nsym))
+		for i := 0; i < nbucket; i++ {
+			Adduint32(Ctxt, s, buckets[i])
+		}
+		for i := 0; i < nsym; i++ {
+			Adduint32(Ctxt, s, chain[i])
+		}
 	}
 
 	// version symbols
@@ -1434,7 +1508,7 @@
 	}
 
 	switch Thearch.Thechar {
-	case '0', '6', '7', '9':
+	case '0', '6', '7', '9', 'z':
 		sy := Linklookup(Ctxt, ".rela.plt", 0)
 		if sy.Size > 0 {
 			Elfwritedynent(s, DT_PLTREL, DT_RELA)
@@ -1574,7 +1648,7 @@
 	var prefix string
 	var typ int
 	switch Thearch.Thechar {
-	case '0', '6', '7', '9':
+	case '0', '6', '7', '9', 'z':
 		prefix = ".rela"
 		typ = SHT_RELA
 	default:
@@ -1748,7 +1822,7 @@
 		Debug['d'] = 1
 
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			Addstring(shstrtab, ".rela.text")
 			Addstring(shstrtab, ".rela.rodata")
 			Addstring(shstrtab, ".rela"+relro_prefix+".typelink")
@@ -1796,7 +1870,7 @@
 	if hasinitarr {
 		Addstring(shstrtab, ".init_array")
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			Addstring(shstrtab, ".rela.init_array")
 		default:
 			Addstring(shstrtab, ".rel.init_array")
@@ -1823,7 +1897,7 @@
 		Addstring(shstrtab, ".dynsym")
 		Addstring(shstrtab, ".dynstr")
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			Addstring(shstrtab, ".rela")
 			Addstring(shstrtab, ".rela.plt")
 		default:
@@ -1841,7 +1915,7 @@
 		s.Type = obj.SELFROSECT
 		s.Attr |= AttrReachable
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			s.Size += ELF64SYMSIZE
 		default:
 			s.Size += ELF32SYMSIZE
@@ -1859,7 +1933,7 @@
 
 		/* relocation table */
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			s = Linklookup(Ctxt, ".rela", 0)
 		default:
 			s = Linklookup(Ctxt, ".rel", 0)
@@ -1904,7 +1978,7 @@
 		Thearch.Elfsetupplt()
 
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			s = Linklookup(Ctxt, ".rela.plt", 0)
 		default:
 			s = Linklookup(Ctxt, ".rel.plt", 0)
@@ -1933,7 +2007,7 @@
 
 		elfwritedynentsym(s, DT_SYMTAB, Linklookup(Ctxt, ".dynsym", 0))
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			Elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE)
 		default:
 			Elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE)
@@ -1941,7 +2015,7 @@
 		elfwritedynentsym(s, DT_STRTAB, Linklookup(Ctxt, ".dynstr", 0))
 		elfwritedynentsymsize(s, DT_STRSZ, Linklookup(Ctxt, ".dynstr", 0))
 		switch Thearch.Thechar {
-		case '0', '6', '7', '9':
+		case '0', '6', '7', '9', 'z':
 			elfwritedynentsym(s, DT_RELA, Linklookup(Ctxt, ".rela", 0))
 			elfwritedynentsymsize(s, DT_RELASZ, Linklookup(Ctxt, ".rela", 0))
 			Elfwritedynent(s, DT_RELAENT, ELF64RELASIZE)
@@ -1957,6 +2031,8 @@
 
 		if Thearch.Thechar == '9' {
 			elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".plt", 0))
+		} else if Thearch.Thechar == 'z' {
+			elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got", 0))
 		} else {
 			elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got.plt", 0))
 		}
@@ -2052,6 +2128,8 @@
 		eh.machine = EM_386
 	case '9':
 		eh.machine = EM_PPC64
+	case 'z':
+		eh.machine = EM_S390
 	}
 
 	elfreserve := int64(ELFRESERVE)
@@ -2237,7 +2315,7 @@
 		}
 
 		switch eh.machine {
-		case EM_X86_64, EM_PPC64, EM_AARCH64:
+		case EM_X86_64, EM_PPC64, EM_AARCH64, EM_S390:
 			sh := elfshname(".rela.plt")
 			sh.type_ = SHT_RELA
 			sh.flags = SHF_ALLOC
@@ -2286,6 +2364,8 @@
 		sh.flags = SHF_ALLOC + SHF_EXECINSTR
 		if eh.machine == EM_X86_64 {
 			sh.entsize = 16
+		} else if eh.machine == EM_S390 {
+			sh.entsize = 32
 		} else if eh.machine == EM_PPC64 {
 			// On ppc64, this is just a table of addresses
 			// filled by the dynamic linker
diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go
index a68c473..0255331 100644
--- a/src/cmd/link/internal/ld/ldelf.go
+++ b/src/cmd/link/internal/ld/ldelf.go
@@ -586,6 +586,11 @@
 			Diag("%s: elf object but not ppc64", pn)
 			return
 		}
+	case 'z':
+		if elfobj.machine != ElfMachS390 || hdr.Ident[4] != ElfClass64 {
+			Diag("%s: elf object but not s390x", pn)
+			return
+		}
 	}
 
 	// load section list into memory.
@@ -778,6 +783,9 @@
 				continue
 			}
 
+			if strings.HasPrefix(sym.name, ".LASF") { // gcc on s390x does this
+				continue
+			}
 			Diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type_)
 			continue
 		}
@@ -1124,6 +1132,9 @@
 		Diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype)
 		fallthrough
 
+	case 'z' | R_390_8:
+		*siz = 1
+
 	case '9' | R_PPC64_TOC16<<24,
 		'9' | R_PPC64_TOC16_LO<<24,
 		'9' | R_PPC64_TOC16_HI<<24,
@@ -1132,7 +1143,12 @@
 		'9' | R_PPC64_TOC16_LO_DS<<24,
 		'9' | R_PPC64_REL16_LO<<24,
 		'9' | R_PPC64_REL16_HI<<24,
-		'9' | R_PPC64_REL16_HA<<24:
+		'9' | R_PPC64_REL16_HA<<24,
+		'z' | R_390_16<<24,
+		'z' | R_390_GOT16<<24,
+		'z' | R_390_PC16<<24,
+		'z' | R_390_PC16DBL<<24,
+		'z' | R_390_PLT16DBL<<24:
 		*siz = 2
 
 	case '5' | R_ARM_ABS32<<24,
@@ -1160,11 +1176,27 @@
 		'8' | R_386_GOTPC<<24,
 		'8' | R_386_GOT32X<<24,
 		'9' | R_PPC64_REL24<<24,
-		'9' | R_PPC_REL32<<24:
+		'9' | R_PPC_REL32<<24,
+		'z' | R_390_32<<24,
+		'z' | R_390_PC32<<24,
+		'z' | R_390_GOT32<<24,
+		'z' | R_390_PLT32<<24,
+		'z' | R_390_PC32DBL<<24,
+		'z' | R_390_PLT32DBL<<24,
+		'z' | R_390_GOTPCDBL<<24,
+		'z' | R_390_GOTENT<<24:
 		*siz = 4
 
 	case '6' | R_X86_64_64<<24,
-		'9' | R_PPC64_ADDR64<<24:
+		'9' | R_PPC64_ADDR64<<24,
+		'z' | R_390_GLOB_DAT<<24,
+		'z' | R_390_RELATIVE<<24,
+		'z' | R_390_GOTOFF<<24,
+		'z' | R_390_GOTPC<<24,
+		'z' | R_390_64<<24,
+		'z' | R_390_PC64<<24,
+		'z' | R_390_GOT64<<24,
+		'z' | R_390_PLT64<<24:
 		*siz = 8
 	}
 
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index fcaa8a0..2f5d155 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -339,7 +339,7 @@
 		switch goos {
 		case "linux":
 			switch goarch {
-			case "386", "amd64", "arm", "arm64", "ppc64le":
+			case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
 			default:
 				return badmode()
 			}
@@ -1242,7 +1242,7 @@
 	switch Thearch.Thechar {
 	case '8':
 		return []string{"-m32"}
-	case '6', '9':
+	case '6', '9', 'z':
 		return []string{"-m64"}
 	case '5':
 		return []string{"-marm"}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index b00f80a..e11b5dc 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -264,6 +264,12 @@
 	RV_POWER_HI
 	RV_POWER_HA
 	RV_POWER_DS
+
+	// RV_390_DBL is a s390x-specific relocation variant that indicates that
+	// the value to be placed into the relocatable field should first be
+	// divided by 2.
+	RV_390_DBL
+
 	RV_CHECK_OVERFLOW = 1 << 8
 	RV_TYPE_MASK      = RV_CHECK_OVERFLOW - 1
 )
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index c44b67d..3f8784f 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -67,7 +67,7 @@
 
 func putelfsyment(off int, addr int64, size int64, info int, shndx int, other int) {
 	switch Thearch.Thechar {
-	case '0', '6', '7', '9':
+	case '0', '6', '7', '9', 'z':
 		Thearch.Lput(uint32(off))
 		Cput(uint8(info))
 		Cput(uint8(other))
@@ -593,6 +593,7 @@
 		adduint(Ctxt, moduledata, uint64(len(Ctxt.Shlibs)))
 		adduint(Ctxt, moduledata, uint64(len(Ctxt.Shlibs)))
 	}
+
 	// The rest of moduledata is zero initialized.
 	// When linking an object that does not contain the runtime we are
 	// creating the moduledata from scratch and it does not have a