@@ -29,7 +29,11 @@ import (
29
29
30
30
// A File represents an open PE file.
31
31
type 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
+
33
37
OptionalHeader any // of type *OptionalHeader32 or *OptionalHeader64
34
38
Sections []* Section
35
39
Symbols []* Symbol // COFF symbols with auxiliary symbol records removed
@@ -39,6 +43,69 @@ type File struct {
39
43
closer io.Closer
40
44
}
41
45
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
+
42
109
// Open opens the named file using [os.Open] and prepares it for use as a PE binary.
43
110
func Open (name string ) (* File , error ) {
44
111
f , err := os .Open (name )
@@ -68,6 +135,69 @@ func (f *File) Close() error {
68
135
69
136
// TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance)
70
137
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
+
71
201
// NewFile creates a new [File] for accessing a PE binary in an underlying reader.
72
202
func NewFile (r io.ReaderAt ) (* File , error ) {
73
203
f := new (File )
@@ -89,11 +219,24 @@ func NewFile(r io.ReaderAt) (*File, error) {
89
219
} else {
90
220
base = int64 (0 )
91
221
}
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 {
94
225
return nil , err
95
226
}
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 () {
97
240
case IMAGE_FILE_MACHINE_AMD64 ,
98
241
IMAGE_FILE_MACHINE_ARM64 ,
99
242
IMAGE_FILE_MACHINE_ARMNT ,
@@ -104,19 +247,17 @@ func NewFile(r io.ReaderAt) (*File, error) {
104
247
IMAGE_FILE_MACHINE_UNKNOWN :
105
248
// ok
106
249
default :
107
- return nil , fmt .Errorf ("unrecognized PE machine: %#x" , f .FileHeader . Machine )
250
+ return nil , fmt .Errorf ("unrecognized PE machine: %#x" , f .GetMachine () )
108
251
}
109
252
110
- var err error
111
-
112
253
// Read string table.
113
- f .StringTable , err = readStringTable ( & f . FileHeader , sr )
254
+ f .StringTable , err = readStringTableFromFile ( f , sr )
114
255
if err != nil {
115
256
return nil , err
116
257
}
117
258
118
259
// Read symbol table.
119
- f .COFFSymbols , err = readCOFFSymbols (& f . FileHeader , sr )
260
+ f .COFFSymbols , err = readCOFFSymbols (f , sr )
120
261
if err != nil {
121
262
return nil , err
122
263
}
@@ -126,20 +267,23 @@ func NewFile(r io.ReaderAt) (*File, error) {
126
267
}
127
268
128
269
// 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 )
130
271
if err != nil {
131
272
return nil , err
132
273
}
133
274
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
+ }
138
281
}
139
282
140
283
// 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 ++ {
143
287
sh := new (SectionHeader32 )
144
288
if err := binary .Read (sr , binary .LittleEndian , sh ); err != nil {
145
289
return nil , err
0 commit comments