@@ -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