Skip to content

Commit e913729

Browse files
arizvisaalexbrainman
authored andcommitted
debug/pe: parse the import directory correctly
This parses the import table properly which allows for debug/pe to extract import symbols from pecoffs linked with an import table in a section named something other than ".idata" The section names in a pecoff object aren't guaranteed to actually mean anything, so hardcoding a search for the ".idata" section is not guaranteed to find the import table in all shared libraries. This resulted in debug/pe being unable to read import symbols from some libraries. The proper way to locate the import table is to validate the number of data directory entries, locate the import entry, and then use the va to identify the section containing the import table. This patch does exactly this. Fixes #16103. Change-Id: I3ab6de7f896a0c56bb86c3863e504e8dd4c8faf3 GitHub-Last-Rev: ce8077c GitHub-Pull-Request: #25193 Reviewed-on: https://go-review.googlesource.com/110555 Run-TryBot: Alex Brainman <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Alex Brainman <[email protected]>
1 parent 529362e commit e913729

File tree

3 files changed

+91
-3
lines changed

3 files changed

+91
-3
lines changed

src/debug/pe/file.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,19 +260,59 @@ type ImportDirectory struct {
260260
// It does not return weak symbols.
261261
func (f *File) ImportedSymbols() ([]string, error) {
262262
pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
263-
ds := f.Section(".idata")
263+
264+
// grab the number of data directory entries
265+
var dd_length uint32
266+
if pe64 {
267+
dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
268+
} else {
269+
dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
270+
}
271+
272+
// check that the length of data directory entries is large
273+
// enough to include the imports directory.
274+
if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT + 1 {
275+
return nil, nil
276+
}
277+
278+
// grab the import data directory entry
279+
var idd DataDirectory
280+
if pe64 {
281+
idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
282+
} else {
283+
idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
284+
}
285+
286+
// figure out which section contains the import directory table
287+
var ds *Section
288+
ds = nil
289+
for _, s := range f.Sections {
290+
if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress + s.VirtualSize {
291+
ds = s
292+
break
293+
}
294+
}
295+
296+
// didn't find a section, so no import libraries were found
264297
if ds == nil {
265-
// not dynamic, so no libraries
266298
return nil, nil
267299
}
300+
268301
d, err := ds.Data()
269302
if err != nil {
270303
return nil, err
271304
}
305+
306+
// seek to the virtual address specified in the import data directory
307+
d = d[idd.VirtualAddress - ds.VirtualAddress:]
308+
309+
// start decoding the import directory
272310
var ida []ImportDirectory
273311
for len(d) > 0 {
274312
var dt ImportDirectory
275313
dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
314+
dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
315+
dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
276316
dt.Name = binary.LittleEndian.Uint32(d[12:16])
277317
dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
278318
d = d[20:]
@@ -282,7 +322,7 @@ func (f *File) ImportedSymbols() ([]string, error) {
282322
ida = append(ida, dt)
283323
}
284324
// TODO(brainman): this needs to be rewritten
285-
// ds.Data() return contets of .idata section. Why store in variable called "names"?
325+
// ds.Data() returns contents of section containing import table. Why store in variable called "names"?
286326
// Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
287327
// getString does not extracts a string from symbol string table (as getString doco says).
288328
// Why ds.Data() called again and again in the loop?

src/debug/pe/file_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,3 +532,31 @@ func TestBuildingWindowsGUI(t *testing.T) {
532532
t.Fatalf("unexpected OptionalHeader type: have %T, but want *pe.OptionalHeader32 or *pe.OptionalHeader64", oh)
533533
}
534534
}
535+
536+
func TestImportTableInUnknownSection(t *testing.T) {
537+
if runtime.GOOS != "windows" {
538+
t.Skip("skipping windows only test")
539+
}
540+
541+
// first we need to find this font driver
542+
path, err := exec.LookPath("atmfd.dll")
543+
if err != nil {
544+
t.Fatalf("unable to locate required file %q in search path: %s", "atmfd.dll", err)
545+
}
546+
547+
f, err := Open(path)
548+
if err != nil {
549+
t.Error(err)
550+
}
551+
defer f.Close()
552+
553+
// now we can extract its imports
554+
symbols, err := f.ImportedSymbols()
555+
if err != nil {
556+
t.Error(err)
557+
}
558+
559+
if len(symbols) == 0 {
560+
t.Fatalf("unable to locate any imported symbols within file %q.", path)
561+
}
562+
}

src/debug/pe/pe.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,23 @@ const (
108108
IMAGE_FILE_MACHINE_THUMB = 0x1c2
109109
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
110110
)
111+
112+
// IMAGE_DIRECTORY_ENTRY constants
113+
const (
114+
IMAGE_DIRECTORY_ENTRY_EXPORT = 0
115+
IMAGE_DIRECTORY_ENTRY_IMPORT = 1
116+
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2
117+
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3
118+
IMAGE_DIRECTORY_ENTRY_SECURITY = 4
119+
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
120+
IMAGE_DIRECTORY_ENTRY_DEBUG = 6
121+
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7
122+
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8
123+
IMAGE_DIRECTORY_ENTRY_TLS = 9
124+
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10
125+
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11
126+
IMAGE_DIRECTORY_ENTRY_IAT = 12
127+
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13
128+
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14
129+
)
130+

0 commit comments

Comments
 (0)