14e6c2a
commit be82bb5f9dfecd854c53eda321d1914f28f19790
14e6c2a
Author: Mark Wielaard <mark@klomp.org>
14e6c2a
Date:   Sat Dec 9 23:01:29 2017 +0100
6cc1e85
14e6c2a
    Fix gnu debug alt file resolving.
14e6c2a
    
14e6c2a
    https://bugs.kde.org/show_bug.cgi?id=387773
14e6c2a
    
14e6c2a
    The path to the alt file is relative to the actual debug file.
14e6c2a
    Make sure that we got the real file, not a (build-id) symlink.
14e6c2a
    Also handle the case where a debug or alt file is an absolute path.
6cc1e85
6cc1e85
diff --git a/coregrind/m_debuginfo/readelf.c b/coregrind/m_debuginfo/readelf.c
14e6c2a
index e612250..c19ff21 100644
6cc1e85
--- a/coregrind/m_debuginfo/readelf.c
6cc1e85
+++ b/coregrind/m_debuginfo/readelf.c
6cc1e85
@@ -33,6 +33,7 @@
6cc1e85
 
6cc1e85
 #include "pub_core_basics.h"
6cc1e85
 #include "pub_core_vki.h"
6cc1e85
+#include "pub_core_vkiscnums.h"
6cc1e85
 #include "pub_core_debuginfo.h"
6cc1e85
 #include "pub_core_libcbase.h"
6cc1e85
 #include "pub_core_libcprint.h"
6cc1e85
@@ -40,6 +41,7 @@
6cc1e85
 #include "pub_core_machine.h"      /* VG_ELF_CLASS */
6cc1e85
 #include "pub_core_options.h"
6cc1e85
 #include "pub_core_oset.h"
6cc1e85
+#include "pub_core_syscall.h"
6cc1e85
 #include "pub_core_tooliface.h"    /* VG_(needs) */
6cc1e85
 #include "pub_core_xarray.h"
6cc1e85
 #include "priv_misc.h"             /* dinfo_zalloc/free/strdup */
14e6c2a
@@ -1323,6 +1325,12 @@ DiImage* find_debug_file( struct _DebugInfo* di,
14e6c2a
                      + (extrapath ? VG_(strlen)(extrapath) : 0)
14e6c2a
                      + (serverpath ? VG_(strlen)(serverpath) : 0));
14e6c2a
 
14e6c2a
+      if (debugname[0] == '/') {
14e6c2a
+         VG_(sprintf)(debugpath, "%s", debugname);
14e6c2a
+         dimg = open_debug_file(debugpath, buildid, crc, rel_ok, NULL);
14e6c2a
+         if (dimg != NULL) goto dimg_ok;
14e6c2a
+      }
14e6c2a
+
14e6c2a
       VG_(sprintf)(debugpath, "%s/%s", objdir, debugname);
14e6c2a
       dimg = open_debug_file(debugpath, buildid, crc, rel_ok, NULL);
14e6c2a
       if (dimg != NULL) goto dimg_ok;
14e6c2a
@@ -1527,6 +1535,56 @@ static Bool check_compression(ElfXX_Shdr* h, DiSlice* s) {
6cc1e85
     return True;
6cc1e85
 }
6cc1e85
 
14e6c2a
+/* Helper function to get the readlink path. Returns a copy of path if the
14e6c2a
+   file wasn't a symbolic link. Returns NULL on error. Unless NULL is
14e6c2a
+   returned the result needs to be released with dinfo_free.
6cc1e85
+*/
6cc1e85
+static HChar* readlink_path (const HChar *path)
6cc1e85
+{
6cc1e85
+   SizeT bufsiz = VG_(strlen)(path);
6cc1e85
+   HChar *buf = ML_(dinfo_strdup)("readlink_path.strdup", path);
14e6c2a
+   UInt   tries = 6;
6cc1e85
+
6cc1e85
+   while (tries > 0) {
6cc1e85
+      SysRes res;
6cc1e85
+#if defined(VGP_arm64_linux)
6cc1e85
+      res = VG_(do_syscall4)(__NR_readlinkat, VKI_AT_FDCWD,
6cc1e85
+                                              (UWord)path, (UWord)buf, bufsiz);
6cc1e85
+#elif defined(VGO_linux) || defined(VGO_darwin)
6cc1e85
+      res = VG_(do_syscall3)(__NR_readlink, (UWord)path, (UWord)buf, bufsiz);
6cc1e85
+#elif defined(VGO_solaris)
6cc1e85
+      res = VG_(do_syscall4)(__NR_readlinkat, VKI_AT_FDCWD, (UWord)path,
6cc1e85
+                             (UWord)buf, bufsiz);
6cc1e85
+#else
6cc1e85
+#       error Unknown OS
6cc1e85
+#endif
14e6c2a
+      if (sr_isError(res)) {
14e6c2a
+         if (sr_Err(res) == VKI_EINVAL)
14e6c2a
+            return buf; // It wasn't a symbolic link, return the strdup result.
14e6c2a
+         ML_(dinfo_free)(buf);
14e6c2a
+         return NULL;
14e6c2a
+      }
6cc1e85
+
14e6c2a
+      SSizeT r = sr_Res(res);
14e6c2a
+      if (r < 0) break;
6cc1e85
+      if (r == bufsiz) {  // buffer too small; increase and retry
6cc1e85
+         bufsiz *= 2 + 16;
6cc1e85
+         buf = ML_(dinfo_realloc)("readlink_path.realloc", buf, bufsiz);
6cc1e85
+         tries--;
6cc1e85
+         continue;
6cc1e85
+      }
6cc1e85
+      buf[r] = '\0';
6cc1e85
+      break;
6cc1e85
+   }
6cc1e85
+
6cc1e85
+   if (tries == 0) { // We tried, but weird long path?
6cc1e85
+      ML_(dinfo_free)(buf);
6cc1e85
+      return NULL;
6cc1e85
+   }
6cc1e85
+
6cc1e85
+  return buf;
6cc1e85
+}
6cc1e85
+
6cc1e85
 /* The central function for reading ELF debug info.  For the
6cc1e85
    object/exe specified by the DebugInfo, find ELF sections, then read
6cc1e85
    the symbols, line number info, file name info, CFA (stack-unwind
14e6c2a
@@ -2926,8 +2984,12 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
6cc1e85
                                 (debugaltlink_escn.szB - buildid_offset)
6cc1e85
                                 * 2 + 1);
6cc1e85
 
6cc1e85
-         /* The altfile might be relative to the debug file or main file. */
6cc1e85
+         /* The altfile might be relative to the debug file or main file.
6cc1e85
+	    Make sure that we got the real file, not a symlink.  */
6cc1e85
          HChar *dbgname = di->fsm.dbgname ? di->fsm.dbgname : di->fsm.filename;
6cc1e85
+         HChar* rdbgname = readlink_path (dbgname);
6cc1e85
+         if (rdbgname == NULL)
6cc1e85
+            rdbgname = ML_(dinfo_strdup)("rdbgname", dbgname);
6cc1e85
 
6cc1e85
          for (j = 0; j < debugaltlink_escn.szB - buildid_offset; j++)
6cc1e85
             VG_(sprintf)(
14e6c2a
@@ -2937,9 +2999,11 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
6cc1e85
                                         + buildid_offset + j));
6cc1e85
 
6cc1e85
          /* See if we can find a matching debug file */
6cc1e85
-         aimg = find_debug_file( di, dbgname, altbuildid,
6cc1e85
+         aimg = find_debug_file( di, rdbgname, altbuildid,
6cc1e85
                                  altfile_str_m, 0, True );
6cc1e85
 
6cc1e85
+         ML_(dinfo_free)(rdbgname);
6cc1e85
+
6cc1e85
          if (altfile_str_m)
6cc1e85
             ML_(dinfo_free)(altfile_str_m);
6cc1e85
          ML_(dinfo_free)(altbuildid);
Mark Wielaard 275b4b6
Mark Wielaard 275b4b6
diff --git a/coregrind/m_debuginfo/readelf.c b/coregrind/m_debuginfo/readelf.c
Mark Wielaard 275b4b6
index c19ff212b..70c28e629 100644
Mark Wielaard 275b4b6
--- a/coregrind/m_debuginfo/readelf.c
Mark Wielaard 275b4b6
+++ b/coregrind/m_debuginfo/readelf.c
Mark Wielaard 275b4b6
@@ -1582,6 +1582,24 @@ static HChar* readlink_path (const HChar *path)
Mark Wielaard 275b4b6
       return NULL;
Mark Wielaard 275b4b6
    }
Mark Wielaard 275b4b6
 
Mark Wielaard 275b4b6
+  if (buf[0] == '/')
Mark Wielaard 275b4b6
+    return buf;
Mark Wielaard 275b4b6
+
Mark Wielaard 275b4b6
+  /* Relative path, add link dir.  */
Mark Wielaard 275b4b6
+  HChar *linkdirptr;
Mark Wielaard 275b4b6
+  SizeT linkdir_len = VG_(strlen)(path);
Mark Wielaard 275b4b6
+  if ((linkdirptr = VG_(strrchr)(path, '/')) != NULL)
Mark Wielaard 275b4b6
+    linkdir_len -= VG_(strlen)(linkdirptr + 1);
Mark Wielaard 275b4b6
+
Mark Wielaard 275b4b6
+  SizeT buflen = VG_(strlen)(buf);
Mark Wielaard 275b4b6
+  SizeT needed = linkdir_len + buflen + 1;
Mark Wielaard 275b4b6
+  if (bufsiz < needed)
Mark Wielaard 275b4b6
+    buf = ML_(dinfo_realloc)("readlink_path.linkdir", buf, needed);
Mark Wielaard 275b4b6
+
Mark Wielaard 275b4b6
+  VG_(memmove)(buf + linkdir_len, buf, buflen);
Mark Wielaard 275b4b6
+  VG_(memcpy)(buf, path, linkdir_len);
Mark Wielaard 275b4b6
+  buf[needed - 1] = '\0';
Mark Wielaard 275b4b6
+
Mark Wielaard 275b4b6
   return buf;
Mark Wielaard 275b4b6
 }
Mark Wielaard 275b4b6
 
Mark Wielaard 275b4b6