This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
[rfc] Query Red Boot's i386 CPU id
- From: Andrew Cagney <ac131313 at ges dot redhat dot com>
- To: gdb-patches at sources dot redhat dot com
- Date: Wed, 28 Aug 2002 17:31:46 -0400
- Subject: [rfc] Query Red Boot's i386 CPU id
Hello,
Red Boot (Red Hat's boot monitor) includes a remote protocol extension
that allows the i386 processor's CPU id to be queried.
The attached, written by Fernando Nasser, makes two things possible:
- auto display of the cpu on a connect vis:
(gdb) target remote /dev/ttyS0
Remote debugging using /dev/ttyS0
Genuine Intel Pentium(R) III processor [Coppermine]
Processor Family: 6 Model: 8 Stepping: 0
- a command to query the cpuid (info cpu).
The actual CPU information is determined using a small file database
(installed in ..../share/gdb/i386-cpuid).
Comments on how this was implemented, and what, if anything, could be
integrated into GDB, welcome.
Andrew
2002-08-28 Andrew Cagney <cagney@redhat.com>
From Fernando Nasser:
* Makefile.in (ALLDEPFILES): Add i386-cpuid.c.
(i386-cpuid.o): Specify dependencies.
(install-cpuid): New target.
(uninstall-cpuid): New target.
(cpuid): New target.
(CPUID): Define.
(GDB_CFLAGS): Add SHAREDIR.
* config/i386/embed.mt (TDEPFILES): Add i386-cpuid.o.
* remote.c (remote_start_remote): Call target_post_open_hook.
* utils.c (target_post_open_hook): Define.
* target.h (target_post_open_hook): Declare.
* cpuid-i386: New file.
* i386-cpuid.c: New file.
* configure.tgt (CONFIG_ALL): Add cpuid.
(CONFIG_INSTALL): Add install-cpuid.
(CONFIG_UNINSTALL): Add uninstall-cpuid.
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.252
diff -u -r1.252 Makefile.in
--- Makefile.in 28 Aug 2002 14:02:18 -0000 1.252
+++ Makefile.in 28 Aug 2002 21:22:13 -0000
@@ -319,7 +319,7 @@
# your system doesn't have fcntl.h in /usr/include (which is where it
# should be according to Posix).
DEFS = @DEFS@
-GDB_CFLAGS = -I. -I$(srcdir) -I$(srcdir)/config -DLOCALEDIR="\"$(prefix)/share/locale\"" $(DEFS)
+GDB_CFLAGS = -I. -I$(srcdir) -I$(srcdir)/config -DLOCALEDIR="\"$(prefix)/share/locale\"" -DSHAREDIR="\"$(prefix)/share\"" $(DEFS)
# M{H,T}_CFLAGS, if defined, have host- and target-dependent CFLAGS
# from the config directory.
@@ -939,6 +939,31 @@
$(INSTALL_DATA) $$i $(GDBTK_LIBRARY)/$$i ; \
done ;
+
+# Build/install any CPU IDs.
+
+CPUID=cpuid-i386
+install-cpuid: force
+ $(SHELL) $(srcdir)/../mkinstalldirs $(datadir)/gdb ; \
+ for i in $(CPUID) ; \
+ do \
+ $(INSTALL_DATA) $$i $(datadir)/gdb/$$i ; \
+ done ;
+uninstall-cpuid: force
+ for c in $(CPUID) ; \
+ do \
+ rm -f $(datadir)/gdb/$$c ; \
+ done ;
+cpuid: force
+ for i in $(CPUID) ; do \
+ if test ! -f ./$$i ; then \
+ (test "$$LN_S" = "ln -s" && ln -s $(srcdir)/$$i .) || \
+ ln $(srcdir)/$$i . || \
+ cp $(srcdir)/$$i ; \
+ fi ; \
+ done
+
+
# We do this by grepping through sources. If that turns out to be too slow,
# maybe we could just require every .o file to have an initialization routine
# of a given name (top.o -> _initialize_top, etc.).
@@ -1356,6 +1381,7 @@
dcache.c delta68-nat.c dpx2-nat.c exec.c fork-child.c \
go32-nat.c h8300-tdep.c h8500-tdep.c \
hp300ux-nat.c hppa-tdep.c hppab-nat.c hppah-nat.c hpread.c \
+ i386-cpuid.c \
i386-tdep.c i386b-nat.c i386v-nat.c i386-linux-nat.c \
i386v4-nat.c i386ly-tdep.c \
i386bsd-nat.c i386bsd-tdep.c i386fbsd-nat.c \
@@ -1725,6 +1751,8 @@
$(gdb_string_h)
hpux-thread.o: hpux-thread.c $(defs_h) $(gdbthread_h) $(target_h) \
$(inferior_h) $(regcache_h) $(gdbcore_h)
+i386-cpuid.o: i386-cpuid.c $(defs_h) $(target_h) $(command_h) $(gdbcmd_h) \
+ $(gdb_string_h)
i386-linux-nat.o: i386-linux-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) \
$(regcache_h) $(gdb_assert_h) $(gdb_string_h) $(gregset_h) \
$(i387_tdep_h) $(i386_tdep_h) $(i386_linux_tdep_h)
Index: configure.tgt
===================================================================
RCS file: /cvs/src/src/gdb/configure.tgt,v
retrieving revision 1.84
diff -u -r1.84 configure.tgt
--- configure.tgt 22 Aug 2002 21:52:44 -0000 1.84
+++ configure.tgt 28 Aug 2002 21:22:13 -0000
@@ -118,7 +118,11 @@
# OBSOLETE i[3456]86-*-osf1mk*) gdb_target=i386mk ;;
i[3456]86-*-cygwin*) gdb_target=cygwin ;;
i[3456]86-*-vxworks*) gdb_target=vxworks ;;
-i[3456]86-*-*) gdb_target=embed ;;
+i[3456]86-*-*) gdb_target=embed
+ CONFIG_ALL="$(CONFIG_ALL) cpuid"
+ CONFIG_INSTALL="${CONFIG_INSTALL} install-cpuid"
+ CONFIG_UNINSTALL="${CONFIG_UNINSTALL} install-cpuid"
+ ;;
# OBSOLETE i960-*-bout*) gdb_target=vxworks960 ;;
# OBSOLETE i960-nindy-coff*) gdb_target=nindy960 ;;
Index: cpuid-i386
===================================================================
RCS file: cpuid-i386
diff -N cpuid-i386
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ cpuid-i386 28 Aug 2002 21:22:13 -0000
@@ -0,0 +1,80 @@
+# Intel i386 CPUID database, for GDB, the GNU Debugger.
+#
+# Copyright 2002 Free Software Foundation, Inc.
+#
+# Contributed by Red Hat, Inc.
+#
+# This file is part of GDB.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+# This file describes the known CPUID codes for Intel processors
+#
+# The format is as follows:
+#
+# TFMScXXXXXXXXXX....XXXXXXXXX
+# where
+# T = Type
+# F = Family
+# M = Model
+# S = Stepping (a 'x' matches any value)
+# c = Cache size code if model 5 or 7 (for ambiguous FMS combinations)
+# Brand Id if model 8 or higher (for ambiguous FMS combinations)
+# XXXXXXX....XXXXXXXX = String describing the processor
+#
+# Lines cannot be longer than 80 bytes.
+# The first match wins, so place entries with a specific stepping before
+# the entry with a generic specifier ('x').
+# "Genuine Intel" will be added automatically if that is verified.
+# As you may have guessed, lines stating with a '#' are comments.
+#
+# Note: make install copies this file to $(install_dir)/share/gdb/cpuid-i386
+#
+040x 486(TM) DX processor
+041x 486(TM) DX processor
+042x 486(TM) SX processor
+043x DX2(TM) processor
+044x 486(TM) processor
+045x SX2(TM) processor
+047x Write-Back Enhanced DX2(TM) processor
+048x DX4(TM) processor
+148x DX4(TM) OverDrive processor
+051x Pentium(R) processor
+151x Pentium(R) OverDrive processor for Pentium processor
+052x Pentium(R) processor
+152x Pentium(R) OverDrive processor for Pentium processor
+053x Pentium(R) OverDrive processor for Intel 486 processor-based systems
+054x Pentium(R) processor with MMX technology
+154x Pentium(R) OverDrive processor with MMX technology for Pentium processor
+061x Pentium(R) Pro processor
+063x Pentium(R) II processor, model 3
+163x Pentium(R) II OverDrive processor
+065x0Celeron(TM) processor, model 5
+065x3Pentium(R) II processor, model 5 or Pentium(R) II Xeon(TM) processor
+065x4Pentium(R) II Xeon(TM) processor
+065x5Pentium(R) II Xeon(TM) processor
+066A Pentium(R) II processor [Dixon]
+066x Celeron(TM) processor, model 6
+067x3Pentium(R) III processor or Intel Pentium(R) III Xeon processor
+067x4Pentium(R) III Xeon(TM) processor
+067x5Pentium(R) III Xeon(TM) processor
+06800Pentium(R) III processor [Coppermine]
+068x1Celeron(TM) processor
+066A Celeron(TM) processor
+063x Pentium(R) II processor
+06Ax Pentium(R) III processor
+068x2Pentium(R) III processor
+068x3Pentium(R) III Xeon(TM) processor
Index: i386-cpuid.c
===================================================================
RCS file: i386-cpuid.c
diff -N i386-cpuid.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ i386-cpuid.c 28 Aug 2002 21:22:14 -0000
@@ -0,0 +1,340 @@
+/* Intel 386 CPUID command for GDB, the GNU Debugger.
+
+ Copyright 2002 Free Software Foundation, Inc.
+
+ Contributed by Red Hat, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "defs.h"
+#include "target.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "gdb_string.h"
+
+/* NOTE: cagney/2001-11-17:
+
+ This module uses the [remote protocol] target_query ("qCPUID")
+ method to extract the i386 CPU information. It then takes that
+ information and uses it to do a lookup on a local file
+ ``cpuid-i386''.
+
+ Thanks to a botched initial thread query mechanism, using anything
+ starting with ``qC'' is dangerous. Since qCPUID does this, it
+ won't interact well with targets that recognize the exiting ``qC''
+ query. Sigh.
+
+ One possible alternative to this qCPUID mechanis would be to plant
+ a CPUID instruction in the target memory, execute it, and then
+ examine the result. This probably isn't pratical since it depends
+ in being able to execute code in the target.
+
+ This file adds the command ``info cpu''. Beware of the existing
+ commands ``{set,show} {processor,architecture}''.
+
+ This file uses a target_post_open_hook() mechanism to hook into the
+ target open (and report the CPUID on an initial connect). Per the
+ comment in the file "target.h", what really should be happening is
+ a core_gdb_open() function should be calling the hook. */
+
+#include <ctype.h>
+
+#ifndef GDBCPUID_FILENAME
+#define GDBCPUID_FILENAME "cpuid-i386"
+#endif
+
+/* Helper for CPUID handler. */
+
+static int
+toint (char c)
+{
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ /* c is not an hex digit */
+ return 0;
+}
+
+/* Get CPUID from the target, if available.
+ The returned null terminated string is of the form:
+ ITFMSC
+ where
+ I = Genuine modifier
+ "I" if this is a Genuine Intel CPU, "N" otherwise;
+ T = Processor Type;
+ F = Processor Family;
+ M = Processor Model;
+ S = Processor Stepping;
+ C = Cache modifier
+ The cache descriptor value if between 0x40 and 0x49, a space otherwise.
+ This modifier is only non-blank for some ambiguous cases where the
+ Family+Model is not enough to distinguish the processors.
+*/
+
+static char *
+i386_get_cpuid (void)
+{
+ static char genuine[] = "756e6547,6c65746e,49656e69";
+ static char Genuine[] = "756E6547,6C65746E,49656E69";
+ int size = 0;
+ char *buf;
+ char *rets;
+
+ /* Is this even supported? */
+ if (current_target.to_query == NULL)
+ return NULL;
+
+ /* Get the current buffer size. Ignore the error return status as
+ this call always fails. */
+ size = 0;
+ target_query ('C', "PUID", NULL, &size);
+
+ buf = xmalloc (size);
+ /* Now query the CPUID info. */
+ if (target_query ('C', "PUID", buf, &size) < 0)
+ return NULL;
+
+ /* Check if we got enough data and do some ad hoc consistence checking. */
+ size = strlen(buf);
+ if ((size < 44)
+ || (buf[35] != ';')
+ || (buf[8] != ','))
+ /* No valid CPUID information available. */
+ return NULL;
+
+ /* Get enough space for the returned string. */
+ rets = xmalloc (7 * sizeof(*rets));
+ if (rets == NULL)
+ internal_error (__FILE__, __LINE__,
+ "Gdb cannot allocate memory. Memory exhausted?");
+
+ /* Copy the basic information, leaving space for the modifiers. */
+ rets[0] = 'N'; /* It is not Intel until proved genuine. */
+ rets[1] = buf[40] & 0xf3; /* Processor Type (just 2 bits). */
+ rets[2] = /* Processor Family. */
+ islower (buf[41]) ? toupper (buf[41]) : buf[41];
+ rets[3] = /* Processor Model. */
+ islower (buf[42]) ? toupper (buf[42]) : buf[42];
+ rets[4] = /* Processor Stepping. */
+ islower (buf[43]) ? toupper (buf[43]) : buf[43];
+ rets[5] = ' '; /* No Cache modifier unless needed. */
+ rets[6] = '\0'; /* String terminator. */
+
+ /* Check if this is a Genuine Intel processor. */
+ buf[35] = '\0';
+ if ((strcmp (Genuine, &buf[9]) == 0)
+ || (strcmp (genuine, &buf[9]) == 0))
+ /* It is a Genuine Intel processor. */
+ rets[0] = 'I';
+
+ /* If one of the ambiguous cases, check for cache size,
+ but check if we have enough information first. */
+ if ((size >= 107)
+ && ((buf[41] == '6') && ((buf[42] == '5') || (buf[42] == '7'))))
+ {
+ int i;
+ for (i = 72; i <= 107; i = i + 2)
+ if ((buf[i] == '4')
+ && (buf[i + 1] >= '0')
+ && (buf[i + 1] <= '9'))
+ {
+ rets[5] = buf[i + 1];
+ break;
+ }
+ }
+
+ /* CPUs from model 8 onwards have a Brand Id field */
+ if ((size >= 53)
+ && ((toint(buf[41]) >= 6) && (toint(buf[42]) >= 8)))
+ {
+ rets[5] = toint(buf[52]) | 0x30;
+ }
+
+ free (buf);
+ return rets;
+}
+
+/* Check our database for description of cpu with this CPUID. */
+
+char *
+i386_get_cpuid_string (char * cpuid)
+{
+ FILE *cpufp;
+ char *cpuidtxt;
+ char *retstr;
+ /* These paths are for when gdb is run from the build tree (like in
+ "make check"). It can be invoked from different levels. */
+ char *build_paths[] = { "/",
+ "/../",
+ "/gdb/",
+ NULL
+ };
+ int idind;
+ char filename[] = GDBCPUID_FILENAME;
+ char str[82];
+ char cpuidx[5];
+ char *xfilename;
+ char *gdb_exec_prefix = getenv ("GDB_EXEC_PREFIX");
+
+ cpufp = NULL;
+
+ /* Look in the standard directories. */
+ xasprintf (&xfilename, "%s/gdb/%s", SHAREDIR, GDBCPUID_FILENAME);
+ cpufp = fopen (xfilename, "r");
+ free (xfilename);
+
+ /* If not found, look in the build tree. */
+ idind = 0;
+ while ((cpufp == NULL) && (build_paths[idind] != NULL))
+ {
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+ xfilename = malloc (PATH_MAX +
+ strlen (build_paths[idind]) +
+ sizeof (GDBCPUID_FILENAME) + 1);
+ if (getcwd (xfilename, PATH_MAX) != NULL)
+ {
+ strcat (xfilename, build_paths[idind]);
+ strcat (xfilename, filename);
+ cpufp = fopen (xfilename, "r");
+ free (xfilename);
+ }
+ idind++;
+ }
+
+ if (cpufp == NULL)
+ {
+ warning ("Cannot find/open %s file.", filename);
+ return NULL;
+ }
+
+ /* Create a pattern to match the any stepping rule. */
+ cpuidx[0] = cpuid[1];
+ cpuidx[1] = cpuid[2];
+ cpuidx[2] = cpuid[3];
+ cpuidx[3] = 'x'; /* 'x' denotes any stepping matches entry. */
+ cpuidx[4] = cpuid[5];
+
+ /* Look in the file for an entry with this CPUID hex string
+ or with the catch all steppings one. */
+ do
+ {
+ cpuidtxt = fgets (str, 81, cpufp);
+ if (ferror (cpufp))
+ {
+ warning ("Error reading %s file.\n", filename);
+ break;
+ }
+ if (cpuidtxt == NULL)
+ break;
+ /* skip comment lines.
+ Not really necessary, but avoid unneeded comparison */
+ if (*cpuidtxt == '#')
+ continue;
+ }
+ while (strncmp (cpuidtxt, (cpuid + 1), 5)
+ && strncmp (cpuidtxt, cpuidx, 5));
+
+ fclose (cpufp);
+
+ /* A valid entry has 5 cpuid hex digits and the description.
+ Note that fgets() includes the new-line in the string. */
+ if ((cpuidtxt != NULL) && (strlen (cpuidtxt) > 7))
+ /* Skip the id and point to the description text. */
+ cpuidtxt += 5;
+ else
+ /* Empty description texts are not accepted. */
+ return NULL;
+
+ /* Check for Genuine Intel and add that to the string if it is the case. */
+ if (*cpuid == 'I')
+ {
+ retstr = xmalloc (strlen (cpuidtxt) + 15);
+ strncpy (retstr, "Genuine Intel ", 14);
+ strcpy ((retstr + 14), cpuidtxt);
+ }
+ else
+ retstr = xstrdup (cpuidtxt);
+
+ return retstr;
+}
+
+/* Prints CPUID information (info cpu). */
+
+/* ARGSUSED */
+static void
+i386_info_cpu_cmd (char *args, int from_tty)
+{
+ char *id;
+ char *msg;
+
+ id = i386_get_cpuid ();
+ if (id == NULL)
+ {
+ printf_filtered ("CPU information not available.\n");
+ return;
+ }
+
+ msg = i386_get_cpuid_string (id);
+ if (msg == NULL)
+ {
+ printf_filtered ("Unidentified CPU (CPUID=%s)\n", id);
+ printf_filtered ("Processor Family: %c Model: %c Stepping: %c\n",
+ id[2], id[3], id[4]);
+ free (id);
+ return;
+ }
+ else
+ {
+ printf_filtered ("%s", msg);
+ printf_filtered ("Processor Family: %c Model: %c Stepping: %c\n",
+ id[2], id[3], id[4]);
+ }
+
+ free (msg);
+ free (id);
+}
+
+/* Done after remote connects to a target. */
+
+static void (*i386_cpuid_post_open_chain) (void);
+
+static void
+i386_cpuid_post_open (void)
+{
+ /* Print CPUID information. */
+ i386_info_cpu_cmd (NULL, 1);
+ /* Call the next thing on the list. */
+ if (i386_cpuid_post_open_chain != NULL)
+ i386_cpuid_post_open_chain ();
+}
+
+extern initialize_file_ftype _initialize_i386_cpuid;
+void
+_initialize_i386_cpuid ()
+{
+ /* Insert this modules post open into the post_open chain. */
+ i386_cpuid_post_open_chain = target_post_open_hook;
+ target_post_open_hook = i386_cpuid_post_open;
+
+ /* Add "info cpu" command. */
+ add_cmd ("cpu", class_info, i386_info_cpu_cmd,
+ "Show information about the target cpu",
+ &infolist);
+}
Index: remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.93
diff -u -r1.93 remote.c
--- remote.c 18 Aug 2002 23:17:57 -0000 1.93
+++ remote.c 28 Aug 2002 21:22:16 -0000
@@ -2141,6 +2141,11 @@
get_offsets (); /* Get text, data & bss offsets */
+ /* Give any interested spectators a chance to do something based on
+ the opening of a new target connection. */
+ if (target_post_open_hook != NULL)
+ target_post_open_hook ();
+
putpkt ("?"); /* initiate a query from remote machine */
immediate_quit--;
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.26
diff -u -r1.26 target.h
--- target.h 26 Aug 2002 19:18:33 -0000 1.26
+++ target.h 28 Aug 2002 21:22:16 -0000
@@ -1002,6 +1002,22 @@
extern void (*target_new_objfile_hook) (struct objfile *);
+/* target_post_open_hook: This hack makes it possible for modules to
+ do extra work at the end of a target_open(). Functions using this
+ should chain their calls, see target_new_objfile_hook.
+
+ FIXME: cagney/2001-11-17: What should happen is: user enters
+ ``(gdb) target blah''; cli calls ``core-gdb target open (blah)'';
+ that core code does generic pre-processing, and then calls
+ ``blah.open()''; if ``blah.open()'' succeeds, the core code calls
+ any post processing which might include this hook. Why hasn't this
+ been done? Well it turns out that ``(gdb) target blah'' is
+ implemented as a call direct to blah.open(). Core GDB doesn't get
+ a look in. Fixing this involves changing the CLI interface so that
+ CLI methods get called with a local context - eg:
+ core_gdb_open(blah). */
+extern void (*target_post_open_hook) (void);
+
#ifndef target_pid_or_tid_to_str
#define target_pid_or_tid_to_str(ID) \
target_pid_to_str (ID)
Index: utils.c
===================================================================
RCS file: /cvs/src/src/gdb/utils.c,v
retrieving revision 1.76
diff -u -r1.76 utils.c
--- utils.c 1 Aug 2002 17:18:33 -0000 1.76
+++ utils.c 28 Aug 2002 21:22:17 -0000
@@ -91,6 +91,9 @@
/* readline defines this. */
#undef savestring
+/* Chain of functions that wan't to know when a remote open occures. */
+void (*target_post_open_hook) (void);
+
void (*error_begin_hook) (void);
/* Holds the last error message issued by gdb */
Index: config/i386/embed.mt
===================================================================
RCS file: /cvs/src/src/gdb/config/i386/embed.mt,v
retrieving revision 1.3
diff -u -r1.3 embed.mt
--- config/i386/embed.mt 18 Nov 2001 21:28:20 -0000 1.3
+++ config/i386/embed.mt 28 Aug 2002 21:22:17 -0000
@@ -1,3 +1,3 @@
# Target: Embedded Intel 386
-TDEPFILES= i386-tdep.o i387-tdep.o
+TDEPFILES= i386-tdep.o i387-tdep.o i386-cpuid.o
TM_FILE= tm-i386.h