summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kratochvil <jan.kratochvil@redhat.com>2012-10-10 20:42:30 +0200
committerJan Kratochvil <jan.kratochvil@redhat.com>2012-10-27 22:26:16 +0200
commit02d61ee510a4fab8b2860505e3a8fb3b32779670 (patch)
tree78405b6d482eeefc83d1d7ea49dd8ff2b19b7dff
parentd7ed0254bcdeeed0ed2b69d864afcd9d5492d0a0 (diff)
libdwfl/
linux-proc-maps.c: Include system.h. (PROCEXEFMT, get_pid_class): New. (grovel_auxv): Detect 32-bit vs. 64-bit auxv, possibly call get_pid_class. Signed-off-by: Jan Kratochvil <jan.kratochvil@redhat.com>
-rw-r--r--libdwfl/ChangeLog7
-rw-r--r--libdwfl/linux-proc-maps.c129
2 files changed, 93 insertions, 43 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index bdd9440f..824daf19 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,10 @@
+2012-10-27 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * linux-proc-maps.c: Include system.h.
+ (PROCEXEFMT, get_pid_class): New.
+ (grovel_auxv): Detect 32-bit vs. 64-bit auxv, possibly call
+ get_pid_class.
+
2012-10-17 Jan Kratochvil <jan.kratochvil@redhat.com>
* dwfl_module_getdwarf.c (mod_verify_build_id): New function with code
diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
index 4fbe90d1..feed6a11 100644
--- a/libdwfl/linux-proc-maps.c
+++ b/libdwfl/linux-proc-maps.c
@@ -39,13 +39,42 @@
#include <unistd.h>
#include <assert.h>
#include <endian.h>
+#include "system.h"
#define PROCMAPSFMT "/proc/%d/maps"
#define PROCMEMFMT "/proc/%d/mem"
#define PROCAUXVFMT "/proc/%d/auxv"
+#define PROCEXEFMT "/proc/%d/exe"
+/* Return ELFCLASS64 or ELFCLASS32 for the main ELF executable. Return
+ ELFCLASSNONE for an error. */
+
+static unsigned char
+get_pid_class (pid_t pid)
+{
+ char *fname;
+ if (asprintf (&fname, PROCEXEFMT, pid) < 0)
+ return ELFCLASSNONE;
+
+ int fd = open64 (fname, O_RDONLY);
+ free (fname);
+ if (fd < 0)
+ return ELFCLASSNONE;
+
+ unsigned char buf[EI_CLASS + 1];
+ ssize_t nread = pread_retry (fd, &buf, sizeof buf, 0);
+ close (fd);
+ if (nread != sizeof (buf) || buf[EI_MAG0] != ELFMAG0
+ || buf[EI_MAG1] != ELFMAG1 || buf[EI_MAG2] != ELFMAG2
+ || buf[EI_MAG3] != ELFMAG3
+ || (buf[EI_CLASS] != ELFCLASS64 && buf[EI_CLASS] != ELFCLASS32))
+ return ELFCLASSNONE;
+
+ return buf[EI_CLASS];
+}
+
/* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. */
static int
@@ -60,61 +89,75 @@ grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
if (fd < 0)
return errno == ENOENT ? 0 : errno;
+ GElf_Addr sysinfo_ehdr64 = 0, sysinfo_ehdr32 = 0;
+ GElf_Addr segment_align64 = dwfl->segment_align;
+ GElf_Addr segment_align32 = dwfl->segment_align;
+ off_t offset = 0;
ssize_t nread;
+ union
+ {
+ Elf64_auxv_t a64[64];
+ Elf32_auxv_t a32[128];
+ } d;
do
{
- union
- {
- char buffer[sizeof (long int) * 2 * 64];
- Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)];
- Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)];
- } d;
- nread = read (fd, &d, sizeof d);
- if (nread > 0)
+ eu_static_assert (sizeof d.a64 == sizeof d.a32);
+ nread = pread_retry (fd, d.a64, sizeof d.a64, offset);
+ if (nread < 0)
+ return errno;
+ for (unsigned a32i = 0; a32i < nread / sizeof d.a32[0]; a32i++)
{
- switch (sizeof (long int))
- {
- case 4:
- for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i)
- if (d.a32[i].a_type == AT_SYSINFO_EHDR)
- {
- *sysinfo_ehdr = d.a32[i].a_un.a_val;
- if (dwfl->segment_align > 1)
- {
- nread = 0;
- break;
- }
- }
- else if (d.a32[i].a_type == AT_PAGESZ
- && dwfl->segment_align <= 1)
- dwfl->segment_align = d.a32[i].a_un.a_val;
+ const Elf32_auxv_t *a32 = d.a32 + a32i;
+ switch (a32->a_type)
+ {
+ case AT_SYSINFO_EHDR:
+ sysinfo_ehdr32 = a32->a_un.a_val;
break;
- case 8:
- for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i)
- if (d.a64[i].a_type == AT_SYSINFO_EHDR)
- {
- *sysinfo_ehdr = d.a64[i].a_un.a_val;
- if (dwfl->segment_align > 1)
- {
- nread = 0;
- break;
- }
- }
- else if (d.a64[i].a_type == AT_PAGESZ
- && dwfl->segment_align <= 1)
- dwfl->segment_align = d.a64[i].a_un.a_val;
+ case AT_PAGESZ:
+ segment_align32 = a32->a_un.a_val;
break;
- default:
- abort ();
+ }
+ }
+ for (unsigned a64i = 0; a64i < nread / sizeof d.a64[0]; a64i++)
+ {
+ const Elf64_auxv_t *a64 = d.a64 + a64i;
+ switch (a64->a_type)
+ {
+ case AT_SYSINFO_EHDR:
+ sysinfo_ehdr64 = a64->a_un.a_val;
break;
- }
+ case AT_PAGESZ:
+ segment_align64 = a64->a_un.a_val;
+ break;
+ }
}
+ offset += nread;
}
- while (nread > 0);
+ while (nread == sizeof d.a64);
close (fd);
- return nread < 0 ? errno : 0;
+ bool valid64 = sysinfo_ehdr64 || segment_align64 != dwfl->segment_align;
+ bool valid32 = sysinfo_ehdr32 || segment_align32 != dwfl->segment_align;
+
+ if (! valid64 && ! valid32)
+ return 0;
+
+ unsigned char pid_class = ELFCLASSNONE;
+ if (valid64 && valid32)
+ pid_class = get_pid_class (pid);
+
+ if (pid_class == ELFCLASS64 || (valid64 && ! valid32))
+ {
+ *sysinfo_ehdr = sysinfo_ehdr64;
+ dwfl->segment_align = segment_align64;
+ }
+ if (pid_class == ELFCLASS32 || (! valid64 && valid32))
+ {
+ *sysinfo_ehdr = sysinfo_ehdr32;
+ dwfl->segment_align = segment_align32;
+ }
+ return 0;
}
static int