@@ -29,7 +29,11 @@ import (
2929
3030// A File represents an open PE file. 
3131type  File  struct  {
32- 	FileHeader 
32+ 	// FileHeader is populated for regular COFF files 
33+ 	FileHeader  * FileHeader 
34+ 	// BigObjHeader is populated for bigobj COFF files 
35+ 	BigObjHeader  * BigObjHeader 
36+ 
3337	OptionalHeader  any  // of type *OptionalHeader32 or *OptionalHeader64 
3438	Sections        []* Section 
3539	Symbols         []* Symbol     // COFF symbols with auxiliary symbol records removed 
@@ -39,6 +43,69 @@ type File struct {
3943	closer  io.Closer 
4044}
4145
46+ // IsBigObj reports whether the file is a bigobj COFF file. 
47+ func  (f  * File ) IsBigObj () bool  {
48+ 	return  f .BigObjHeader  !=  nil 
49+ }
50+ 
51+ // GetMachine returns the machine type from the appropriate header. 
52+ func  (f  * File ) GetMachine () uint16  {
53+ 	if  f .BigObjHeader  !=  nil  {
54+ 		return  f .BigObjHeader .Machine 
55+ 	}
56+ 	return  f .FileHeader .Machine 
57+ }
58+ 
59+ // GetNumberOfSections returns the number of sections from the appropriate header. 
60+ func  (f  * File ) GetNumberOfSections () uint32  {
61+ 	if  f .BigObjHeader  !=  nil  {
62+ 		return  f .BigObjHeader .NumberOfSections 
63+ 	}
64+ 	return  uint32 (f .FileHeader .NumberOfSections )
65+ }
66+ 
67+ // GetTimeDateStamp returns the timestamp from the appropriate header. 
68+ func  (f  * File ) GetTimeDateStamp () uint32  {
69+ 	if  f .BigObjHeader  !=  nil  {
70+ 		return  f .BigObjHeader .TimeDateStamp 
71+ 	}
72+ 	return  f .FileHeader .TimeDateStamp 
73+ }
74+ 
75+ // GetPointerToSymbolTable returns the symbol table pointer from the appropriate header. 
76+ func  (f  * File ) GetPointerToSymbolTable () uint32  {
77+ 	if  f .BigObjHeader  !=  nil  {
78+ 		return  f .BigObjHeader .PointerToSymbolTable 
79+ 	}
80+ 	return  f .FileHeader .PointerToSymbolTable 
81+ }
82+ 
83+ // GetNumberOfSymbols returns the number of symbols from the appropriate header. 
84+ func  (f  * File ) GetNumberOfSymbols () uint32  {
85+ 	if  f .BigObjHeader  !=  nil  {
86+ 		return  f .BigObjHeader .NumberOfSymbols 
87+ 	}
88+ 	return  f .FileHeader .NumberOfSymbols 
89+ }
90+ 
91+ // GetSizeOfOptionalHeader returns the optional header size from the appropriate header. 
92+ // BigObj files don't have optional headers, so this returns 0 for them. 
93+ func  (f  * File ) GetSizeOfOptionalHeader () uint16  {
94+ 	if  f .BigObjHeader  !=  nil  {
95+ 		return  0 
96+ 	}
97+ 	return  f .FileHeader .SizeOfOptionalHeader 
98+ }
99+ 
100+ // GetCharacteristics returns the characteristics from the appropriate header. 
101+ // BigObj files don't have characteristics, so this returns 0 for them. 
102+ func  (f  * File ) GetCharacteristics () uint16  {
103+ 	if  f .BigObjHeader  !=  nil  {
104+ 		return  0 
105+ 	}
106+ 	return  f .FileHeader .Characteristics 
107+ }
108+ 
42109// Open opens the named file using [os.Open] and prepares it for use as a PE binary. 
43110func  Open (name  string ) (* File , error ) {
44111	f , err  :=  os .Open (name )
@@ -68,6 +135,69 @@ func (f *File) Close() error {
68135
69136// TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance) 
70137
138+ // isBigObjFormat detects if the reader contains a bigobj COFF file by checking 
139+ // the signature and GUID. The reader position should be at the start of the COFF header. 
140+ func  isBigObjFormat (r  io.ReadSeeker ) (bool , error ) {
141+ 	currentPos , err  :=  r .Seek (0 , io .SeekCurrent )
142+ 	if  err  !=  nil  {
143+ 		return  false , err 
144+ 	}
145+ 	defer  r .Seek (currentPos , io .SeekStart )
146+ 
147+ 	// Read the first part of what could be a BigObjHeader 
148+ 	var  sig  struct  {
149+ 		Sig1           uint16 
150+ 		Sig2           uint16 
151+ 		Version        uint16 
152+ 		Machine        uint16 
153+ 		TimeDateStamp  uint32 
154+ 		ClassID        [16 ]uint8 
155+ 	}
156+ 
157+ 	err  =  binary .Read (r , binary .LittleEndian , & sig )
158+ 	if  err  !=  nil  {
159+ 		return  false , err 
160+ 	}
161+ 
162+ 	if  sig .Sig1  !=  BigObjSig1  ||  sig .Sig2  !=  BigObjSig2  {
163+ 		return  false , nil 
164+ 	}
165+ 
166+ 	if  sig .ClassID  !=  BigObjClassID  {
167+ 		return  false , nil 
168+ 	}
169+ 
170+ 	return  true , nil 
171+ }
172+ 
173+ // readCOFFHeader reads the appropriate COFF header type ("regular" or bigobj). 
174+ // The unused header type will be nil 
175+ func  readCOFFHeader (sr  * io.SectionReader , base  int64 ) (* FileHeader , * BigObjHeader , error ) {
176+ 	_ , err  :=  sr .Seek (base , io .SeekStart )
177+ 	if  err  !=  nil  {
178+ 		return  nil , nil , err 
179+ 	}
180+ 
181+ 	isBigObj , err  :=  isBigObjFormat (sr )
182+ 	if  err  !=  nil  {
183+ 		return  nil , nil , err 
184+ 	}
185+ 
186+ 	if  isBigObj  {
187+ 		bigObjHeader  :=  new (BigObjHeader )
188+ 		if  err  :=  binary .Read (sr , binary .LittleEndian , bigObjHeader ); err  !=  nil  {
189+ 			return  nil , nil , err 
190+ 		}
191+ 		return  nil , bigObjHeader , nil 
192+ 	} else  {
193+ 		fileHeader  :=  new (FileHeader )
194+ 		if  err  :=  binary .Read (sr , binary .LittleEndian , fileHeader ); err  !=  nil  {
195+ 			return  nil , nil , err 
196+ 		}
197+ 		return  fileHeader , nil , nil 
198+ 	}
199+ }
200+ 
71201// NewFile creates a new [File] for accessing a PE binary in an underlying reader. 
72202func  NewFile (r  io.ReaderAt ) (* File , error ) {
73203	f  :=  new (File )
@@ -89,11 +219,24 @@ func NewFile(r io.ReaderAt) (*File, error) {
89219	} else  {
90220		base  =  int64 (0 )
91221	}
92- 	sr .Seek (base , io .SeekStart )
93- 	if  err  :=  binary .Read (sr , binary .LittleEndian , & f .FileHeader ); err  !=  nil  {
222+ 	// Read appropriate header type - unused header will be nil 
223+ 	fileHeader , bigObjHeader , err  :=  readCOFFHeader (sr , base )
224+ 	if  err  !=  nil  {
94225		return  nil , err 
95226	}
96- 	switch  f .FileHeader .Machine  {
227+ 	f .FileHeader  =  fileHeader 
228+ 	f .BigObjHeader  =  bigObjHeader 
229+ 
230+ 	// Calculate header size based on actual type 
231+ 	var  headerSize  int 
232+ 	if  f .BigObjHeader  !=  nil  {
233+ 		headerSize  =  binary .Size (* f .BigObjHeader )
234+ 	} else  {
235+ 		headerSize  =  binary .Size (* f .FileHeader )
236+ 	}
237+ 
238+ 	// Validate machine type 
239+ 	switch  f .GetMachine () {
97240	case  IMAGE_FILE_MACHINE_AMD64 ,
98241		IMAGE_FILE_MACHINE_ARM64 ,
99242		IMAGE_FILE_MACHINE_ARMNT ,
@@ -104,19 +247,17 @@ func NewFile(r io.ReaderAt) (*File, error) {
104247		IMAGE_FILE_MACHINE_UNKNOWN :
105248		// ok 
106249	default :
107- 		return  nil , fmt .Errorf ("unrecognized PE machine: %#x" , f .FileHeader . Machine )
250+ 		return  nil , fmt .Errorf ("unrecognized PE machine: %#x" , f .GetMachine () )
108251	}
109252
110- 	var  err  error 
111- 
112253	// Read string table. 
113- 	f .StringTable , err  =  readStringTable ( & f . FileHeader , sr )
254+ 	f .StringTable , err  =  readStringTableFromFile ( f , sr )
114255	if  err  !=  nil  {
115256		return  nil , err 
116257	}
117258
118259	// Read symbol table. 
119- 	f .COFFSymbols , err  =  readCOFFSymbols (& f . FileHeader , sr )
260+ 	f .COFFSymbols , err  =  readCOFFSymbols (f , sr )
120261	if  err  !=  nil  {
121262		return  nil , err 
122263	}
@@ -126,20 +267,23 @@ func NewFile(r io.ReaderAt) (*File, error) {
126267	}
127268
128269	// Seek past file header. 
129- 	_ , err  =  sr .Seek (base + int64 (binary . Size ( f . FileHeader ) ), io .SeekStart )
270+ 	_ , err  =  sr .Seek (base + int64 (headerSize ), io .SeekStart )
130271	if  err  !=  nil  {
131272		return  nil , err 
132273	}
133274
134- 	// Read optional header. 
135- 	f .OptionalHeader , err  =  readOptionalHeader (sr , f .FileHeader .SizeOfOptionalHeader )
136- 	if  err  !=  nil  {
137- 		return  nil , err 
275+ 	// Read optional header (only for regular COFF files). 
276+ 	if  ! f .IsBigObj () {
277+ 		f .OptionalHeader , err  =  readOptionalHeader (sr , f .GetSizeOfOptionalHeader ())
278+ 		if  err  !=  nil  {
279+ 			return  nil , err 
280+ 		}
138281	}
139282
140283	// Process sections. 
141- 	f .Sections  =  make ([]* Section , f .FileHeader .NumberOfSections )
142- 	for  i  :=  0 ; i  <  int (f .FileHeader .NumberOfSections ); i ++  {
284+ 	numSections  :=  f .GetNumberOfSections ()
285+ 	f .Sections  =  make ([]* Section , numSections )
286+ 	for  i  :=  uint32 (0 ); i  <  numSections ; i ++  {
143287		sh  :=  new (SectionHeader32 )
144288		if  err  :=  binary .Read (sr , binary .LittleEndian , sh ); err  !=  nil  {
145289			return  nil , err 
0 commit comments