如果PCH文件找到了,需确保它是有效的。
1257 static bool
1258 validate_pch (cpp_reader *pfile, _cpp_file *file, const char *pchname) in cppfiles.c
1259 {
1260 const char *saved_path = file->path;
1261 bool valid = false;
1262
1263 file->path = pchname;
1264 if (open_file (file))
1265 {
1266 valid = 1 & pfile->cb.valid_pch (pfile, pchname, file->fd);
1267
1268 if (!valid)
1269 {
1270 close (file->fd);
1271 file->fd = -1;
1272 }
1273
1274 if (CPP_OPTION (pfile, print_include_names))
1275 {
1276 unsigned int i;
1277 for (i = 1; i < pfile->line_maps.depth; i++)
1278 putc ('.', stderr);
1279 fprintf (stderr, "%c %s/n",
1280 valid ? '!' : 'x', pchname);
1281 }
1282 }
1283
1284 file->path = saved_path;
1285 return valid;
1286 }
预编译头文件的格式尚未统一,GCC以后可能会引入其他预编译头文件的格式,因此cpp_reader提供钩子cb绑定指定的预编译头文件的各种操作函数。对于C++/C,init_c_lex完成了这个绑定。钩子cb的定义如下,只有最后2个域是用于预编译头文件的,其他域则也为其他相应的操作提供了灵活的架构。
379 struct cpp_callbacks in cpplib.h
380 {
381 /* Called when a new line of preprocessed output is started. */
382 void (*line_change) (cpp_reader *, const cpp_token *, int);
383
384 /* Called when switching to/from a new file.
385 The line_map is for the new file. It is NULL if there is no new file.
386 (In C this happens when done with <built-in>+<command line> and also
387 when done with a main file.) This can be used for resource cleanup. */
388 void (*file_change) (cpp_reader *, const struct line_map *);
389
390 void (*dir_change) (cpp_reader *, const char *);
391 void (*include) (cpp_reader *, unsigned int, const unsigned char *,
392 const char *, int);
393 void (*define) (cpp_reader *, unsigned int, cpp_hashnode *);
394 void (*undef) (cpp_reader *, unsigned int, cpp_hashnode *);
395 void (*ident) (cpp_reader *, unsigned int, const cpp_string *);
396 void (*def_pragma) (cpp_reader *, unsigned int);
397 int (*valid_pch) (cpp_reader *, const char *, int);
398 void (*read_pch) (cpp_reader *, const char *, int, const char *);
399 };
当前的valid_pch,对于C/C++是c_common_valid_pch。对于PCH文件,文件的最开头包含8字节的识别码,后跟一个如下定义的c_pch_validity结构。
53 struct c_pch_validity in c-pch.c
54 {
55 unsigned char host_machine_length;
56 unsigned char target_machine_length;
57 unsigned char version_length;
58 unsigned char debug_info_type;
59 unsigned int flags_info;
60 void (*pch_init) (void);
61 size_t target_data_length;
62 };
在c_common_valid_pch中,很容易就知道通常一个PCH文件,在开头有如下内容:
图 9:PCH文件的布局
域debug_info_type表明了要产生的调试信息的类型,而flags_info被设置为1,如果在生成PCH文件时使用了选项-funit-at-a-time。
219 int
220 c_common_valid_pch (cpp_reader *pfile, const char *name, int fd) in c-pch.c
221 {
222 int sizeread;
223 int result;
224 char ident[IDENT_LENGTH];
225 char short_strings[256 * 3];
226 int strings_length;
227 const char *pch_ident;
228 struct c_pch_validity v;
229 unsigned int current_flags_info = 0;
230
231 if (flag_unit_at_a_time)
232 current_flags_info |= FLAG_UNIT_AT_A_TIME_SET;
233
234 /* Perform a quick test of whether this is a valid
235 precompiled header for the current language
236 and with the current flag settings. */
237
238 sizeread = read (fd, ident, IDENT_LENGTH);
239 if (sizeread == -1)
240 fatal_error ("can't read %s: %m", name);
241 else if (sizeread != IDENT_LENGTH)
242 return 2;
243
244 pch_ident = get_ident();
245 if (memcmp (ident, pch_ident, IDENT_LENGTH) != 0)
246 {
247 if (cpp_get_options (pfile)->warn_invalid_pch)
248 {
249 if (memcmp (ident, pch_ident, 5) == 0)
250 /* It's a PCH, for the right language, but has the wrong version.
251 */
252 cpp_error (pfile, CPP_DL_WARNING,
253 "%s: not compatible with this GCC version", name);
254 else if (memcmp (ident, pch_ident, 4) == 0)
255 /* It's a PCH for the wrong language. */
256 cpp_error (pfile, CPP_DL_WARNING, "%s: not for %s", name,
257 lang_hooks.name);
258 else
259 /* Not any kind of PCH. */
260 cpp_error (pfile, CPP_DL_WARNING, "%s: not a PCH file", name);
261 }
262 return 2;
263 }
264
265 /* At this point, we know it's a PCH file, so it ought to be long enough
266 that we can read a c_pch_validity structure. */
267 if (read (fd, &v, sizeof (v)) != sizeof (v))
268 fatal_error ("can't read %s: %m", name);
269
270 strings_length = (v.host_machine_length + v.target_machine_length
271 + v.version_length);
272 if (read (fd, short_strings, strings_length) != strings_length)
273 fatal_error ("can't read %s: %m", name);
274 if (v.host_machine_length != strlen (host_machine)
275 || memcmp (host_machine, short_strings, strlen (host_machine)) != 0)
276 {
277 if (cpp_get_options (pfile)->warn_invalid_pch)
278 cpp_error (pfile, CPP_DL_WARNING,
279 "%s: created on host `%.*s', but used on host `%s'", name,
280 v.host_machine_length, short_strings, host_machine);
281 return 2;
282 }
283 if (v.target_machine_length != strlen (target_machine)
284 || memcmp (target_machine, short_strings + v.host_machine_length,
285 strlen (target_machine)) != 0)
286 {
287 if (cpp_get_options (pfile)->warn_invalid_pch)
288 cpp_error (pfile, CPP_DL_WARNING,
289 "%s: created for target `%.*s', but used for target `%s'",
290 name, v.target_machine_length,
291 short_strings + v.host_machine_length, target_machine);
292 return 2;
293 }
294 if (v.version_length != strlen (version_string)
295 || memcmp (version_string,
296 (short_strings + v.host_machine_length
297 + v.target_machine_length),
298 v.version_length) != 0)
299 {
300 if (cpp_get_options (pfile)->warn_invalid_pch)
301 cpp_error (pfile, CPP_DL_WARNING,
302 "%s: created by version `%.*s', but this is version `%s'",
303 name, v.version_length,
304 (short_strings + v.host_machine_length
305 + v.target_machine_length),
306 version_string);
307 return 2;
308 }
309 if (v.flags_info != current_flags_info)
310 {
311 if (cpp_get_options (pfile)->warn_invalid_pch)
312 cpp_error (pfile, CPP_DL_WARNING,
313 "%s: created using different flags",
314 name);
315 return 2;
316 }
317
318 /* The allowable debug info combinations are that either the PCH file
319 was built with the same as is being used now, or the PCH file was
320 built for some kind of debug info but now none is in use. */
321 if (v.debug_info_type != write_symbols
322 && write_symbols != NO_DEBUG)
323 {
324 if (cpp_get_options (pfile)->warn_invalid_pch)
325 cpp_error (pfile, CPP_DL_WARNING,
326 "%s: created with -g%s, but used with -g%s", name,
327 debug_type_names[v.debug_info_type],
328 debug_type_names[write_symbols]);
329 return 2;
330 }
331
332 /* If the text segment was not loaded at the same address as it was
333 when the PCH file was created, function pointers loaded from the
334 PCH will not be valid. We could in theory remap all the function
335 pointers, but no support for that exists at present. */
336 if (v.pch_init != &pch_init)
337 {
338 if (cpp_get_options (pfile)->warn_invalid_pch)
339 cpp_error (pfile, CPP_DL_WARNING,
340 "%s: had text segment at different address", name);
341 return 2;
342 }
343
344 /* Check the target-specific validity data. */
345 {
346 void *this_file_data = xmalloc (v.target_data_length);
347 const char *msg;
348
349 if ((size_t) read (fd, this_file_data, v.target_data_length)
350 != v.target_data_length)
351 fatal_error ("can't read %s: %m", name);
352 msg = targetm.pch_valid_p (this_file_data, v.target_data_length);
353 free (this_file_data);
354 if (msg != NULL)
355 {
356 if (cpp_get_options (pfile)->warn_invalid_pch)
357 cpp_error (pfile, CPP_DL_WARNING, "%s: %s", name, msg);
358 return 2;
359 }
360 }
361
362 /* Check the preprocessor macros are the same as when the PCH was
363 generated. */
364
365 result = cpp_valid_state (pfile, name, fd);
366 if (result == -1)
367 return 2;
368 else
369 return result == 0;
370 }
如果target_data之前的域都是有效的,紧接着这个数据的验证是依赖于目标平台的。然而对于GCC目前所支持的大部平台,默认的方法就可以了。
4112 const char *
4113 default_pch_valid_p (const void *data_p, size_t len) in toplev.c
4114 {
4115 const char *data = (const char *)data_p;
4116 const char *flag_that_differs = NULL;
4117 size_t i;
4118
4119 /* -fpic and -fpie also usually make a PCH invalid. */
4120 if (data[0] != flag_pic)
4121 return _("created and used with different settings of -fpic");
4122 if (data[1] != flag_pie)
4123 return _("created and used with different settings of -fpie");
4124 data += 2;
4125
4126 /* Check target_flags. */
4127 if (memcmp (data, &target_flags, sizeof (target_flags)) != 0)
4128 {
4129 for (i = 0; i < ARRAY_SIZE (target_switches); i++)
4130 {
4131 int bits;
4132 int tf;
4133
4134 memcpy (&tf, data, sizeof (target_flags));
4135
4136 bits = target_switches[i].value;
4137 if (bits < 0)
4138 bits = -bits;
4139 if ((target_flags & bits) != (tf & bits))
4140 {
4141 flag_that_differs = target_switches[i].name;
4142 goto make_message;
4143 }
4144 }
4145 abort ();
4146 }
4147 data += sizeof (target_flags);
4148 len -= sizeof (target_flags);
4149
4150 /* Check string options. */
4151 #ifdef TARGET_OPTIONS
4152 for (i = 0; i < ARRAY_SIZE (target_options); i++)
4153 {
4154 const char *str = *target_options[i].variable;
4155 size_t l;
4156 if (! str)
4157 str = "";
4158 l = strlen (str) + 1;
4159 if (len < l || memcmp (data, str, l) != 0)
4160 {
4161 flag_that_differs = target_options[i].prefix;
4162 goto make_message;
4163 }
4164 data += l;
4165 len -= l;
4166 }
4167 #endif
4168
4169 return NULL;
4170
4171 make_message:
4172 {
4173 char *r;
4174 asprintf (&r, _("created and used with differing settings of `-m%s'"),
4175 flag_that_differs);
4176 if (r == NULL)
4177 return _("out of memory");
4178 return r;
4179 }
4180 }
在函数中,可以看到target_data包含了以下信息。在初始化与目标平台相关选项一节中,我们已经看过了target_flags及target_options的含义和设置。作为有效的PCH文件,其target_data必须与当前的所使用的相应的数据匹配。注意到在target_options部分,所有的目标选项都会出现,那些没有在编译中使用的选项,其内容将是“/0”。
图 10:PCH文件中target data的布局
如果PCH文件是有效的,它即是我们所希冀的,因此在find_file_in_dir的326行即可返回。否则就要尝试打开普通文件。
_cpp_find_file (continue)
418 if (entry)
419 {
420 /* Cache for START_DIR too, sharing the _cpp_file structure. */
421 free ((char *) file->name);
422 free (file);
423 file = entry->u.file;
424 }
425 else
426 {
427 /* This is a new file; put it in the list. */
428 file->next_file = pfile->all_files;
429 pfile->all_files = file;
430 }
431
432 /* Store this new result in the hash table. */
433 entry = new_file_hash_entry (pfile);
434 entry->next = *hash_slot;
435 entry->start_dir = start_dir;
436 entry->u.file = file;
437 *hash_slot = entry;
438
439 return file;
440 }
如果418行的entry不为NULL,即找到缓存的文件,但此文件不是在start_dir所指定的目录下发现的。为了使今后在start_dir下查找该文件,不至于重复上面的操作,同样需要创建一个entry把start_dir和该文件绑定。