Re: [patch] validate binary before use

Aleksandar Ristovski
2012-12-24T19:57:01Z

[-- Attachment #1: Type: text/plain, Size: 3739 bytes --]

On 12-12-21 03:20 PM, Aleksandar Ristovski wrote:
> Hello all,
>
>
> Currently gdb may use mismatched binary without complaining or noticing.
> This is particularly often the case when debugging remote targets, or
> multiple of, with multiple versions of libraries.
>
> The situation is possible on self hosted as well.
>
> For example, consider having:
> 1) a library called 'libso.so.1', with SONAME 'libso.so.1';
> 2) a program linked against it;
> 3) environment in which program can be executed and library from 1)
> found by the dynamic linker;
> 4) modified libso.so.1, different from the one found by the dynamic
> linker in 3), in the current working directory of gdb;
> 5) attach to process stared in 3) using gdb.
>
> What happens is, gdb will happily use modified libso.so.1 from current
> working directory without noticing the mismatch. This causes a lot of
> confusion.
>
> (I can elaborate on the example if needed).
>
>
> Change introduces new target_so_ops function dedicated to performing
> validation. If we have loaded and relocated the object, validation
> fails, we throw away bfd and loading of the object fails.
>
> The gist of the change is in solib-svr4.c and solib.c The rest are
> mechanical changes to fill-in new field with valid value.
>
> (for bonus points, solib-svr4.c now compiles without -Wno-unused)
>
> Regression test suite shows no regressions. (tested on
> x86_64-unknown-linux-gnu).
>
>

While writing a testcase I realized the patch as posted did not work in
general cases. Comparing whole elf header and whole pheader is not an
option as they change depending on whether the binary is stripped and such.

New change log:
<date> Aleksandar Ristovski <aristovski@qnx.com>

* mips-linux-tdep.c (mips_linux_init_abi): Assign validate value.
* ppc-linux-tdep.c (ppc_linux_init_abi): Ditto.
* solib-darwin.c (_initialize_darwin_solib): Ditto.
* solib-dsbt.c (_initialize_dsbt_solib): Ditto.
* solib-frv.c (_initialize_frv_solib): Ditto.
* solib-ia64-hpux.c (ia64_hpux_target_so_ops): Ditto.
* solib-irix.c (_initialize_irix_solib): Ditto.
* solib-osf.c (_initialize_osf_solib): Ditto.
* solib-pa64.c (_initialize_pa64_solib): Ditto.
* solib-som.c (_initialize_som_solib): Ditto.
* solib-spu.c (set_spu_solib_ops): Ditto.
* solib-sunos.c (_initialize_sunos_solib): Ditto.
* solib-svr4.c (read_program_headers_from_bfd,
read_elf_header_from_bfd): Forward declare.
(lm_addr_check): New formal argument TRUSTED.
(svr4_keep_data_in_core): Remove unused variable.
(read_elf_header_from_target, read_program_headers_from_target,
svr4_validate_ehdr_match, svr4_validate_phdr_match,
svr4_validate_hdrs_match, svr4_validate): New function.
(svr4_read_so_list): Remove unused variable.
(enable_break): Pass TRUSTED to lm_addr_check.
(read_elf_header_from_bfd): New function.
(svr4_relocate_section_address): Pass TRUSTED to lm_addr_check.
(_initialize_svr4_solib): Use svr4_validate.
* solib-target.c (solib.h): Include.
(_initialize_solib_target): Assign validate value.
* solib.c (solib_map_sections): Validate loaded SO.
(solib_validate): New function.
* solib.h (solib_validate): Declare.
* solist.h (target_so_ops): New field 'validate'.

Test suite change log:
<date> Aleksandar Ristovski <aristovski@qnx.com>

* gdb.base/solib-mismatch-lib.c: New file.
* gdb.base/solib-mismatch-libmod.c: New file.
* gdb.base/solib-mismatch-libmod2.c: New file.
* gdb.base/solib-mismatch.c: New file.
* gdb.base/solib-mismatch.exp: New test for solib validation.

Updated patch, including tests attached.

Thank you,

Aleksandar

[-- Attachment #2: solib-validate-201212241437.patch --]
[-- Type: text/x-patch, Size: 33402 bytes --]

Index: gdb/mips-linux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/mips-linux-tdep.c,v
retrieving revision 1.96
diff -u -p -r1.96 mips-linux-tdep.c
--- gdb/mips-linux-tdep.c 9 Nov 2012 19:57:59 -0000 1.96
+++ gdb/mips-linux-tdep.c 24 Dec 2012 19:37:16 -0000
@@ -1495,6 +1495,8 @@ mips_linux_init_abi (struct gdbarch_info
mips_svr4_so_ops.in_dynsym_resolve_code
= mips_linux_in_dynsym_resolve_code;
}
+ if (mips_svr4_so_ops.validate == NULL)
+ mips_svr4_so_ops.validate = solib_validate;
set_solib_ops (gdbarch, &mips_svr4_so_ops);

set_gdbarch_write_pc (gdbarch, mips_linux_write_pc);
Index: gdb/ppc-linux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-linux-tdep.c,v
retrieving revision 1.140
diff -u -p -r1.140 ppc-linux-tdep.c
--- gdb/ppc-linux-tdep.c 8 Nov 2012 14:16:33 -0000 1.140
+++ gdb/ppc-linux-tdep.c 24 Dec 2012 19:37:16 -0000
@@ -1733,6 +1733,8 @@ ppc_linux_init_abi (struct gdbarch_info
powerpc_so_ops.in_dynsym_resolve_code =
powerpc_linux_in_dynsym_resolve_code;
}
+ if (powerpc_so_ops.validate == NULL)
+ powerpc_so_ops.validate = solib_validate;
set_solib_ops (gdbarch, &powerpc_so_ops);

set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
Index: gdb/solib-darwin.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-darwin.c,v
retrieving revision 1.34
diff -u -p -r1.34 solib-darwin.c
--- gdb/solib-darwin.c 18 Dec 2012 06:19:54 -0000 1.34
+++ gdb/solib-darwin.c 24 Dec 2012 19:37:16 -0000
@@ -647,4 +647,5 @@ _initialize_darwin_solib (void)
darwin_so_ops.in_dynsym_resolve_code = darwin_in_dynsym_resolve_code;
darwin_so_ops.lookup_lib_global_symbol = darwin_lookup_lib_symbol;
darwin_so_ops.bfd_open = darwin_bfd_open;
+ darwin_so_ops.validate = solib_validate;
}
Index: gdb/solib-dsbt.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-dsbt.c,v
retrieving revision 1.8
diff -u -p -r1.8 solib-dsbt.c
--- gdb/solib-dsbt.c 9 Nov 2012 19:58:00 -0000 1.8
+++ gdb/solib-dsbt.c 24 Dec 2012 19:37:16 -0000
@@ -1182,6 +1182,7 @@ _initialize_dsbt_solib (void)
dsbt_so_ops.open_symbol_file_object = open_symbol_file_object;
dsbt_so_ops.in_dynsym_resolve_code = dsbt_in_dynsym_resolve_code;
dsbt_so_ops.bfd_open = solib_bfd_open;
+ dsbt_so_ops.validate = solib_validate;

/* Debug this file's internals. */
add_setshow_zuinteger_cmd ("solib-dsbt", class_maintenance,
Index: gdb/solib-frv.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-frv.c,v
retrieving revision 1.48
diff -u -p -r1.48 solib-frv.c
--- gdb/solib-frv.c 9 Nov 2012 19:58:00 -0000 1.48
+++ gdb/solib-frv.c 24 Dec 2012 19:37:16 -0000
@@ -1182,6 +1182,7 @@ _initialize_frv_solib (void)
frv_so_ops.open_symbol_file_object = open_symbol_file_object;
frv_so_ops.in_dynsym_resolve_code = frv_in_dynsym_resolve_code;
frv_so_ops.bfd_open = solib_bfd_open;
+ frv_so_ops.validate = solib_validate;

/* Debug this file's internals. */
add_setshow_zuinteger_cmd ("solib-frv", class_maintenance,
Index: gdb/solib-ia64-hpux.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-ia64-hpux.c,v
retrieving revision 1.5
diff -u -p -r1.5 solib-ia64-hpux.c
--- gdb/solib-ia64-hpux.c 9 Nov 2012 19:58:00 -0000 1.5
+++ gdb/solib-ia64-hpux.c 24 Dec 2012 19:37:16 -0000
@@ -686,6 +686,7 @@ ia64_hpux_target_so_ops (void)
ops->open_symbol_file_object = ia64_hpux_open_symbol_file_object;
ops->in_dynsym_resolve_code = ia64_hpux_in_dynsym_resolve_code;
ops->bfd_open = solib_bfd_open;
+ ops->validate = solib_validate;

return ops;
}
Index: gdb/solib-irix.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-irix.c,v
retrieving revision 1.45
diff -u -p -r1.45 solib-irix.c
--- gdb/solib-irix.c 9 Nov 2012 19:58:00 -0000 1.45
+++ gdb/solib-irix.c 24 Dec 2012 19:37:16 -0000
@@ -653,4 +653,5 @@ _initialize_irix_solib (void)
irix_so_ops.open_symbol_file_object = irix_open_symbol_file_object;
irix_so_ops.in_dynsym_resolve_code = irix_in_dynsym_resolve_code;
irix_so_ops.bfd_open = solib_bfd_open;
+ irix_so_ops.validate = solib_validate;
}
Index: gdb/solib-osf.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-osf.c,v
retrieving revision 1.37
diff -u -p -r1.37 solib-osf.c
--- gdb/solib-osf.c 24 May 2012 16:51:36 -0000 1.37
+++ gdb/solib-osf.c 24 Dec 2012 19:37:16 -0000
@@ -634,6 +634,7 @@ _initialize_osf_solib (void)
osf_so_ops.open_symbol_file_object = osf_open_symbol_file_object;
osf_so_ops.in_dynsym_resolve_code = osf_in_dynsym_resolve_code;
osf_so_ops.bfd_open = solib_bfd_open;
+ osf_so_ops.validate = solib_validate;

/* FIXME: Don't do this here. *_gdbarch_init() should set so_ops. */
current_target_so_ops = &osf_so_ops;
Index: gdb/solib-pa64.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-pa64.c,v
retrieving revision 1.39
diff -u -p -r1.39 solib-pa64.c
--- gdb/solib-pa64.c 9 Nov 2012 19:58:01 -0000 1.39
+++ gdb/solib-pa64.c 24 Dec 2012 19:37:16 -0000
@@ -623,6 +623,7 @@ _initialize_pa64_solib (void)
pa64_so_ops.open_symbol_file_object = pa64_open_symbol_file_object;
pa64_so_ops.in_dynsym_resolve_code = pa64_in_dynsym_resolve_code;
pa64_so_ops.bfd_open = solib_bfd_open;
+ pa64_so_ops.validate = solib_validate;

memset (&dld_cache, 0, sizeof (dld_cache));
}
Index: gdb/solib-som.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-som.c,v
retrieving revision 1.38
diff -u -p -r1.38 solib-som.c
--- gdb/solib-som.c 9 Nov 2012 19:58:01 -0000 1.38
+++ gdb/solib-som.c 24 Dec 2012 19:37:16 -0000
@@ -811,6 +811,7 @@ _initialize_som_solib (void)
som_so_ops.open_symbol_file_object = som_open_symbol_file_object;
som_so_ops.in_dynsym_resolve_code = som_in_dynsym_resolve_code;
som_so_ops.bfd_open = solib_bfd_open;
+ som_so_ops.validate = solib_validate;
}

void
Index: gdb/solib-spu.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-spu.c,v
retrieving revision 1.23
diff -u -p -r1.23 solib-spu.c
--- gdb/solib-spu.c 9 Nov 2012 19:58:01 -0000 1.23
+++ gdb/solib-spu.c 24 Dec 2012 19:37:16 -0000
@@ -519,6 +519,7 @@ set_spu_solib_ops (struct gdbarch *gdbar
spu_so_ops.current_sos = spu_current_sos;
spu_so_ops.bfd_open = spu_bfd_open;
spu_so_ops.lookup_lib_global_symbol = spu_lookup_lib_symbol;
+ spu_so_ops.validate = solib_validate;
}

set_solib_ops (gdbarch, &spu_so_ops);
Index: gdb/solib-sunos.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-sunos.c,v
retrieving revision 1.50
diff -u -p -r1.50 solib-sunos.c
--- gdb/solib-sunos.c 9 Nov 2012 19:58:01 -0000 1.50
+++ gdb/solib-sunos.c 24 Dec 2012 19:37:16 -0000
@@ -739,6 +739,7 @@ _initialize_sunos_solib (void)
sunos_so_ops.open_symbol_file_object = open_symbol_file_object;
sunos_so_ops.in_dynsym_resolve_code = sunos_in_dynsym_resolve_code;
sunos_so_ops.bfd_open = solib_bfd_open;
+ sunos_so_ops.validate = solib_validate;

/* FIXME: Don't do this here. *_gdbarch_init() should set so_ops. */
current_target_so_ops = &sunos_so_ops;
Index: gdb/solib-svr4.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-svr4.c,v
retrieving revision 1.171
diff -u -p -r1.171 solib-svr4.c
--- gdb/solib-svr4.c 13 Dec 2012 21:29:50 -0000 1.171
+++ gdb/solib-svr4.c 24 Dec 2012 19:37:16 -0000
@@ -51,6 +51,8 @@
static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
static int svr4_have_link_map_offsets (void);
static void svr4_relocate_main_executable (void);
+static gdb_byte *read_program_headers_from_bfd (bfd *abfd, int *phdrs_size);
+static gdb_byte *read_elf_header_from_bfd (bfd *abfd, int *bfd_ehdr_size);

/* Link map info to include in an allocated so_list entry. */

@@ -190,13 +192,16 @@ has_lm_dynamic_from_link_map (void)
}

static CORE_ADDR
-lm_addr_check (struct so_list *so, bfd *abfd)
+lm_addr_check (const struct so_list *so, bfd *abfd, int *const trusted)
{
- if (!so->lm_info->l_addr_p)
+ if (!so->lm_info->l_addr_p || trusted != NULL)
{
struct bfd_section *dyninfo_sect;
CORE_ADDR l_addr, l_dynaddr, dynaddr;

+ if (trusted != NULL)
+ *trusted = 1;
+
l_addr = so->lm_info->l_addr_inferior;

if (! abfd || ! has_lm_dynamic_from_link_map ())
@@ -281,6 +286,11 @@ lm_addr_check (struct so_list *so, bfd *
warning (_(".dynamic section for \"%s\" "
"is not at the expected address "
"(wrong library or version mismatch?)"), so->so_name);
+
+ /* l_addr is something we calculated. Didn't exactly
+ * match what is expected, and caller wants to know about it */
+ if (trusted != NULL)
+ *trusted = 0;
}
}

@@ -849,7 +859,6 @@ svr4_keep_data_in_core (CORE_ADDR vaddr,
CORE_ADDR ldsomap;
struct so_list *new;
struct cleanup *old_chain;
- struct link_map_offsets *lmo;
CORE_ADDR name_lm;

info = get_svr4_info ();
@@ -863,7 +872,6 @@ svr4_keep_data_in_core (CORE_ADDR vaddr,
if (!ldsomap)
return 0;

- lmo = svr4_fetch_link_map_offsets ();
new = XZALLOC (struct so_list);
old_chain = make_cleanup (xfree, new);
new->lm_info = lm_info_read (ldsomap);
@@ -874,6 +882,330 @@ svr4_keep_data_in_core (CORE_ADDR vaddr,
return (name_lm >= vaddr && name_lm < vaddr + size);
}

+
+/* Read elf header corresponding to SO from target memory.
+ Allocates memory for the returned value (which ought be
+ freed with xfree). Returns elf header size in *HDR_SIZE. */
+
+static gdb_byte *
+read_elf_header_from_target (const struct so_list *const so,
+ int *const hdr_size)
+{
+ int load_base_trusted = 0;
+ const CORE_ADDR so_base_addr = lm_addr_check (so, so->abfd,
+ &load_base_trusted);
+ const int ptr_size
+ = builtin_type (target_gdbarch ())->builtin_data_ptr->length;
+ const gdb_byte elf_magic[] = { 0x7f, 'E', 'L', 'F' };
+ int status;
+ gdb_byte *ret;
+
+ if (!load_base_trusted)
+ {
+ warning (_("Unable to load elf header from target as load base for %s could not be determined."),
+ so->abfd->filename);
+ return NULL;
+ }
+
+ if (!so->lm_info->l_addr_p)
+ {
+ warning (_("Unable to load elf header from target since shared object %s has not been relocated."),
+ so->abfd->filename);
+ return NULL;
+ }
+
+ *hdr_size = (ptr_size == 4)? sizeof (Elf32_External_Ehdr)
+ : sizeof (Elf64_External_Ehdr);
+
+ ret = xzalloc (*hdr_size);
+
+ if (ret == NULL)
+ {
+ warning (_("Unable to load elf header from target due to out of memory."));
+ return NULL;
+ }
+
+ if ((status = target_read_memory (so_base_addr, ret, *hdr_size)) != 0)
+ {
+ warning (_("Unable to load elf header from target due to failed memory read at address %s"),
+ paddress (target_gdbarch (), so_base_addr));
+ xfree (ret);
+ return NULL;
+ }
+
+ /* Verify that we have actually read the header. */
+ if (memcmp (elf_magic, ret, 4) != 0)
+ {
+ warning (_("Unable to load elf header from target due to elf magic string mismatch."));
+ xfree (ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+/* Read program headers from target memory for a given shared object.
+ SO must have been relocated and its addr_low correctly set. */
+
+static gdb_byte *
+read_program_headers_from_target (const struct so_list *const so,
+ int *const phdrs_size)
+{
+ int load_base_trusted = 0;
+ const CORE_ADDR so_base_addr = lm_addr_check (so, so->abfd,
+ &load_base_trusted);
+ const enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
+ const int ptr_size
+ = builtin_type (target_gdbarch ())->builtin_data_ptr->length;
+ union ElfXX_Ehdr
+ {
+ Elf32_External_Ehdr _32;
+ Elf64_External_Ehdr _64;
+ } ELF_EHDR;
+ unsigned phnum, phentsize, phoff; /* In our byte order */
+ int ehdr_size;
+ int status;
+ gdb_byte *e_phnum, *e_phentsize, *e_phoff;
+ int e_phnum_len, e_phentsize_len, e_phoff_len;
+ gdb_byte *ret;
+
+ if (!load_base_trusted)
+ {
+ warning (_("Unable to load program headers from target for %s as load base could not be determined."),
+ so->abfd->filename);
+ return NULL;
+ }
+
+ if (!so->lm_info->l_addr_p)
+ {
+ warning (_("Unable to load program headers from target since shared object %s has not been relocated."), so->abfd->filename);
+ return NULL;
+ }
+
+ ret = read_elf_header_from_target (so, &ehdr_size);
+
+ if (ret == NULL)
+ return NULL;
+
+ memcpy (&ELF_EHDR, ret, ehdr_size);
+ xfree (ret);
+
+ if (ptr_size == 4)
+ {
+ e_phnum = (gdb_byte *) &ELF_EHDR._32.e_phnum;
+ e_phnum_len = sizeof (ELF_EHDR._32.e_phnum);
+ e_phentsize = (gdb_byte *) &ELF_EHDR._32.e_phentsize;
+ e_phentsize_len = sizeof (ELF_EHDR._32.e_phentsize);
+ e_phoff = (gdb_byte *) &ELF_EHDR._32.e_phoff;
+ e_phoff_len = sizeof (ELF_EHDR._32.e_phoff);
+ }
+ else
+ {
+ e_phnum = (gdb_byte *) &ELF_EHDR._64.e_phnum;
+ e_phnum_len = sizeof (ELF_EHDR._64.e_phnum);
+ e_phentsize = (gdb_byte *) &ELF_EHDR._64.e_phentsize;
+ e_phentsize_len = sizeof (ELF_EHDR._64.e_phentsize);
+ e_phoff = (gdb_byte *) &ELF_EHDR._64.e_phoff;
+ e_phoff_len = sizeof (ELF_EHDR._64.e_phoff);
+ }
+
+ phnum = extract_unsigned_integer (e_phnum, e_phnum_len, byte_order);
+ phentsize = extract_unsigned_integer (e_phentsize, e_phentsize_len,
+ byte_order);
+ phoff = extract_unsigned_integer (e_phoff, e_phoff_len, byte_order);
+
+ *phdrs_size = phnum * phentsize;
+
+ ret = xzalloc (*phdrs_size);
+ if (ret == NULL)
+ {
+ warning (_("Unable to load target pheaders due to out of memory."));
+ return NULL;
+ }
+
+ if ((status = target_read_memory (so_base_addr + phoff, ret, *phdrs_size))
+ != 0)
+ {
+ memory_error (status, so_base_addr + phoff);
+ xfree (ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+static int
+svr4_validate_ehdr_match (const gdb_byte *const pehdr1,
+ const gdb_byte *const pehdr2)
+{
+ int ok = 1;
+ const int ptr_size
+ = builtin_type (target_gdbarch ())->builtin_data_ptr->length;
+ const union ElfXX_Ehdr
+ {
+ Elf32_External_Ehdr _32;
+ Elf64_External_Ehdr _64;
+ } *ehdr1, *ehdr2;
+
+ ehdr1 = (void*)pehdr1;
+ ehdr2 = (void*)pehdr2;
+
+ /* Magic */
+ ok = ok && memcmp (ehdr1, ehdr2, 4) == 0;
+
+ /* Compare fields that must match. Endian-ess does not matter as
+ both headers must be for the same architecture. */
+ if (ptr_size == 4)
+ {
+#define check(fld) \
+ ok = ok && memcmp (ehdr1->_32.fld, ehdr2->_32.fld, \
+ sizeof(ehdr1->_32.fld)) == 0
+ check (e_type);
+ check (e_machine);
+ check (e_version);
+ check (e_entry);
+ check (e_phoff);
+ check (e_flags);
+ check (e_ehsize);
+ check (e_phentsize);
+#undef check
+ }
+ else
+ {
+#define check(fld) \
+ ok = ok && memcmp (ehdr1->_64.fld, ehdr2->_64.fld, \
+ sizeof(ehdr1->_64.fld)) == 0
+ check (e_type);
+ check (e_machine);
+ check (e_version);
+ check (e_entry);
+ check (e_phoff);
+ check (e_flags);
+ check (e_ehsize);
+ check (e_phentsize);
+#undef check
+ }
+ return ok;
+}
+
+static int
+svr4_validate_phdr_match (const gdb_byte *const pphdr1,
+ const gdb_byte *const pphdr2, const int phdr_size)
+{
+ int ok = 1;
+ const int ptr_size
+ = builtin_type (target_gdbarch ())->builtin_data_ptr->length;
+ const union ElfXX_Phdr
+ {
+ Elf32_External_Phdr _32;
+ Elf64_External_Phdr _64;
+ } *phdr1, *phdr2;
+
+ phdr1 = (void*)pphdr1;
+ phdr2 = (void*)pphdr2;
+
+ /* Check fields of pheaders that remain the same even for
+ objcopy --only-keep-debug */
+
+ /* Compare fields that must match. Endian-ess does not matter as
+ both headers must be for the same architecture. */
+ if (ptr_size == 4)
+ {
+#define check(fld) \
+ ok = ok && memcmp (phdr1->_32.fld, phdr2->_32.fld, \
+ sizeof(phdr1->_32.fld)) == 0
+ check (p_type);
+ check (p_flags);
+ check (p_vaddr);
+ check (p_memsz);
+ check (p_align);
+#undef check
+ }
+ else
+ {
+#define check(fld) \
+ ok = ok && memcmp (phdr1->_64.fld, phdr2->_64.fld, \
+ sizeof(phdr1->_64.fld)) == 0
+ check (p_type);
+ check (p_flags);
+ check (p_vaddr);
+ check (p_memsz);
+ check (p_align);
+#undef check
+ }
+ return ok;
+}
+
+/* Validate elf header and phdrs of SO->ABFD and in-memory match. */
+
+static int
+svr4_validate_hdrs_match (const struct so_list *const so)
+{
+ int mem_ehdr_size, bfd_ehdr_size;
+ int mem_phdrs_size, bfd_phdrs_size;
+ gdb_byte *mem_ehdr, *mem_phdrs = NULL;
+ gdb_byte *bfd_ehdr = NULL, *bfd_phdrs = NULL;
+ int load_base_trusted = 0;
+ int ok = 1;
+
+ (void)lm_addr_check (so, so->abfd, &load_base_trusted);
+
+ if (!load_base_trusted)
+ return 0;
+
+ mem_ehdr = read_elf_header_from_target (so, &mem_ehdr_size);
+ if (mem_ehdr)
+ mem_phdrs = read_program_headers_from_target (so, &mem_phdrs_size);
+ if (mem_phdrs)
+ bfd_ehdr = read_elf_header_from_bfd (so->abfd, &bfd_ehdr_size);
+ if (bfd_ehdr)
+ bfd_phdrs = read_program_headers_from_bfd (so->abfd, &bfd_phdrs_size);
+
+ if (bfd_phdrs != NULL)
+ {
+ /* Finally, check. Since both memory and file representations
+ must match, it is o.k. to bit compare headers. */
+ if (bfd_ehdr_size != mem_ehdr_size
+ || svr4_validate_ehdr_match (mem_ehdr, bfd_ehdr) == 0)
+ {
+ warning (_("Elf header does not match for %s"),
+ so->abfd->filename);
+ ok = 0;
+ }
+ else if (bfd_phdrs_size != mem_phdrs_size
+ || svr4_validate_phdr_match (mem_phdrs, bfd_phdrs,
+ mem_phdrs_size) == 0)
+ {
+ warning (_("Program headers do not match for %s"),
+ so->abfd->filename);
+ ok = 0;
+ }
+ }
+ else
+ warning (_("Unable to validate binary match for %s"), so->abfd->filename);
+
+ xfree (bfd_phdrs);
+ xfree (bfd_ehdr);
+ xfree (mem_phdrs);
+ xfree (mem_ehdr);
+
+ return ok;
+}
+
+/* Validate SO by checking whether opened file matches
+ in-memory object. */
+
+static int
+svr4_validate(const struct so_list *const so)
+{
+ gdb_assert (so != NULL);
+ gdb_assert (so->abfd != NULL);
+
+
+ return svr4_validate_hdrs_match (so);
+}
+
/* Implement the "open_symbol_file_object" target_so_ops method.

If no open symbol file, attempt to locate and open the main symbol
@@ -1176,7 +1508,6 @@ svr4_read_so_list (CORE_ADDR lm, struct

for (; lm != 0; prev_lm = lm, lm = next_lm)
{
- struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
struct so_list *new;
struct cleanup *old_chain;
int errcode;
@@ -1577,7 +1908,7 @@ enable_break (struct svr4_info *info, in
{
load_addr_found = 1;
loader_found_in_list = 1;
- load_addr = lm_addr_check (so, tmp_bfd);
+ load_addr = lm_addr_check (so, tmp_bfd, NULL);
break;
}
so = so->next;
@@ -1741,6 +2072,36 @@ svr4_special_symbol_handling (void)
/* Nothing to do. */
}

+/* Read the ELF header from ABFD. Return the raw contents and
+ set *ELFHDR_SIZE to the size of the elf header. */
+
+static gdb_byte *
+read_elf_header_from_bfd (bfd *const abfd, int *const bfd_ehdr_size)
+{
+ const int ptr_size
+ = builtin_type (target_gdbarch ())->builtin_data_ptr->length;
+ const int ehdr_size = (ptr_size == 4)? sizeof (Elf32_External_Ehdr)
+ : sizeof (Elf64_External_Ehdr);
+ gdb_byte *const bfd_ehdr = xmalloc (ehdr_size);
+
+ if (bfd_ehdr == NULL)
+ {
+ warning (_("Unable to read raw elf header due to out of memory."));
+ return NULL;
+ }
+
+ *bfd_ehdr_size = ehdr_size;
+
+ if (bfd_seek (abfd, 0, SEEK_SET) != 0
+ || bfd_bread (bfd_ehdr, ehdr_size, abfd) != ehdr_size)
+ {
+ warning (_("Unable to read raw elf header due to failed file operation."));
+ xfree (bfd_ehdr);
+ return NULL;
+ }
+ return bfd_ehdr;
+}
+
/* Read the ELF program headers from ABFD. Return the contents and
set *PHDRS_SIZE to the size of the program headers. */

@@ -2279,9 +2640,11 @@ svr4_relocate_section_addresses (struct
struct target_section *sec)
{
sec->addr = svr4_truncate_ptr (sec->addr + lm_addr_check (so,
- sec->bfd));
+ sec->bfd,
+ NULL));
sec->endaddr = svr4_truncate_ptr (sec->endaddr + lm_addr_check (so,
- sec->bfd));
+ sec->bfd,
+ NULL));
}
\f

@@ -2462,4 +2825,5 @@ _initialize_svr4_solib (void)
svr4_so_ops.lookup_lib_global_symbol = elf_lookup_lib_symbol;
svr4_so_ops.same = svr4_same;
svr4_so_ops.keep_data_in_core = svr4_keep_data_in_core;
+ svr4_so_ops.validate = svr4_validate;
}
Index: gdb/solib-target.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-target.c,v
retrieving revision 1.25
diff -u -p -r1.25 solib-target.c
--- gdb/solib-target.c 14 Dec 2012 23:27:13 -0000 1.25
+++ gdb/solib-target.c 24 Dec 2012 19:37:16 -0000
@@ -25,6 +25,7 @@
#include "target.h"
#include "vec.h"
#include "solib-target.h"
+#include "solib.h"

#include "gdb_string.h"

@@ -501,6 +502,7 @@ _initialize_solib_target (void)
solib_target_so_ops.in_dynsym_resolve_code
= solib_target_in_dynsym_resolve_code;
solib_target_so_ops.bfd_open = solib_bfd_open;
+ solib_target_so_ops.validate = solib_validate;

/* Set current_target_so_ops to solib_target_so_ops if not already
set. */
Index: gdb/solib.c
===================================================================
RCS file: /cvs/src/src/gdb/solib.c,v
retrieving revision 1.168
diff -u -p -r1.168 solib.c
--- gdb/solib.c 9 Nov 2012 19:58:01 -0000 1.168
+++ gdb/solib.c 24 Dec 2012 19:37:18 -0000
@@ -495,6 +495,15 @@ solib_map_sections (struct so_list *so)
}
}

+ if (!ops->validate (so))
+ {
+ warning (_("Shared object could not be validated and will be ignored: %s."),
+ so->abfd->filename);
+ gdb_bfd_unref (so->abfd);
+ so->abfd = NULL;
+ return 0;
+ }
+
/* Add the shared object's sections to the current set of file
section tables. Do this immediately after mapping the object so
that later nodes in the list can query this object, as is needed
@@ -1448,6 +1457,14 @@ gdb_bfd_lookup_symbol (bfd *abfd,
return symaddr;
}

+/* Default implementation does not perform any validation. */
+
+int
+solib_validate (const struct so_list *const so)
+{
+ return 1; /* No validation. */
+}
+
extern initialize_file_ftype _initialize_solib; /* -Wmissing-prototypes */

void
Index: gdb/solib.h
===================================================================
RCS file: /cvs/src/src/gdb/solib.h,v
retrieving revision 1.33
diff -u -p -r1.33 solib.h
--- gdb/solib.h 3 Feb 2012 15:19:37 -0000 1.33
+++ gdb/solib.h 24 Dec 2012 19:37:18 -0000
@@ -91,4 +91,8 @@ extern CORE_ADDR gdb_bfd_lookup_symbol_f
void *),
void *data);

+/* Default validation always returns 1. */
+
+extern int solib_validate (const struct so_list *so);
+
#endif /* SOLIB_H */
Index: gdb/solist.h
===================================================================
RCS file: /cvs/src/src/gdb/solist.h,v
retrieving revision 1.37
diff -u -p -r1.37 solist.h
--- gdb/solist.h 4 Jan 2012 08:17:11 -0000 1.37
+++ gdb/solist.h 24 Dec 2012 19:37:18 -0000
@@ -149,6 +149,11 @@ struct target_so_ops
core file (in particular, for readonly sections). */
int (*keep_data_in_core) (CORE_ADDR vaddr,
unsigned long size);
+
+
+ /* Return 0 if SO does not match target SO it is supposed to
+ represent. Return 1 otherwise. */
+ int (*validate) (const struct so_list *so);
};

/* Free the memory associated with a (so_list *). */
Index: gdb/testsuite/gdb.base/solib-mismatch-lib.c
===================================================================
RCS file: gdb/testsuite/gdb.base/solib-mismatch-lib.c
diff -N gdb/testsuite/gdb.base/solib-mismatch-lib.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/solib-mismatch-lib.c 24 Dec 2012 19:37:18 -0000
@@ -0,0 +1,11 @@
+int _bar = 42;
+
+int bar(void)
+{
+ return _bar + 21;
+}
+
+int foo(void)
+{
+ return _bar;
+}
Index: gdb/testsuite/gdb.base/solib-mismatch-libmod.c
===================================================================
RCS file: gdb/testsuite/gdb.base/solib-mismatch-libmod.c
diff -N gdb/testsuite/gdb.base/solib-mismatch-libmod.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/solib-mismatch-libmod.c 24 Dec 2012 19:37:18 -0000
@@ -0,0 +1,12 @@
+
+int _bar = 21;
+
+int bar(void)
+{
+ return 42 - _bar;
+}
+
+int foo(void)
+{
+ return 24 + bar();
+}
Index: gdb/testsuite/gdb.base/solib-mismatch-libmod2.c
===================================================================
RCS file: gdb/testsuite/gdb.base/solib-mismatch-libmod2.c
diff -N gdb/testsuite/gdb.base/solib-mismatch-libmod2.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/solib-mismatch-libmod2.c 24 Dec 2012 19:37:18 -0000
@@ -0,0 +1,11 @@
+
+int bar(void)
+{
+ return 42;
+}
+
+int foo(void)
+{
+ return 24;
+}
+
Index: gdb/testsuite/gdb.base/solib-mismatch.c
===================================================================
RCS file: gdb/testsuite/gdb.base/solib-mismatch.c
diff -N gdb/testsuite/gdb.base/solib-mismatch.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/solib-mismatch.c 24 Dec 2012 19:37:18 -0000
@@ -0,0 +1,25 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+
+#define lib "solib-mismatch.so"
+
+
+int main(int argc, char *argv[])
+{
+ void *h = dlopen(lib, RTLD_NOW);
+ int (*foo)(void);
+
+ if (h == NULL)
+ {
+ printf("ERROR - could not open lib %s\n", lib);
+ return 1;
+ }
+ foo = dlsym(h, "foo");
+ printf("foo says: %d\n", (*foo)());
+ raise(SIGSTOP); /* Let gdb attach */
+ dlclose(h);
+ return 0;
+}
+
Index: gdb/testsuite/gdb.base/solib-mismatch.exp
===================================================================
RCS file: gdb/testsuite/gdb.base/solib-mismatch.exp
diff -N gdb/testsuite/gdb.base/solib-mismatch.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/solib-mismatch.exp 24 Dec 2012 19:37:18 -0000
@@ -0,0 +1,190 @@
+# Copyright 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+# are we on a target board
+set test "solib-mismatch"
+set testfile "solib-mismatch"
+
+# Test overview:
+# generate two shared objects. One that will be used by the process
+# and another, modified, that will be found by gdb. Gdb should
+# detect the mismatch and refuse to use mismatched shared object.
+
+# First version of the object, to be loaded by ld
+set srclibfilerun ${testfile}-lib.c
+# Modified version of the object to be loaded by gdb
+# Code in -libmod.c is tuned so it gives a mismatch but
+# leaves .dynamic at the same point.
+set srclibfilegdb ${testfile}-libmod.c
+# Modified version of the object to be loaded by gdb tuned
+# in such a way that .dynamic moves as well.
+set srclibfilegdb2 ${testfile}-libmod2.c
+
+# So file name:
+set binlibfilebase ${testfile}.so
+
+# Setup run directory (where program is run from)
+# It contains executable and '-lib' version of the library.
+set binlibfiledirrun ${objdir}/${subdir}/lib${testfile}
+set binlibfilerun ${binlibfiledirrun}/${binlibfilebase}
+
+# Second solib version is in current directory, '-libmod' version.
+set binlibfiledirgdb ${objdir}/${subdir}
+set binlibfilegdb ${binlibfiledirgdb}/${binlibfilebase}
+set binlibfilegdb2 "${binlibfiledirgdb}/${binlibfilebase}-2"
+
+# Executeable
+set srcfile ${testfile}.c
+set executable ${testfile}
+set objfile ${objdir}/${subdir}/${executable}.o
+set binfile ${objdir}/${subdir}/${executable}
+
+file mkdir "${binlibfiledirrun}"
+
+set exec_opts [list debug additional_flags=-ldl]
+
+if [istarget "*-*-nto-*"] {
+ set exec_opts {}
+}
+
+# build the first test case
+if { [get_compiler_info]
+ || [gdb_compile_shlib "${srcdir}/${subdir}/${srclibfilerun}" "${binlibfilerun}" [list debug ldflags=-Wl,-soname,${binlibfilebase}]] != ""
+ || [gdb_gnu_strip_debug $binlibfilerun]
+ || [gdb_compile_shlib "${srcdir}/${subdir}/${srclibfilegdb}" "${binlibfilegdb}" [list debug ldflags=-Wl,-soname,${binlibfilebase}]] != ""
+ || [gdb_compile_shlib "${srcdir}/${subdir}/${srclibfilegdb2}" "${binlibfilegdb2}" [list debug ldflags=-Wl,-soname,${binlibfilebase}]] != ""
+ || [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${objfile}" object {debug}] != ""
+ || [gdb_compile "${objfile}" "${binfile}" executable ${exec_opts}] != "" } {
+ untested ${testfile}.exp
+ return -1
+}
+
+# Start with a fresh gdb
+
+# Start the exe. It will raise SIGSTOP on itself.
+send_user "Exec: $executable\r\n"
+set curdir [eval pwd]
+set dummy [eval cd "${binlibfiledirrun}"]
+set testpid [eval exec "../${executable}" &]
+send_user "You can now attach to $testpid\r\n"
+
+set dummy [eval cd "${binlibfiledirgdb}"]
+
+proc solib_matching_test { solibfile symsloaded } {
+ global gdb_prompt
+ global testpid
+ global test
+ global executable
+ global srcdir
+ global subdir
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load ${executable}
+
+ send_gdb "set auto-solib-add off\r\n"
+ gdb_expect {
+ -re "${gdb_prompt} $" {
+ # Nothing, just drain the buffer
+ }
+ }
+
+ # Test unstripped, .dynamic matching so
+ send_gdb "attach ${testpid}\r\n"
+ gdb_expect {
+ -re "Attaching to program:.*${executable}, process ${testpid}.*${gdb_prompt} $" {
+# Nothing
+ }
+ default {
+ untested "${test}: Could not attach to ${testpid}"
+ return -1
+ }
+ }
+
+ gdb_test_multiple "sharedlibrary" $test {
+ -re ".*${gdb_prompt} $" {
+ pass "Validate library detects mismatch"
+ }
+ default {
+ fail "${test}: sharedlibrary failure"
+ }
+ }
+
+ gdb_test "info sharedlibrary ${solibfile}" \
+ ".*From.*To.*Syms.*\r\n0x\[0-9a-f\]+.*0x\[0-9a-f\]+.*${symsloaded}.*${solibfile}" \
+ "Symbols for ${solibfile} loaded: expected ${symsloaded}"
+
+ send_gdb "detach\r\n"
+ gdb_expect {
+ -re ".*Detaching from program.*${executable}.*${gdb_prompt} $" {
+#Nothing, just drain the buffer
+ }
+ default {
+ untested "${test}: Could not detach from ${testpid}"
+ return -1
+ }
+ }
+ return 0
+}
+
+# Test unstripped, .dynamic matching
+solib_matching_test "${binlibfilebase}" "No"
+
+# Test --only-keep-debug, .dynamic matching so
+send_user "test --only-keep-debug"
+set objcopy_program [transform objcopy]
+set result [catch "exec $objcopy_program --only-keep-debug ${binlibfilegdb}"]
+if {$result != 0} {
+ untested "${test} test --only-keep-debug"
+ return -1
+}
+solib_matching_test "${binlibfilebase}" "No"
+
+# Test version -2 of mismatched library, the one w
+file copy -force "${binlibfilegdb2}" "${binlibfilegdb}"
+solib_matching_test ${binlibfilebase} "No"
+# Test --only-keep-debug, .dynamic matching so
+send_user "test --only-keep-debug"
+set objcopy_program [transform objcopy]
+set result [catch "exec $objcopy_program --only-keep-debug ${binlibfilegdb}"]
+if {$result != 0} {
+ untested "${test} test --only-keep-debug"
+ return -1
+}
+solib_matching_test "${binlibfilebase}" "No"
+
+# Now test it does not mis-validate matching libraries
+if { [gdb_compile_shlib "${curdir}/${srcdir}/${subdir}/${srclibfilerun}" "${binlibfilegdb}" [list debug ldflags=-Wl,-soname,${binlibfilebase}]] != "" } {
+ untested "${testfile}.exp: failed to build shared object?"
+ return -1
+}
+solib_matching_test "${binlibfilebase}" "Yes"
+if { [gdb_gnu_strip_debug $binlibfilegdb] } {
+ untested "${testfile}.exp: Strip failed. Further test impossible."
+ return -1
+}
+solib_matching_test "${binlibfilebase}" "Yes"
+if { [gdb_compile_shlib "${curdir}/${srcdir}/${subdir}/${srclibfilerun}" "${binlibfilegdb}" [list debug ldflags=-Wl,-soname,${binlibfilebase}]] != "" } {
+ untested "${testfile}.exp: failed to build shared object?"
+ return -1
+}
+set result [catch "exec $objcopy_program --only-keep-debug ${binlibfilegdb}"]
+if {$result != 0} {
+ untested "${test} test --only-keep-debug - matching lib"
+ return -1
+}
+solib_matching_test "${binlibfilebase}" "Yes"
+