mirror of
https://github.com/golang/go
synced 2024-09-29 11:34:32 -06:00
cmd/link/internal/loadpe: add rudimentary COMDAT support
Add some rudimentary support to the PE file loader for handling sections in COMDAT when reading host object files. This is needed in order to link programs with support libraries that are of a more modern vintage than GCC 5.X. If a given section XYZ is in COMDAT, the symbol for that section will be flagged, e.g. section 'Characteristics' field will have the IMAGE_SCN_LNK_COMDAT bit set, and the symbol will be followed by an "aux" symbol that includes the COMDAT handling strategy that the linker needs to use. This patch supports two COMDAT strategies (IMAGE_COMDAT_SELECT_ANY and IMAGE_COMDAT_SELECT_SAME_SIZE); more work will have to be done in the future to support other flavors if it turns out that they are needed. Updates #35006. Change-Id: I516e825c30ed3df94ba08323b8a24fb847e10c1a Reviewed-on: https://go-review.googlesource.com/c/go/+/383835 Reviewed-by: Cherry Mui <cherryyz@google.com> Trust: Than McIntosh <thanm@google.com> Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
378221bd6e
commit
1edc2769ca
@ -135,17 +135,6 @@ const (
|
||||
IMAGE_REL_ARM64_REL32 = 0x0011
|
||||
)
|
||||
|
||||
// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld, ideally in debug/pe.
|
||||
const (
|
||||
IMAGE_SCN_CNT_CODE = 0x00000020
|
||||
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
|
||||
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
|
||||
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000
|
||||
IMAGE_SCN_MEM_EXECUTE = 0x20000000
|
||||
IMAGE_SCN_MEM_READ = 0x40000000
|
||||
IMAGE_SCN_MEM_WRITE = 0x80000000
|
||||
)
|
||||
|
||||
// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf
|
||||
|
||||
// peBiobuf makes bio.Reader look like io.ReaderAt.
|
||||
@ -179,12 +168,19 @@ type peLoaderState struct {
|
||||
l *loader.Loader
|
||||
arch *sys.Arch
|
||||
f *pe.File
|
||||
pn string
|
||||
sectsyms map[*pe.Section]loader.Sym
|
||||
defWithImp map[string]struct{}
|
||||
comdats map[uint16]int64 // key is section index, val is size
|
||||
sectdata map[*pe.Section][]byte
|
||||
localSymVersion int
|
||||
}
|
||||
|
||||
// comdatDefinitions records the names of symbols for which we've
|
||||
// previously seen a definition in COMDAT. Key is symbol name, value
|
||||
// is symbol size (or -1 if we're using the "any" strategy).
|
||||
var comdatDefinitions = make(map[string]int64)
|
||||
|
||||
// Load loads the PE file pn from input.
|
||||
// Symbols are written into syms, and a slice of the text symbols is returned.
|
||||
// If an .rsrc section or set of .rsrc$xx sections is found, its symbols are
|
||||
@ -196,6 +192,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
sectsyms: make(map[*pe.Section]loader.Sym),
|
||||
sectdata: make(map[*pe.Section][]byte),
|
||||
localSymVersion: localSymVersion,
|
||||
pn: pn,
|
||||
}
|
||||
|
||||
// Some input files are archives containing multiple of
|
||||
@ -216,11 +213,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
|
||||
// create symbols for mapped sections
|
||||
for _, sect := range f.Sections {
|
||||
if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
|
||||
if sect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
|
||||
if sect.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
|
||||
// This has been seen for .idata sections, which we
|
||||
// want to ignore. See issues 5106 and 5273.
|
||||
continue
|
||||
@ -230,17 +227,17 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
s := state.l.LookupOrCreateCgoExport(name, localSymVersion)
|
||||
bld := l.MakeSymbolUpdater(s)
|
||||
|
||||
switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
|
||||
case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
|
||||
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)
|
||||
|
||||
case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss
|
||||
case pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.bss
|
||||
bld.SetType(sym.SNOPTRBSS)
|
||||
|
||||
case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data
|
||||
case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.data
|
||||
bld.SetType(sym.SNOPTRDATA)
|
||||
|
||||
case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text
|
||||
case pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE | pe.IMAGE_SCN_MEM_READ: //.text
|
||||
bld.SetType(sym.STEXT)
|
||||
|
||||
default:
|
||||
@ -277,10 +274,10 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
if rsect.NumberOfRelocations == 0 {
|
||||
continue
|
||||
}
|
||||
if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
|
||||
if rsect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 {
|
||||
continue
|
||||
}
|
||||
if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
|
||||
if rsect.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
|
||||
// This has been seen for .idata sections, which we
|
||||
// want to ignore. See issues 5106 and 5273.
|
||||
continue
|
||||
@ -465,6 +462,16 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// Check for COMDAT symbol.
|
||||
if sz, ok1 := state.comdats[uint16(pesym.SectionNumber-1)]; ok1 {
|
||||
if psz, ok2 := comdatDefinitions[l.SymName(s)]; ok2 {
|
||||
if sz == psz {
|
||||
// OK to discard, we've seen an instance
|
||||
// already.
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if l.OuterSym(s) != 0 {
|
||||
if l.AttrDuplicateOK(s) {
|
||||
continue
|
||||
@ -486,6 +493,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
|
||||
}
|
||||
bld.SetExternal(true)
|
||||
}
|
||||
if sz, ok := state.comdats[uint16(pesym.SectionNumber-1)]; ok {
|
||||
// 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)
|
||||
}
|
||||
comdatDefinitions[l.SymName(s)] = sz
|
||||
}
|
||||
}
|
||||
|
||||
// Sort outer lists by address, adding to textp.
|
||||
@ -583,8 +598,20 @@ func (state *peLoaderState) readpesym(pesym *pe.COFFSymbol) (*loader.SymbolBuild
|
||||
// reading and looks for cases where we have both a symbol definition
|
||||
// for "XXX" and an "__imp_XXX" symbol, recording these cases in a map
|
||||
// in the state struct. This information will be used in readpesym()
|
||||
// above to give such symbols special treatment.
|
||||
// above to give such symbols special treatment. This function also
|
||||
// gathers information about COMDAT sections/symbols for later use
|
||||
// in readpesym().
|
||||
func (state *peLoaderState) preprocessSymbols() error {
|
||||
|
||||
// Locate comdat sections.
|
||||
state.comdats = make(map[uint16]int64)
|
||||
for i, s := range state.f.Sections {
|
||||
if s.Characteristics&uint32(pe.IMAGE_SCN_LNK_COMDAT) != 0 {
|
||||
state.comdats[uint16(i)] = int64(s.Size)
|
||||
}
|
||||
}
|
||||
|
||||
// Examine symbol defs.
|
||||
imp := make(map[string]struct{})
|
||||
def := make(map[string]struct{})
|
||||
for i, numaux := 0, 0; i < len(state.f.COFFSymbols); i += numaux + 1 {
|
||||
@ -601,6 +628,29 @@ func (state *peLoaderState) preprocessSymbols() error {
|
||||
if strings.HasPrefix(symname, "__imp_") {
|
||||
imp[strings.TrimPrefix(symname, "__imp_")] = struct{}{}
|
||||
}
|
||||
if _, isc := state.comdats[uint16(pesym.SectionNumber-1)]; !isc {
|
||||
continue
|
||||
}
|
||||
if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) {
|
||||
continue
|
||||
}
|
||||
// This symbol corresponds to a COMDAT section. Read the
|
||||
// aux data for it.
|
||||
auxsymp, err := state.f.COFFSymbolReadSectionDefAux(i)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read aux info for section def symbol %d %s: pe.COFFSymbolReadComdatInfo returns %v", i, symname, err)
|
||||
}
|
||||
if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_SAME_SIZE {
|
||||
// This is supported.
|
||||
} else if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_ANY {
|
||||
// Also supported.
|
||||
state.comdats[uint16(pesym.SectionNumber-1)] = int64(-1)
|
||||
} else {
|
||||
// We don't support any of the other strategies at the
|
||||
// moment. I suspect that we may need to also support
|
||||
// "associative", we'll see.
|
||||
return fmt.Errorf("internal error: unsupported COMDAT selection strategy found in path=%s sec=%d strategy=%d idx=%d, please file a bug", state.pn, auxsymp.SecNum, auxsymp.Selection, i)
|
||||
}
|
||||
}
|
||||
state.defWithImp = make(map[string]struct{})
|
||||
for n := range imp {
|
||||
|
Loading…
Reference in New Issue
Block a user