[draft patch 6/6] gdbserver build-id attribute generator (unfixed)

Jan Kratochvil
2013-03-10T21:09:08Z

--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -40179,6 +40179,9 @@ memory address. It is a displacement of absolute memory address against
address the file was prelinked to during the library load.
@item
@code{l_ld}, which is memory address of the @code{PT_DYNAMIC} segment
+@item
+@code{build-id}, hex encoded @code{.note.gnu.build-id} section, if such
+section exists.
@end itemize

Additionally the single @code{main-lm} attribute specifies address of
@@ -40196,7 +40199,7 @@ looks like this:
<library name="/lib/ld-linux.so.2" lm="0xe4f51c" l_addr="0xe2d000"
l_ld="0xe4eefc"/>
<library name="/lib/libc.so.6" lm="0xe4fbe8" l_addr="0x154000"
- l_ld="0x152350"/>
+ l_ld="0x152350" build-id="9afccf7cc41e6293476223fe72480854"/>
</library-list-svr>
@end smallexample

@@ -40212,6 +40215,7 @@ The format of an SVR4 library list is described by this DTD:
<!ATTLIST library lm CDATA #REQUIRED>
<!ATTLIST library l_addr CDATA #REQUIRED>
<!ATTLIST library l_ld CDATA #REQUIRED>
+<!ATTLIST library build-id CDATA #IMPLIED>
@end smallexample

@node Memory Map Format
--- a/gdb/features/library-list-svr4.dtd
+++ b/gdb/features/library-list-svr4.dtd
@@ -14,3 +14,4 @@
<!ATTLIST library lm CDATA #REQUIRED>
<!ATTLIST library l_addr CDATA #REQUIRED>
<!ATTLIST library l_ld CDATA #REQUIRED>
+<!ATTLIST library build-id CDATA #IMPLIED>
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -20,6 +20,7 @@
#include "linux-low.h"
#include "linux-osdata.h"
#include "agent.h"
+#include "linux-maps.h"

#include "gdb_wait.h"
#include <stdio.h>
@@ -5639,6 +5640,149 @@ struct link_map_offsets
int l_prev_offset;
};

+struct find_memory_region_callback_data
+{
+ unsigned is_elf64 : 1;
+ const char *soname;
+ CORE_ADDR l_addr;
+
+ /* Return. Meaningful iff *build_id != NULL. */
+ size_t build_idsz;
+
+ /* Return. malloc allocated memory. */
+ void *build_id;
+};
+
+static linux_find_memory_region_ftype find_memory_region_callback;
+
+static int
+find_memory_region_callback (ULONGEST vaddr, ULONGEST size, ULONGEST offset,
+ ULONGEST inode, int read, int write, int exec,
+ int modified, const char *filename, void *data)
+{
+ struct find_memory_region_callback_data *p = data;
+
+ if (filename != NULL && strcmp (filename, p->soname) == 0)
+ {
+ union ElfXX_Ehdr
+ {
+ Elf32_Ehdr _32;
+ Elf64_Ehdr _64;
+ } ehdr;
+ union ElfXX_Phdr
+ {
+ Elf32_Phdr _32;
+ Elf64_Phdr _64;
+ } phdr;
+ union ElfXX_Nhdr
+ {
+ Elf32_Nhdr _32;
+ Elf64_Nhdr _64;
+ } *nhdr;
+#define HDR(p, hdr, fld) (((p)->is_elf64)? (hdr)._64.fld : (hdr)._32.fld)
+ if (linux_read_memory (vaddr, (unsigned char *) &ehdr, sizeof (ehdr))
+ == 0
+ && HDR (p, ehdr, e_ident[EI_MAG0]) == ELFMAG0
+ && HDR (p, ehdr, e_ident[EI_MAG1]) == ELFMAG1
+ && HDR (p, ehdr, e_ident[EI_MAG2]) == ELFMAG2
+ && HDR (p, ehdr, e_ident[EI_MAG3]) == ELFMAG3)
+ {
+ unsigned i;
+
+ for (i = 0; i < HDR (p, ehdr, e_phnum); ++i)
+ {
+ if (linux_read_memory (vaddr + HDR (p, ehdr, e_phoff)
+ + HDR (p, ehdr, e_phentsize) * i,
+ (unsigned char *) &phdr,
+ HDR (p, ehdr, e_phentsize)) != 0)
+ {
+ warning ("Could not read program header.");
+ break;
+ }
+ if (HDR (p, phdr, p_type) == PT_NOTE)
+ {
+ nhdr = xmalloc (HDR (p, phdr, p_memsz));
+
+ if (linux_read_memory (p->l_addr + HDR (p, phdr, p_vaddr),
+ (unsigned char *) nhdr,
+ HDR (p, phdr, p_memsz)) != 0)
+ {
+ warning ("Could not read note.");
+ break;
+ }
+ if (HDR (p, *nhdr, n_type) == NT_GNU_BUILD_ID)
+ {
+ p->build_idsz = (HDR (p, *nhdr, n_namesz)
+ + HDR (p, *nhdr, n_descsz)
+ + (p->is_elf64
+ ? sizeof (nhdr->_64)
+ : sizeof (nhdr->_32)));
+ if (p->build_idsz > HDR (p, phdr, p_memsz))
+ {
+ warning ("NT_GNU_BUILD_ID has invalid size..");
+ break;
+ }
+ p->build_id = nhdr;
+ break;
+ }
+ free (nhdr);
+ }
+ }
+ }
+ else
+ warning ("Reading build-id failed.");
+
+ return 1;
+ }
+#undef HDR
+ return 0;
+}
+
+/* Return malloc allocated buffer. User must free it.
+
+ NULL may be returned if build-id could not be fetched. */
+
+static char *
+get_hex_build_id (const char *const soname, const int is_elf64,
+ const CORE_ADDR l_addr)
+{
+ struct find_memory_region_callback_data data;
+ char *hex_build_id;
+ char *real_soname = realpath (soname, NULL);
+
+ if (real_soname == NULL)
+ {
+ fprintf (stderr, "Failed to get realpath of %s (%s).\n", soname,
+ strerror (errno));
+ return NULL;
+ }
+
+ data.is_elf64 = is_elf64 != 0;
+ data.soname = real_soname;
+ data.l_addr = l_addr;
+ data.build_idsz = 0;
+ data.build_id = NULL;
+
+ linux_find_memory_regions_full (lwpid_of (get_thread_lwp (current_inferior)),
+ find_memory_region_callback, &data, NULL);
+ free (real_soname);
+ if (data.build_id != NULL)
+ {
+ hex_build_id = xmalloc (data->build_idsz * 2 + 1);
+ if (bin2hex (data.build_id, hex_build_id, data->build_idsz)
+ != data->build_idsz)
+ {
+ fprintf (stderr, "Hex encoding of build-id failed\n");
+ xfree (hex_build_id);
+ hex_build_id = NULL;
+ }
+ }
+ else
+ hex_build_id = NULL;
+ free (data.build_id);
+ return hex_build_id;
+}
+
/* Construct qXfer:libraries-svr4:read reply. */

static int
@@ -5760,6 +5904,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
/* 6x the size for xml_escape_text below. */
size_t len = 6 * strlen ((char *) libname);
char *name;
+ char *hex_enc_build_id;

if (!header_done)
{
@@ -5768,7 +5913,12 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
header_done = 1;
}

- while (allocated < p - document + len + 200)
+ name = xml_escape_text ((char *) libname);
+ hex_enc_build_id = get_hex_build_id (name, is_elf64, l_addr);
+
+ while (allocated < (p - document + len + 200
+ + (hex_enc_build_id != NULL
+ ? strlen (hex_enc_build_id) : 0)))
{
/* Expand to guarantee sufficient storage. */
uintptr_t document_len = p - document;
@@ -5778,12 +5928,15 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
p = document + document_len;
}

- name = xml_escape_text ((char *) libname);
p += sprintf (p, "<library name=\"%s\" lm=\"0x%lx\" "
- "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
+ "l_addr=\"0x%lx\" l_ld=\"0x%lx\"",
name, (unsigned long) lm_addr,
(unsigned long) l_addr, (unsigned long) l_ld);
+ if (hex_enc_build_id != NULL)
+ p += sprintf (p, " build-id=\"%s\"", hex_enc_build_id);
+ p += sprintf(p, "/>");
free (name);
+ xfree (hex_enc_build_id);
}
else if (lm_prev == 0)
{