mirror of https://github.com/golang/go.git
cmd/internal/link: merge .pdata and .xdata sections from host object files
The Go linker doesn't currently merge .pdata/.xdata sections from the host object files generated by the C compiler when using internal linking. This means that the stack can't be unwind in C -> Go. This CL fixes that and adds a test to ensure that the stack can be unwind in C -> Go and Go -> C transitions, which was not well tested. Updates #57302 Change-Id: Ie86a5e6e30b80978277e66ccc2c48550e51263c8 Reviewed-on: https://go-review.googlesource.com/c/go/+/534555 Reviewed-by: Heschi Kreinick <heschi@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
parent
96eeb4512b
commit
3aa2823d8b
|
|
@ -0,0 +1,133 @@
|
|||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cgotest
|
||||
|
||||
/*
|
||||
#include <windows.h>
|
||||
USHORT backtrace(ULONG FramesToCapture, PVOID *BackTrace) {
|
||||
#ifdef _AMD64_
|
||||
CONTEXT context;
|
||||
RtlCaptureContext(&context);
|
||||
ULONG64 ControlPc;
|
||||
ControlPc = context.Rip;
|
||||
int i;
|
||||
for (i = 0; i < FramesToCapture; i++) {
|
||||
PRUNTIME_FUNCTION FunctionEntry;
|
||||
ULONG64 ImageBase;
|
||||
VOID *HandlerData;
|
||||
ULONG64 EstablisherFrame;
|
||||
|
||||
FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL);
|
||||
|
||||
if (!FunctionEntry) {
|
||||
// For simplicity, don't unwind leaf entries, which are not used in this test.
|
||||
break;
|
||||
} else {
|
||||
RtlVirtualUnwind(0, ImageBase, ControlPc, FunctionEntry, &context, &HandlerData, &EstablisherFrame, NULL);
|
||||
}
|
||||
|
||||
ControlPc = context.Rip;
|
||||
// Check if we left the user range.
|
||||
if (ControlPc < 0x10000) {
|
||||
break;
|
||||
}
|
||||
|
||||
BackTrace[i] = (PVOID)(ControlPc);
|
||||
}
|
||||
return i;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"internal/testenv"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Test that the stack can be unwound through a call out and call back
|
||||
// into Go.
|
||||
func testCallbackCallersSEH(t *testing.T) {
|
||||
testenv.SkipIfOptimizationOff(t) // This test requires inlining.
|
||||
if runtime.Compiler != "gc" {
|
||||
// The exact function names are not going to be the same.
|
||||
t.Skip("skipping for non-gc toolchain")
|
||||
}
|
||||
if runtime.GOARCH != "amd64" {
|
||||
// TODO: support SEH on other architectures.
|
||||
t.Skip("skipping on non-amd64")
|
||||
}
|
||||
const cgoexpPrefix = "_cgoexp_"
|
||||
want := []string{
|
||||
"runtime.asmcgocall_landingpad",
|
||||
"runtime.asmcgocall",
|
||||
"runtime.cgocall",
|
||||
"test._Cfunc_backtrace",
|
||||
"test.testCallbackCallersSEH.func1.1",
|
||||
"test.testCallbackCallersSEH.func1",
|
||||
"test.goCallback",
|
||||
cgoexpPrefix + "goCallback",
|
||||
"runtime.cgocallbackg1",
|
||||
"runtime.cgocallbackg",
|
||||
"runtime.cgocallbackg",
|
||||
"runtime.cgocallback",
|
||||
"crosscall2",
|
||||
"runtime.asmcgocall_landingpad",
|
||||
"runtime.asmcgocall",
|
||||
"runtime.cgocall",
|
||||
"test._Cfunc_callback",
|
||||
"test.nestedCall.func1",
|
||||
"test.nestedCall",
|
||||
"test.testCallbackCallersSEH",
|
||||
"test.TestCallbackCallersSEH",
|
||||
"testing.tRunner",
|
||||
"testing.(*T).Run.gowrap1",
|
||||
"runtime.goexit",
|
||||
}
|
||||
pc := make([]uintptr, 100)
|
||||
n := 0
|
||||
nestedCall(func() {
|
||||
n = int(C.backtrace(C.DWORD(len(pc)), (*C.PVOID)(unsafe.Pointer(&pc[0]))))
|
||||
})
|
||||
got := make([]string, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
f := runtime.FuncForPC(pc[i] - 1)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
fname := f.Name()
|
||||
switch fname {
|
||||
case "goCallback", "callback":
|
||||
// TODO(qmuntal): investigate why these functions don't appear
|
||||
// when using the external linker.
|
||||
continue
|
||||
}
|
||||
// Skip cgo-generated functions, the runtime might not know about them,
|
||||
// depending on the link mode.
|
||||
if strings.HasPrefix(fname, "_cgo_") {
|
||||
continue
|
||||
}
|
||||
// Remove the cgo-generated random prefix.
|
||||
if strings.HasPrefix(fname, cgoexpPrefix) {
|
||||
idx := strings.Index(fname[len(cgoexpPrefix):], "_")
|
||||
if idx >= 0 {
|
||||
fname = cgoexpPrefix + fname[len(cgoexpPrefix)+idx+1:]
|
||||
}
|
||||
}
|
||||
// In module mode, this package has a fully-qualified import path.
|
||||
// Remove it if present.
|
||||
fname = strings.TrimPrefix(fname, "cmd/cgo/internal/")
|
||||
got = append(got, fname)
|
||||
}
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("incorrect backtrace:\nwant:\t%v\ngot:\t%v", want, got)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build cgo && windows
|
||||
|
||||
package cgotest
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCallbackCallersSEH(t *testing.T) { testCallbackCallersSEH(t) }
|
||||
|
|
@ -1161,11 +1161,11 @@ func dwarfblk(ctxt *Link, out *OutBuf, addr int64, size int64) {
|
|||
}
|
||||
|
||||
func pdatablk(ctxt *Link, out *OutBuf, addr int64, size int64) {
|
||||
writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, []loader.Sym{sehp.pdata}, addr, size, zeros[:])
|
||||
writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, sehp.pdata, addr, size, zeros[:])
|
||||
}
|
||||
|
||||
func xdatablk(ctxt *Link, out *OutBuf, addr int64, size int64) {
|
||||
writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, []loader.Sym{sehp.xdata}, addr, size, zeros[:])
|
||||
writeBlocks(ctxt, out, ctxt.outSem, ctxt.loader, sehp.xdata, addr, size, zeros[:])
|
||||
}
|
||||
|
||||
var covCounterDataStartOff, covCounterDataLen uint64
|
||||
|
|
@ -2199,14 +2199,14 @@ func (state *dodataState) allocateDwarfSections(ctxt *Link) {
|
|||
// allocateSEHSections allocate a sym.Section object for SEH
|
||||
// symbols, and assigns symbols to sections.
|
||||
func (state *dodataState) allocateSEHSections(ctxt *Link) {
|
||||
if sehp.pdata > 0 {
|
||||
sect := state.allocateDataSectionForSym(&Segpdata, sehp.pdata, 04)
|
||||
state.assignDsymsToSection(sect, []loader.Sym{sehp.pdata}, sym.SRODATA, aligndatsize)
|
||||
if len(sehp.pdata) > 0 {
|
||||
sect := state.allocateNamedDataSection(&Segpdata, ".pdata", []sym.SymKind{}, 04)
|
||||
state.assignDsymsToSection(sect, sehp.pdata, sym.SRODATA, aligndatsize)
|
||||
state.checkdatsize(sym.SSEHSECT)
|
||||
}
|
||||
if sehp.xdata > 0 {
|
||||
if len(sehp.xdata) > 0 {
|
||||
sect := state.allocateNamedDataSection(&Segxdata, ".xdata", []sym.SymKind{}, 04)
|
||||
state.assignDsymsToSection(sect, []loader.Sym{sehp.xdata}, sym.SRODATA, aligndatsize)
|
||||
state.assignDsymsToSection(sect, sehp.xdata, sym.SRODATA, aligndatsize)
|
||||
state.checkdatsize(sym.SSEHSECT)
|
||||
}
|
||||
}
|
||||
|
|
@ -2872,7 +2872,12 @@ func (ctxt *Link) address() []*sym.Segment {
|
|||
}
|
||||
}
|
||||
|
||||
for _, s := range []loader.Sym{sehp.pdata, sehp.xdata} {
|
||||
for _, s := range sehp.pdata {
|
||||
if sect := ldr.SymSect(s); sect != nil {
|
||||
ldr.AddToSymValue(s, int64(sect.Vaddr))
|
||||
}
|
||||
}
|
||||
for _, s := range sehp.xdata {
|
||||
if sect := ldr.SymSect(s); sect != nil {
|
||||
ldr.AddToSymValue(s, int64(sect.Vaddr))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2220,15 +2220,21 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
|
|||
0xc401, // arm
|
||||
0x64aa: // arm64
|
||||
ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
|
||||
textp, rsrc, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
|
||||
ls, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn)
|
||||
if err != nil {
|
||||
Errorf(nil, "%v", err)
|
||||
return
|
||||
}
|
||||
if len(rsrc) != 0 {
|
||||
setpersrc(ctxt, rsrc)
|
||||
if len(ls.Resources) != 0 {
|
||||
setpersrc(ctxt, ls.Resources)
|
||||
}
|
||||
ctxt.Textp = append(ctxt.Textp, textp...)
|
||||
if ls.PData != 0 {
|
||||
sehp.pdata = append(sehp.pdata, ls.PData)
|
||||
}
|
||||
if ls.XData != 0 {
|
||||
sehp.xdata = append(sehp.xdata, ls.XData)
|
||||
}
|
||||
ctxt.Textp = append(ctxt.Textp, ls.Textp...)
|
||||
}
|
||||
return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -635,11 +635,11 @@ func (f *peFile) emitRelocations(ctxt *Link) {
|
|||
{f.rdataSect, &Segrodata, ctxt.datap},
|
||||
{f.dataSect, &Segdata, ctxt.datap},
|
||||
}
|
||||
if sehp.pdata != 0 {
|
||||
sects = append(sects, relsect{f.pdataSect, &Segpdata, []loader.Sym{sehp.pdata}})
|
||||
if len(sehp.pdata) != 0 {
|
||||
sects = append(sects, relsect{f.pdataSect, &Segpdata, sehp.pdata})
|
||||
}
|
||||
if sehp.xdata != 0 {
|
||||
sects = append(sects, relsect{f.xdataSect, &Segxdata, []loader.Sym{sehp.xdata}})
|
||||
if len(sehp.xdata) != 0 {
|
||||
sects = append(sects, relsect{f.xdataSect, &Segxdata, sehp.xdata})
|
||||
}
|
||||
for _, s := range sects {
|
||||
s.peSect.emitRelocations(ctxt.Out, func() int {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import (
|
|||
)
|
||||
|
||||
var sehp struct {
|
||||
pdata loader.Sym
|
||||
xdata loader.Sym
|
||||
pdata []sym.LoaderSym
|
||||
xdata []sym.LoaderSym
|
||||
}
|
||||
|
||||
func writeSEH(ctxt *Link) {
|
||||
|
|
@ -72,6 +72,6 @@ func writeSEHAMD64(ctxt *Link) {
|
|||
pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, s, ldr.SymSize(s))
|
||||
pdata.AddPEImageRelativeAddrPlus(ctxt.Arch, xdata.Sym(), off)
|
||||
}
|
||||
sehp.pdata = pdata.Sym()
|
||||
sehp.xdata = xdata.Sym()
|
||||
sehp.pdata = append(sehp.pdata, pdata.Sym())
|
||||
sehp.xdata = append(sehp.xdata, xdata.Sym())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,12 +221,17 @@ type peLoaderState struct {
|
|||
// is symbol size (or -1 if we're using the "any" strategy).
|
||||
var comdatDefinitions map[string]int64
|
||||
|
||||
// Symbols contains the symbols that can be loaded from a PE file.
|
||||
type Symbols struct {
|
||||
Textp []loader.Sym // text symbols
|
||||
Resources []loader.Sym // .rsrc section or set of .rsrc$xx sections
|
||||
PData loader.Sym
|
||||
XData loader.Sym
|
||||
}
|
||||
|
||||
// Load loads the PE file pn from input.
|
||||
// Symbols from the object file are created via the loader 'l',
|
||||
// and a slice of the text symbols is returned.
|
||||
// If an .rsrc section or set of .rsrc$xx sections is found, its symbols are
|
||||
// returned as rsrc.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) {
|
||||
// Symbols from the object file are created via the loader 'l'.
|
||||
func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (*Symbols, error) {
|
||||
state := &peLoaderState{
|
||||
l: l,
|
||||
arch: arch,
|
||||
|
|
@ -249,11 +254,13 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
// TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
|
||||
f, err := pe.NewFile(sr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
state.f = f
|
||||
|
||||
var ls Symbols
|
||||
|
||||
// TODO return error if found .cormeta
|
||||
|
||||
// create symbols for mapped sections
|
||||
|
|
@ -274,7 +281,12 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
|
||||
switch sect.Characteristics & (pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE | pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE) {
|
||||
case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ: //.rdata
|
||||
bld.SetType(sym.SRODATA)
|
||||
if issehsect(arch, sect) {
|
||||
bld.SetType(sym.SSEHSECT)
|
||||
bld.SetAlign(4)
|
||||
} else {
|
||||
bld.SetType(sym.SRODATA)
|
||||
}
|
||||
|
||||
case pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.bss
|
||||
bld.SetType(sym.SNOPTRBSS)
|
||||
|
|
@ -286,13 +298,13 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
bld.SetType(sym.STEXT)
|
||||
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
|
||||
return nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
|
||||
}
|
||||
|
||||
if bld.Type() != sym.SNOPTRBSS {
|
||||
data, err := sect.Data()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
state.sectdata[sect] = data
|
||||
bld.SetData(data)
|
||||
|
|
@ -300,13 +312,19 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
bld.SetSize(int64(sect.Size))
|
||||
state.sectsyms[sect] = s
|
||||
if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") {
|
||||
rsrc = append(rsrc, s)
|
||||
ls.Resources = append(ls.Resources, s)
|
||||
} else if bld.Type() == sym.SSEHSECT {
|
||||
if sect.Name == ".pdata" {
|
||||
ls.PData = s
|
||||
} else if sect.Name == ".xdata" {
|
||||
ls.XData = s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make a prepass over the symbols to collect info about COMDAT symbols.
|
||||
if err := state.preprocessSymbols(); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// load relocations
|
||||
|
|
@ -327,22 +345,23 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
}
|
||||
|
||||
splitResources := strings.HasPrefix(rsect.Name, ".rsrc$")
|
||||
issehsect := issehsect(arch, rsect)
|
||||
sb := l.MakeSymbolUpdater(state.sectsyms[rsect])
|
||||
for j, r := range rsect.Relocs {
|
||||
if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
|
||||
return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
|
||||
return nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
|
||||
}
|
||||
pesym := &f.COFFSymbols[r.SymbolTableIndex]
|
||||
_, gosym, err := state.readpesym(pesym)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
if gosym == 0 {
|
||||
name, err := pesym.FullName(f.StringTable)
|
||||
if err != nil {
|
||||
name = string(pesym.Name[:])
|
||||
}
|
||||
return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
|
||||
return nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
|
||||
}
|
||||
|
||||
rSym := gosym
|
||||
|
|
@ -352,21 +371,29 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
var rType objabi.RelocType
|
||||
switch arch.Family {
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
|
||||
return nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
|
||||
case sys.I386, sys.AMD64:
|
||||
switch r.Type {
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, state.sectsyms[rsect], r.Type)
|
||||
return nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, state.sectsyms[rsect], r.Type)
|
||||
|
||||
case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
|
||||
IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
|
||||
IMAGE_REL_AMD64_ADDR32NB:
|
||||
rType = objabi.R_PCREL
|
||||
if r.Type == IMAGE_REL_AMD64_ADDR32NB {
|
||||
rType = objabi.R_PEIMAGEOFF
|
||||
} else {
|
||||
rType = objabi.R_PCREL
|
||||
}
|
||||
|
||||
rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
|
||||
|
||||
case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
|
||||
rType = objabi.R_ADDR
|
||||
if r.Type == IMAGE_REL_I386_DIR32NB {
|
||||
rType = objabi.R_PEIMAGEOFF
|
||||
} else {
|
||||
rType = objabi.R_ADDR
|
||||
}
|
||||
|
||||
// load addend from image
|
||||
rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
|
||||
|
|
@ -383,7 +410,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
case sys.ARM:
|
||||
switch r.Type {
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, state.sectsyms[rsect], r.Type)
|
||||
return nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, state.sectsyms[rsect], r.Type)
|
||||
|
||||
case IMAGE_REL_ARM_SECREL:
|
||||
rType = objabi.R_PCREL
|
||||
|
|
@ -391,7 +418,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
|
||||
|
||||
case IMAGE_REL_ARM_ADDR32, IMAGE_REL_ARM_ADDR32NB:
|
||||
rType = objabi.R_ADDR
|
||||
if r.Type == IMAGE_REL_ARM_ADDR32NB {
|
||||
rType = objabi.R_PEIMAGEOFF
|
||||
} else {
|
||||
rType = objabi.R_ADDR
|
||||
}
|
||||
|
||||
rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
|
||||
|
||||
|
|
@ -404,10 +435,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
case sys.ARM64:
|
||||
switch r.Type {
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("%s: %v: unknown ARM64 relocation type %v", pn, state.sectsyms[rsect], r.Type)
|
||||
return nil, fmt.Errorf("%s: %v: unknown ARM64 relocation type %v", pn, state.sectsyms[rsect], r.Type)
|
||||
|
||||
case IMAGE_REL_ARM64_ADDR32, IMAGE_REL_ARM64_ADDR32NB:
|
||||
rType = objabi.R_ADDR
|
||||
if r.Type == IMAGE_REL_ARM64_ADDR32NB {
|
||||
rType = objabi.R_PEIMAGEOFF
|
||||
} else {
|
||||
rType = objabi.R_ADDR
|
||||
}
|
||||
|
||||
rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:])))
|
||||
}
|
||||
|
|
@ -420,12 +455,20 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
if issect(pesym) || splitResources {
|
||||
rAdd += int64(pesym.Value)
|
||||
}
|
||||
if issehsect {
|
||||
// .pdata and .xdata sections can contain records
|
||||
// associated to functions that won't be used in
|
||||
// the final binary, in which case the relocation
|
||||
// target symbol won't be reachable.
|
||||
rType |= objabi.R_WEAK
|
||||
}
|
||||
|
||||
rel, _ := sb.AddRel(rType)
|
||||
rel.SetOff(rOff)
|
||||
rel.SetSiz(rSize)
|
||||
rel.SetSym(rSym)
|
||||
rel.SetAdd(rAdd)
|
||||
|
||||
}
|
||||
|
||||
sb.SortRelocs()
|
||||
|
|
@ -439,7 +482,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
|
||||
name, err := pesym.FullName(f.StringTable)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
if name == "" {
|
||||
continue
|
||||
|
|
@ -477,7 +520,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
|
||||
bld, s, err := state.readpesym(pesym)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pesym.SectionNumber == 0 { // extern
|
||||
|
|
@ -491,14 +534,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
} else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
|
||||
sect = f.Sections[pesym.SectionNumber-1]
|
||||
if _, found := state.sectsyms[sect]; !found {
|
||||
return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
|
||||
return nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s)
|
||||
}
|
||||
} else {
|
||||
return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
|
||||
return nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s)
|
||||
}
|
||||
|
||||
if sect == nil {
|
||||
return nil, nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check for COMDAT symbol.
|
||||
|
|
@ -517,7 +560,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
}
|
||||
outerName := l.SymName(l.OuterSym(s))
|
||||
sectName := l.SymName(state.sectsyms[sect])
|
||||
return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
|
||||
return nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName)
|
||||
}
|
||||
|
||||
bld = makeUpdater(l, bld, s)
|
||||
|
|
@ -528,7 +571,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
bld.SetSize(4)
|
||||
if l.SymType(sectsym) == sym.STEXT {
|
||||
if bld.External() && !bld.DuplicateOK() {
|
||||
return nil, nil, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
|
||||
return nil, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s))
|
||||
}
|
||||
bld.SetExternal(true)
|
||||
}
|
||||
|
|
@ -536,7 +579,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
// This is a COMDAT definition. Record that we're picking
|
||||
// this instance so that we can ignore future defs.
|
||||
if _, ok := comdatDefinitions[l.SymName(s)]; ok {
|
||||
return nil, nil, fmt.Errorf("internal error: preexisting COMDAT definition for %q", name)
|
||||
return nil, fmt.Errorf("internal error: preexisting COMDAT definition for %q", name)
|
||||
}
|
||||
comdatDefinitions[l.SymName(s)] = sz
|
||||
}
|
||||
|
|
@ -554,15 +597,19 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
|||
if l.SymType(s) == sym.STEXT {
|
||||
for ; s != 0; s = l.SubSym(s) {
|
||||
if l.AttrOnList(s) {
|
||||
return nil, nil, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
|
||||
return nil, fmt.Errorf("symbol %s listed multiple times", l.SymName(s))
|
||||
}
|
||||
l.SetAttrOnList(s, true)
|
||||
textp = append(textp, s)
|
||||
ls.Textp = append(ls.Textp, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return textp, rsrc, nil
|
||||
if ls.PData != 0 {
|
||||
processSEH(l, arch, ls.PData, ls.XData)
|
||||
}
|
||||
|
||||
return &ls, nil
|
||||
}
|
||||
|
||||
// PostProcessImports works to resolve inconsistencies with DLL import
|
||||
|
|
@ -643,6 +690,10 @@ func PostProcessImports() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func issehsect(arch *sys.Arch, s *pe.Section) bool {
|
||||
return arch.Family == sys.AMD64 && (s.Name == ".pdata" || s.Name == ".xdata")
|
||||
}
|
||||
|
||||
func issect(s *pe.COFFSymbol) bool {
|
||||
return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package loadpe
|
||||
|
||||
import (
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"cmd/link/internal/loader"
|
||||
"cmd/link/internal/sym"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
UNW_FLAG_EHANDLER = 1 << 3
|
||||
UNW_FLAG_UHANDLER = 2 << 3
|
||||
UNW_FLAG_CHAININFO = 3 << 3
|
||||
unwStaticDataSize = 8
|
||||
)
|
||||
|
||||
// processSEH walks all pdata relocations looking for exception handler function symbols.
|
||||
// We want to mark these as reachable if the function that they protect is reachable
|
||||
// in the final binary.
|
||||
func processSEH(ldr *loader.Loader, arch *sys.Arch, pdata sym.LoaderSym, xdata sym.LoaderSym) error {
|
||||
switch arch.Family {
|
||||
case sys.AMD64:
|
||||
ldr.SetAttrReachable(pdata, true)
|
||||
if xdata != 0 {
|
||||
ldr.SetAttrReachable(xdata, true)
|
||||
}
|
||||
return processSEHAMD64(ldr, pdata)
|
||||
default:
|
||||
// TODO: support SEH on other architectures.
|
||||
return fmt.Errorf("unsupported architecture for SEH: %v", arch.Family)
|
||||
}
|
||||
}
|
||||
|
||||
func processSEHAMD64(ldr *loader.Loader, pdata sym.LoaderSym) error {
|
||||
// The following loop traverses a list of pdata entries,
|
||||
// each entry being 3 relocations long. The first relocation
|
||||
// is a pointer to the function symbol to which the pdata entry
|
||||
// corresponds. The third relocation is a pointer to the
|
||||
// corresponding .xdata entry.
|
||||
// Reference:
|
||||
// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function
|
||||
rels := ldr.Relocs(pdata)
|
||||
if rels.Count()%3 != 0 {
|
||||
return fmt.Errorf(".pdata symbol %q has invalid relocation count", ldr.SymName(pdata))
|
||||
}
|
||||
for i := 0; i < rels.Count(); i += 3 {
|
||||
xrel := rels.At(i + 2)
|
||||
handler := findHandlerInXDataAMD64(ldr, xrel.Sym(), xrel.Add())
|
||||
if handler != 0 {
|
||||
sb := ldr.MakeSymbolUpdater(rels.At(i).Sym())
|
||||
r, _ := sb.AddRel(objabi.R_KEEP)
|
||||
r.SetSym(handler)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findHandlerInXDataAMD64 finds the symbol in the .xdata section that
|
||||
// corresponds to the exception handler.
|
||||
// Reference:
|
||||
// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
|
||||
func findHandlerInXDataAMD64(ldr *loader.Loader, xsym sym.LoaderSym, add int64) loader.Sym {
|
||||
data := ldr.Data(xsym)
|
||||
if add < 0 || add+unwStaticDataSize > int64(len(data)) {
|
||||
return 0
|
||||
}
|
||||
data = data[add:]
|
||||
var isChained bool
|
||||
switch flag := data[0]; {
|
||||
case flag&UNW_FLAG_EHANDLER != 0 || flag&UNW_FLAG_UHANDLER != 0:
|
||||
// Exception handler.
|
||||
case flag&UNW_FLAG_CHAININFO != 0:
|
||||
isChained = true
|
||||
default:
|
||||
// Nothing to do.
|
||||
return 0
|
||||
}
|
||||
codes := data[3]
|
||||
if codes%2 != 0 {
|
||||
// There are always an even number of unwind codes, even if the last one is unused.
|
||||
codes += 1
|
||||
}
|
||||
// The exception handler relocation is the first relocation after the unwind codes,
|
||||
// unless it is chained, but we will handle this case later.
|
||||
targetOff := add + unwStaticDataSize*(1+int64(codes))
|
||||
xrels := ldr.Relocs(xsym)
|
||||
idx := sort.Search(xrels.Count(), func(i int) bool {
|
||||
return int64(xrels.At(i).Off()) >= targetOff
|
||||
})
|
||||
if idx == 0 {
|
||||
return 0
|
||||
}
|
||||
if isChained {
|
||||
// The third relocations references the next .xdata entry in the chain, recurse.
|
||||
idx += 2
|
||||
if idx >= xrels.Count() {
|
||||
return 0
|
||||
}
|
||||
r := xrels.At(idx)
|
||||
return findHandlerInXDataAMD64(ldr, r.Sym(), r.Add())
|
||||
}
|
||||
return xrels.At(idx).Sym()
|
||||
}
|
||||
Loading…
Reference in New Issue