[RFC] gdb_realpath causes problems with GVD

Joel Brobecker
2002-03-19T16:12:45Z

[-- Attachment #1: Type: text/plain, Size: 3173 bytes --]

Machine: x86-inux

The problem appears when some of the files used to build the
application are symbolic links and when GDB annotations are activated.
For us, this happens when GVD, our graphical front-end, drives GDB.

First, the context: In Ada, the filename associated to a unit need to
follow a strict convention, and this convention is dictated by the
compiler. For instance, the GNAT convention says that the spec of
package Hello must be located in file hello.ads. But the Apex naming
schemes says that it must be named hello.1.ada. The trouble starts when
somebody wants to develop/navigate using Apex, while building and
debugging using GNAT. In order to solve this naming issue, it has been
decided to keep the Apex filenames, and to create one symbolic link per
file which name follows GNAT naming scheme. So far, so good.

Now, here is an example of this situation, where I replace Ada by C,
which shows the problem we encountered. Supose we have directory
called symlinks, in which we have one C file, called toto.C, and one
symbolic link to this file called toto.c:
<<
void
break_me (void)
{
}

int
main (void)
{
break_me ();
}
>>

We build the executable using the symlink:

% gcc -g -o toto toto.c

Let's debug toto to see what happens:
<<
GNU gdb 2002-03-19-cvs
[...]
This GDB was configured as "i686-pc-linux-gnu"...
(gdb) set annotate 1
(gdb) b break_me
Breakpoint 1 at 0x804846b: file toto.c, line 4.
(gdb) run
Starting program: /bonn.a/brobecke/symlinks/toto

Breakpoint 1, break_me () at toto.c:4
yy/bonn.a/brobecke/symlinks/toto.C:4:23:beg:0x804846b
^^^^^^
>>

As you see, GDB has translated toto.c into toto.C. This translation
causes GDB to think that the inferior stopped in a file named toto.C
(which is not known to GDB, since the compiler used only toto.c). As a
consequence, when the user tries to put breakpoints using the GVD, GVD
issues the following break command

(gdb) break toto.C:4

to which GDB answers

No source file named toto.C

The translation is performed by gdb_realpath. I searched the gdb-patches
archives, and found the reason for this translation in a message from
Tom Tromey. I think I found a way to keep the fix to his problem and
then at the same time fix our issue: instead of canonicalizing the
entire filename, I suggest that we only expand the directory prefix
(ie the part returned by the "dirname" unix command).

Here is a patch which implements this idea. I'd like to get your opinion
on it. Ideally, I'll like to commit this change if it is ok. Here is the
change log:

2002-03-19 Joel Brobecker <brobecker@gnat.com>

* utils.c (gdb_canonicalize_path): New function.
(gdb_realpath_fallback): New function.
(gdb_realpath): Restrict the canonicalization to the directory
prefix rather than canonicalizing the entire filename, to avoid
returning a filename unknown to GDB when the file is actually a
symbolic link.

This change was tested

BTW: I just a message requesting a change in the exact same function.
I haven't integrated it yet, because I don't know if it will be accepted
or not. If it is accepted, I can merge it and resubmit.

Thanks,
--
Joel

[-- Attachment #2: utils.c.diff --]
[-- Type: text/plain, Size: 4450 bytes --]

Index: utils.c
===================================================================
RCS file: /cvs/src/src/gdb/utils.c,v
retrieving revision 1.70
diff -c -3 -p -r1.70 utils.c
*** utils.c 2002/03/19 02:51:07 1.70
--- utils.c 2002/03/19 16:08:08
*************** string_to_core_addr (const char *my_stri
*** 2531,2558 ****
return addr;
}

- char *
- gdb_realpath (const char *filename)
- {
#if defined(HAVE_REALPATH)
# if defined (PATH_MAX)
! char buf[PATH_MAX];
# define USE_REALPATH
# elif defined (MAXPATHLEN)
! char buf[MAXPATHLEN];
# define USE_REALPATH
# elif defined (HAVE_UNISTD_H) && defined(HAVE_ALLOCA)
! char *buf = alloca ((size_t)pathconf ("/", _PC_PATH_MAX));
# define USE_REALPATH
# endif
#endif /* HAVE_REALPATH */

! #if defined(USE_REALPATH)
! char *rp = realpath (filename, buf);
! return xstrdup (rp ? rp : filename);
! #elif defined(HAVE_CANONICALIZE_FILE_NAME)
! return canonicalize_file_name (filename);
#else
return xstrdup (filename);
#endif
}
--- 2531,2640 ----
return addr;
}

#if defined(HAVE_REALPATH)
# if defined (PATH_MAX)
! static const int max_rp_buffer_size = PATH_MAX;
# define USE_REALPATH
# elif defined (MAXPATHLEN)
! static const int max_rp_buffer_size = MAXPATHLEN;
# define USE_REALPATH
# elif defined (HAVE_UNISTD_H) && defined(HAVE_ALLOCA)
! static const int max_rp_buffer_size = pathconf ("/", _PC_PATH_MAX);
# define USE_REALPATH
# endif
#endif /* HAVE_REALPATH */
+
+ /*
+ * gdb_canonicalize_path
+ *
+ * Sets RESOLVED_PATH to the canonicalized form of FILENAME, as realpath ()
+ * does. If there is no error, it returns a pointer to the resolved path.
+ * Otherwise, returns null.
+ *
+ * Returns null on platforms where no canonicalization routine (such as
+ * realpath for instance) are available.
+ */
+ static char *
+ gdb_canonicalize_path (const char *path, char *resolved_path)
+ {
+ #if defined (USE_REALPATH)
+ return realpath (path, resolved_path);
+
+ #elif defined (HAVE_CANONICALIZE_FILE_NAME)
+ const char *canonicalized = canonicalize_file_name (path);
+
+ if (!canonicalized)
+ return null;

! strcpy (resolved_path, canonicalized);
! return resolved_path;
!
#else
+ return null;
+
+ #endif
+ }
+
+ /*
+ * gdb_realpath_fallback
+ *
+ * This is the fallback version of gdb_realpath when there is no
+ * canonicalization routine (such as realpath for instance) available.
+ * In this case, we simply return a copy of FILENAME.
+ */
+ static char *
+ gdb_realpath_fallback (const char *filename)
+ {
return xstrdup (filename);
+ }
+
+ /*
+ * gdb_realpath
+ *
+ * Return a copy of FILENAME, with its directory prefix canonicalized,
+ * as for realpath() (see "man realpath" for more details on what
+ * this function does), or simply a copy of FILENAME on platforms
+ * where no canonicalization routine is available.
+ *
+ * We don't want to canonicalize the entire FILENAME, because
+ * the canonicalization routines used to perform the operation also
+ * expand symbolic links. If FILENAME is itself a link to another file,
+ * it can lead GDB to translate FILENAME into another filename that GDB
+ * does not know about. This can confuse graphical front-ends for GDB
+ * which, like GVD, rely on the filename displayed by GDB to get the
+ * current file:line location, for example.
+ */
+ char *
+ gdb_realpath (const char *filename)
+ {
+ const char *base_name = lbasename (filename);
+ char *dir_name;
+ char *buf;
+ char *rp = NULL;
+
+ #if !defined (USE_REALPATH) || !defined (HAVE_CANONICALIZE_FILE_NAME)
+ return gdb_realpath_fallback (filename);
#endif
+
+ /* If basename and filename are equal, then there is no path to
+ canonicalize. Just return a copy of filename */
+ if (base_name == filename)
+ return xstrdup (filename);
+
+ dir_name = alloca ((size_t) (base_name - filename + 1));
+ strncpy (dir_name, filename, base_name - filename);
+
+ /* Allocate enough space to contain the largest path possible returned
+ by realpath, plus the SLASH_STRING and the base_name */
+ buf = alloca ((size_t) max_rp_buffer_size +
+ strlen (SLASH_STRING) + strlen (base_name));
+ rp = gdb_canonicalize_path (dir_name, buf);
+
+ if (rp == NULL)
+ return xstrdup (filename);
+
+ strcat (buf, SLASH_STRING);
+ strcat (buf, base_name);
+ return xstrdup (buf);
}
+