Add auxv reader

We should be able to instrument dynamic linker as well, if that is
requested.  Unfortunately we may still need artificial _start probe,
because we don't know _r_debug address until the dynamic linker fills
the DT_DEBUG entry.  (Though we are likely to find _r_debug in symbol
table if we look.)
diff --git a/common.h b/common.h
index 0d824da..eef3769 100644
--- a/common.h
+++ b/common.h
@@ -236,5 +236,13 @@
 int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp);
 void arch_breakpoint_destroy(struct breakpoint *sbp);
 
+typedef void *target_address_t;
+/* This should extract entry point address and interpreter (dynamic
+ * linker) bias if possible.  Returns 0 if there were no errors, -1
+ * otherwise.  Sets *ENTRYP and *INTERP_BIASP to non-zero values if
+ * the corresponding value is known.  Unknown values are set to 0.  */
+int process_get_entry(struct Process *proc,
+		      target_address_t *entryp,
+		      target_address_t *interp_biasp);
 
 #endif
diff --git a/proc.c b/proc.c
index c50236b..afdc3a8 100644
--- a/proc.c
+++ b/proc.c
@@ -72,13 +72,26 @@
 		return -1;
 	}
 
-	if (proc->leader == proc && breakpoints_init(proc, enable) < 0) {
-		fprintf(stderr, "failed to init breakpoints %d\n",
+	/* For secondary threads, this is all that we need to do.  */
+	if (proc->leader != proc)
+		return 0;
+
+	target_address_t entry;
+	target_address_t interp_bias;
+	if (process_get_entry(proc, &entry, &interp_bias) < 0) {
+		fprintf(stderr, "Couldn't get entry points of process %d\n",
 			proc->pid);
+	fail:
 		process_bare_destroy(proc);
 		return -1;
 	}
 
+	if (breakpoints_init(proc, enable) < 0) {
+		fprintf(stderr, "failed to init breakpoints %d\n",
+			proc->pid);
+		goto fail;
+	}
+
 	return 0;
 }
 
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index 8a63ed9..3eacf2a 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -567,6 +567,76 @@
 	return 0;
 }
 
+static int
+fetch_auxv64_entry(int fd, Elf64_auxv_t *ret)
+{
+	/* Reaching EOF is as much problem as not reading whole
+	 * entry.  */
+	return read(fd, ret, sizeof(*ret)) == sizeof(*ret) ? 0 : -1;
+}
+
+static int
+fetch_auxv32_entry(int fd, Elf64_auxv_t *ret)
+{
+	Elf32_auxv_t auxv;
+	if (read(fd, &auxv, sizeof(auxv)) != sizeof(auxv))
+		return -1;
+
+	ret->a_type = auxv.a_type;
+	ret->a_un.a_val = auxv.a_un.a_val;
+	return 0;
+}
+
+static int (*
+auxv_fetcher(struct Process *proc))(int, Elf64_auxv_t *)
+{
+	return select_32_64(proc, fetch_auxv32_entry, fetch_auxv64_entry);
+}
+
+int
+process_get_entry(struct Process *proc,
+		  target_address_t *entryp,
+		  target_address_t *interp_biasp)
+{
+	PROC_PID_FILE(fn, "/proc/%d/auxv", proc->pid);
+	int fd = open(fn, O_RDONLY);
+	if (fd == -1) {
+	fail:
+		error(0, errno, "couldn't read %s", fn);
+	done:
+		if (fd != -1)
+			close(fd);
+		return fd == -1 ? -1 : 0;
+	}
+
+	target_address_t at_entry = 0;
+	target_address_t at_bias = 0;
+	while (1) {
+		Elf64_auxv_t entry;
+		if (auxv_fetcher(proc)(fd, &entry) < 0)
+			goto fail;
+
+		switch (entry.a_type) {
+		case AT_BASE:
+			at_bias = (target_address_t)entry.a_un.a_val;
+			continue;
+
+		case AT_ENTRY:
+			at_entry = (target_address_t)entry.a_un.a_val;
+		default:
+			continue;
+
+		case AT_NULL:
+			break;
+		}
+		break;
+	}
+
+	*entryp = at_entry;
+	*interp_biasp = at_bias;
+	goto done;
+}
+
 int
 task_kill (pid_t pid, int sig)
 {