Hello Roland and others,
I found an issue with dwarf_getsrc_file() and its ability to
support a combination of Windows and UNIX slashes (‘\\’ vs. ‘/’).
The problem occurs when I load an ELF file that was built on Windows and has
relative paths to source files (i.e. “dir\\file.c”). The
logic in dwarf_getsrc_file() currently handles this as a basename path
(is_basename is true). In this case, it will compare “dir\\file.c”
with basename(“dir/file.c”), which will always fail. The fix
for this is to check for the presence of either a ‘/’ or ‘\\’
in the filename passed into the function.
Once this is fixed, there’s still the issue of using a
straight strcmp() to compare the paths, which fails because the separator is
different (I always use ‘/’ as my separator on Linux – I haven’t
found an easy way to determine which separator to use for a given ELF file, but
maybe someone has a suggestion here and I could change my application code
instead). But it would be nice if libdw handled ELF files built on both
Windows and Linux seamlessly.
I propose the changes below to fix these issues (note that
this diff is against 0.142, but nothing has changed in this file since then).
I’m certain that this is not the most elegant way to solve the problem,
so if someone wants to improve on this and get it into the trunk, that would be
most excellent.
Please let me know if I can provide any additional
information to help get this fixed. Also, I will happily test out any fixes
if that would be helpful.
Thanks for your time!
--Mike
diff --git a/libdw/dwarf_getsrc_file.c b/libdw/dwarf_getsrc_file.c
index bc612f6..dcdda68 100644
--- a/libdw/dwarf_getsrc_file.c
+++ b/libdw/dwarf_getsrc_file.c
@@ -59,6 +59,23 @@
#include "libdwP.h"
+#include <stdio.h>
+
+int path_chr(char c)
+{
+ if(c == '\\')
+ return '/';
+ return c;
+}
+
+int path_strcmp (const char * p1, const char * p2)
+{
+ for(; path_chr(*p1) == path_chr(*p2); ++p1, ++p2)
+ if(*p1 == 0) {
+ return 0;
+ }
+ return *(unsigned char *)p1 < *(unsigned char
*)p2 ? -1 : 1;
+}
int
dwarf_getsrc_file (Dwarf *dbg, const char *fname, int
lineno, int column,
@@ -67,7 +84,8 @@ dwarf_getsrc_file (Dwarf *dbg, const char
*fname, int lineno, int column,
if (dbg == NULL)
return -1;
- bool is_basename = strchr (fname, '/') == NULL;
+ bool is_basename = (strchr (fname, '/') == NULL)
+ && (strchr(fname, '\\') ==
NULL);
size_t max_match = *nsrcs ?: ~0u;
size_t act_match = *nsrcs;
@@ -119,9 +137,9 @@ dwarf_getsrc_file (Dwarf *dbg, const
char *fname, int lineno, int column,
/* Match the name with the name the user provided. */
const char *fname2 = line->files->info[lastfile].name;
if (is_basename)
-
lastmatch = strcmp (basename (fname2), fname) == 0;
+
lastmatch = path_strcmp (basename (fname2), fname) == 0;
else
-
lastmatch = strcmp (fname2, fname) == 0;
+
lastmatch = path_strcmp (fname2, fname) == 0;
}
if
(!lastmatch)
continue;