From 5979d6b69b20a8355ea94b75fad97415fce4788c Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Wed, 2 Apr 2014 12:07:33 +1030 Subject: [PATCH] Handle VDSO section headers past end of page When a VDSO gets large enough that it doesn't entirely fit in one page, but not so large that the part described by the program header exceeds one page, then gdb/BFD doesn't read the section headers and symbol table information. This patch cures that by passing the size of the vdso to BFD, and fixes a number of other issues in the BFD code. bfd/ * elfcode.h (bfd_from_remote_memory): Add "size" parameter. Consolidate code handling possible section headers past end of segment. Don't use p_align for page size guess, instead use minpagesize. Take note of ld.so clearing section headers when p_memsz > p_filesz. Handle file header specifying no section headers. Handle zero p_align throughout. Default loadbase to zero. Add comments. Rename contents_size to high_offset, and make it a bfd_vma. Delete unnecessary bfd_set_error calls. * bfd-in.h (bfd_elf_bfd_from_remote_memory): Update prototpe. * elf-bfd.h (struct elf_backend_data ): Likewise. (_bfd_elf32_bfd_from_remote_memory): Likewise. (_bfd_elf64_bfd_from_remote_memory): Likewise. * elf.c (bfd_elf_bfd_from_remote_memory): Adjust. * bfd-in2.h: Regnerate. gdb/ * symfile-mem.c (symbol_file_add_from_memory): Add size parameter. Pass to bfd_elf_bfd_from_remote_memory. Adjust all callers. (struct symbol_file_add_from_memory_args): Add size field. (find_vdso_size): New function. (add_vsyscall_page): Attempt to find vdso size. --- bfd/ChangeLog | 18 ++++++ bfd/bfd-in.h | 26 ++++---- bfd/bfd-in2.h | 26 ++++---- bfd/elf-bfd.h | 10 +-- bfd/elf.c | 3 +- bfd/elfcode.h | 153 ++++++++++++++++++++++++++++------------------ gdb/ChangeLog | 8 +++ gdb/symfile-mem.c | 38 +++++++++--- 8 files changed, 184 insertions(+), 98 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 442f8ada48..5c7b2b301e 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,21 @@ +2014-04-02 Alan Modra + + * elfcode.h (bfd_from_remote_memory): Add "size" parameter. + Consolidate code handling possible section headers past end of + segment. Don't use p_align for page size guess, instead use + minpagesize. Take note of ld.so clearing section headers when + p_memsz > p_filesz. Handle file header specifying no section + headers. Handle zero p_align throughout. Default loadbase to + zero. Add comments. Rename contents_size to high_offset, and + make it a bfd_vma. Delete unnecessary bfd_set_error calls. + * bfd-in.h (bfd_elf_bfd_from_remote_memory): Update prototpe. + * elf-bfd.h (struct elf_backend_data ): + Likewise. + (_bfd_elf32_bfd_from_remote_memory): Likewise. + (_bfd_elf64_bfd_from_remote_memory): Likewise. + * elf.c (bfd_elf_bfd_from_remote_memory): Adjust. + * bfd-in2.h: Regnerate. + 2014-04-01 Tristan Gingold * mach-o.c (bfd_mach_o_canonicalize_one_reloc): Avoid to crash diff --git a/bfd/bfd-in.h b/bfd/bfd-in.h index da350a51a8..ddc8270b75 100644 --- a/bfd/bfd-in.h +++ b/bfd/bfd-in.h @@ -680,19 +680,21 @@ extern int bfd_get_elf_phdrs (bfd *abfd, void *phdrs); /* Create a new BFD as if by bfd_openr. Rather than opening a file, - reconstruct an ELF file by reading the segments out of remote memory - based on the ELF file header at EHDR_VMA and the ELF program headers it - points to. If not null, *LOADBASEP is filled in with the difference - between the VMAs from which the segments were read, and the VMAs the - file headers (and hence BFD's idea of each section's VMA) put them at. - - The function TARGET_READ_MEMORY is called to copy LEN bytes from the - remote memory at target address VMA into the local buffer at MYADDR; it - should return zero on success or an `errno' code on failure. TEMPL must - be a BFD for an ELF target with the word size and byte order found in - the remote memory. */ + reconstruct an ELF file by reading the segments out of remote + memory based on the ELF file header at EHDR_VMA and the ELF program + headers it points to. If non-zero, SIZE is the known extent of the + object. If not null, *LOADBASEP is filled in with the difference + between the VMAs from which the segments were read, and the VMAs + the file headers (and hence BFD's idea of each section's VMA) put + them at. + + The function TARGET_READ_MEMORY is called to copy LEN bytes from + the remote memory at target address VMA into the local buffer at + MYADDR; it should return zero on success or an `errno' code on + failure. TEMPL must be a BFD for a target with the word size and + byte order found in the remote memory. */ extern bfd *bfd_elf_bfd_from_remote_memory - (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep, + (bfd *templ, bfd_vma ehdr_vma, size_t size, bfd_vma *loadbasep, int (*target_read_memory) (bfd_vma vma, bfd_byte *myaddr, bfd_size_type len)); diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index f08b578501..8cfae51069 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -687,19 +687,21 @@ extern int bfd_get_elf_phdrs (bfd *abfd, void *phdrs); /* Create a new BFD as if by bfd_openr. Rather than opening a file, - reconstruct an ELF file by reading the segments out of remote memory - based on the ELF file header at EHDR_VMA and the ELF program headers it - points to. If not null, *LOADBASEP is filled in with the difference - between the VMAs from which the segments were read, and the VMAs the - file headers (and hence BFD's idea of each section's VMA) put them at. - - The function TARGET_READ_MEMORY is called to copy LEN bytes from the - remote memory at target address VMA into the local buffer at MYADDR; it - should return zero on success or an `errno' code on failure. TEMPL must - be a BFD for an ELF target with the word size and byte order found in - the remote memory. */ + reconstruct an ELF file by reading the segments out of remote + memory based on the ELF file header at EHDR_VMA and the ELF program + headers it points to. If non-zero, SIZE is the known extent of the + object. If not null, *LOADBASEP is filled in with the difference + between the VMAs from which the segments were read, and the VMAs + the file headers (and hence BFD's idea of each section's VMA) put + them at. + + The function TARGET_READ_MEMORY is called to copy LEN bytes from + the remote memory at target address VMA into the local buffer at + MYADDR; it should return zero on success or an `errno' code on + failure. TEMPL must be a BFD for a target with the word size and + byte order found in the remote memory. */ extern bfd *bfd_elf_bfd_from_remote_memory - (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep, + (bfd *templ, bfd_vma ehdr_vma, size_t size, bfd_vma *loadbasep, int (*target_read_memory) (bfd_vma vma, bfd_byte *myaddr, bfd_size_type len)); diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index ee6e6cbcd5..6d07303246 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -1191,9 +1191,9 @@ struct elf_backend_data /* This function implements `bfd_elf_bfd_from_remote_memory'; see elf.c, elfcode.h. */ bfd *(*elf_backend_bfd_from_remote_memory) - (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep, - int (*target_read_memory) (bfd_vma vma, bfd_byte *myaddr, - bfd_size_type len)); + (bfd *templ, bfd_vma ehdr_vma, size_t size, bfd_vma *loadbasep, + int (*target_read_memory) (bfd_vma vma, bfd_byte *myaddr, + bfd_size_type len)); /* This function is used by `_bfd_elf_get_synthetic_symtab'; see elf.c. */ @@ -2334,10 +2334,10 @@ extern char *elfcore_write_ppc_linux_prpsinfo32 (bfd *, char *, int *, const struct elf_internal_linux_prpsinfo *); extern bfd *_bfd_elf32_bfd_from_remote_memory - (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep, + (bfd *templ, bfd_vma ehdr_vma, size_t size, bfd_vma *loadbasep, int (*target_read_memory) (bfd_vma, bfd_byte *, bfd_size_type)); extern bfd *_bfd_elf64_bfd_from_remote_memory - (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep, + (bfd *templ, bfd_vma ehdr_vma, size_t size, bfd_vma *loadbasep, int (*target_read_memory) (bfd_vma, bfd_byte *, bfd_size_type)); extern bfd_vma bfd_elf_obj_attr_size (bfd *); diff --git a/bfd/elf.c b/bfd/elf.c index 3ded683efe..d67b917c68 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -9908,11 +9908,12 @@ bfd * bfd_elf_bfd_from_remote_memory (bfd *templ, bfd_vma ehdr_vma, + size_t size, bfd_vma *loadbasep, int (*target_read_memory) (bfd_vma, bfd_byte *, bfd_size_type)) { return (*get_elf_backend_data (templ)->elf_backend_bfd_from_remote_memory) - (templ, ehdr_vma, loadbasep, target_read_memory); + (templ, ehdr_vma, size, loadbasep, target_read_memory); } long diff --git a/bfd/elfcode.h b/bfd/elfcode.h index 20101bef9f..f8400659ef 100644 --- a/bfd/elfcode.h +++ b/bfd/elfcode.h @@ -1587,37 +1587,40 @@ elf_debug_file (Elf_Internal_Ehdr *ehdrp) #endif /* Create a new BFD as if by bfd_openr. Rather than opening a file, - reconstruct an ELF file by reading the segments out of remote memory - based on the ELF file header at EHDR_VMA and the ELF program headers it - points to. If not null, *LOADBASEP is filled in with the difference - between the VMAs from which the segments were read, and the VMAs the - file headers (and hence BFD's idea of each section's VMA) put them at. - - The function TARGET_READ_MEMORY is called to copy LEN bytes from the - remote memory at target address VMA into the local buffer at MYADDR; it - should return zero on success or an `errno' code on failure. TEMPL must - be a BFD for a target with the word size and byte order found in the - remote memory. */ + reconstruct an ELF file by reading the segments out of remote + memory based on the ELF file header at EHDR_VMA and the ELF program + headers it points to. If non-zero, SIZE is the known extent of the + object. If not null, *LOADBASEP is filled in with the difference + between the VMAs from which the segments were read, and the VMAs + the file headers (and hence BFD's idea of each section's VMA) put + them at. + + The function TARGET_READ_MEMORY is called to copy LEN bytes from + the remote memory at target address VMA into the local buffer at + MYADDR; it should return zero on success or an `errno' code on + failure. TEMPL must be a BFD for a target with the word size and + byte order found in the remote memory. */ bfd * NAME(_bfd_elf,bfd_from_remote_memory) (bfd *templ, bfd_vma ehdr_vma, + size_t size, bfd_vma *loadbasep, int (*target_read_memory) (bfd_vma, bfd_byte *, bfd_size_type)) { Elf_External_Ehdr x_ehdr; /* Elf file header, external form */ Elf_Internal_Ehdr i_ehdr; /* Elf file header, internal form */ Elf_External_Phdr *x_phdrs; - Elf_Internal_Phdr *i_phdrs, *last_phdr; + Elf_Internal_Phdr *i_phdrs, *last_phdr, *first_phdr; bfd *nbfd; struct bfd_in_memory *bim; - int contents_size; bfd_byte *contents; int err; unsigned int i; + bfd_vma high_offset; + bfd_vma shdr_end; bfd_vma loadbase; - bfd_boolean loadbase_set; /* Read in the ELF header in external format. */ err = target_read_memory (ehdr_vma, (bfd_byte *) &x_ehdr, sizeof x_ehdr); @@ -1677,10 +1680,7 @@ NAME(_bfd_elf,bfd_from_remote_memory) x_phdrs = (Elf_External_Phdr *) bfd_malloc (i_ehdr.e_phnum * (sizeof *x_phdrs + sizeof *i_phdrs)); if (x_phdrs == NULL) - { - bfd_set_error (bfd_error_no_memory); - return NULL; - } + return NULL; err = target_read_memory (ehdr_vma + i_ehdr.e_phoff, (bfd_byte *) x_phdrs, i_ehdr.e_phnum * sizeof x_phdrs[0]); if (err) @@ -1692,34 +1692,44 @@ NAME(_bfd_elf,bfd_from_remote_memory) } i_phdrs = (Elf_Internal_Phdr *) &x_phdrs[i_ehdr.e_phnum]; - contents_size = 0; + high_offset = 0; + loadbase = 0; + first_phdr = NULL; last_phdr = NULL; - loadbase = ehdr_vma; - loadbase_set = FALSE; for (i = 0; i < i_ehdr.e_phnum; ++i) { elf_swap_phdr_in (templ, &x_phdrs[i], &i_phdrs[i]); if (i_phdrs[i].p_type == PT_LOAD) { - bfd_vma segment_end; - segment_end = (i_phdrs[i].p_offset + i_phdrs[i].p_filesz - + i_phdrs[i].p_align - 1) & -i_phdrs[i].p_align; - if (segment_end > (bfd_vma) contents_size) - contents_size = segment_end; - - /* LOADADDR is the `Base address' from the gELF specification: - `lowest p_vaddr value for a PT_LOAD segment' is P_VADDR from the - first PT_LOAD as PT_LOADs are ordered by P_VADDR. */ - if (!loadbase_set && (i_phdrs[i].p_offset & -i_phdrs[i].p_align) == 0) + bfd_vma segment_end = i_phdrs[i].p_offset + i_phdrs[i].p_filesz; + + if (segment_end > high_offset) { - loadbase = ehdr_vma - (i_phdrs[i].p_vaddr & -i_phdrs[i].p_align); - loadbase_set = TRUE; + high_offset = segment_end; + last_phdr = &i_phdrs[i]; } - last_phdr = &i_phdrs[i]; + /* If this program header covers offset zero, where the file + header sits, then we can figure out the loadbase. */ + if (first_phdr == NULL) + { + bfd_vma p_offset = i_phdrs[i].p_offset; + bfd_vma p_vaddr = i_phdrs[i].p_vaddr; + + if (i_phdrs[i].p_align > 1) + { + p_offset &= -i_phdrs[i].p_align; + p_vaddr &= -i_phdrs[i].p_align; + } + if (p_offset == 0) + { + loadbase = ehdr_vma - p_vaddr; + first_phdr = &i_phdrs[i]; + } + } } } - if (last_phdr == NULL) + if (high_offset == 0) { /* There were no PT_LOAD segments, so we don't have anything to read. */ free (x_phdrs); @@ -1727,40 +1737,64 @@ NAME(_bfd_elf,bfd_from_remote_memory) return NULL; } - /* Trim the last segment so we don't bother with zeros in the last page - that are off the end of the file. However, if the extra bit in that - page includes the section headers, keep them. */ - if ((bfd_vma) contents_size > last_phdr->p_offset + last_phdr->p_filesz - && (bfd_vma) contents_size >= (i_ehdr.e_shoff - + i_ehdr.e_shnum * i_ehdr.e_shentsize)) + shdr_end = 0; + if (i_ehdr.e_shoff != 0 && i_ehdr.e_shnum != 0 && i_ehdr.e_shentsize != 0) { - contents_size = last_phdr->p_offset + last_phdr->p_filesz; - if ((bfd_vma) contents_size < (i_ehdr.e_shoff - + i_ehdr.e_shnum * i_ehdr.e_shentsize)) - contents_size = i_ehdr.e_shoff + i_ehdr.e_shnum * i_ehdr.e_shentsize; + shdr_end = i_ehdr.e_shoff + i_ehdr.e_shnum * i_ehdr.e_shentsize; + + if (last_phdr->p_filesz != last_phdr->p_memsz) + { + /* If the last PT_LOAD header has a bss area then ld.so will + have cleared anything past p_filesz, zapping the section + headers. */ + } + else if (size >= shdr_end) + high_offset = shdr_end; + else + { + bfd_vma page_size = get_elf_backend_data (templ)->minpagesize; + bfd_vma segment_end = last_phdr->p_offset + last_phdr->p_filesz; + + /* Assume we loaded full pages, allowing us to sometimes see + section headers. */ + if (page_size > 1 && shdr_end > segment_end) + { + bfd_vma page_end = (segment_end + page_size - 1) & -page_size; + + if (page_end >= shdr_end) + /* Whee, section headers covered. */ + high_offset = shdr_end; + } + } } - else - contents_size = last_phdr->p_offset + last_phdr->p_filesz; /* Now we know the size of the whole image we want read in. */ - contents = (bfd_byte *) bfd_zmalloc (contents_size); + contents = (bfd_byte *) bfd_zmalloc (high_offset); if (contents == NULL) { free (x_phdrs); - bfd_set_error (bfd_error_no_memory); return NULL; } for (i = 0; i < i_ehdr.e_phnum; ++i) if (i_phdrs[i].p_type == PT_LOAD) { - bfd_vma start = i_phdrs[i].p_offset & -i_phdrs[i].p_align; - bfd_vma end = (i_phdrs[i].p_offset + i_phdrs[i].p_filesz - + i_phdrs[i].p_align - 1) & -i_phdrs[i].p_align; - if (end > (bfd_vma) contents_size) - end = contents_size; - err = target_read_memory ((loadbase + i_phdrs[i].p_vaddr) - & -i_phdrs[i].p_align, + bfd_vma start = i_phdrs[i].p_offset; + bfd_vma end = start + i_phdrs[i].p_filesz; + bfd_vma vaddr = i_phdrs[i].p_vaddr; + + /* Extend the beginning of the first pt_load to cover file + header and program headers, if we proved earlier that its + aligned offset is 0. */ + if (first_phdr == &i_phdrs[i]) + { + vaddr -= start; + start = 0; + } + /* Extend the end of the last pt_load to cover section headers. */ + if (last_phdr == &i_phdrs[i]) + end = high_offset; + err = target_read_memory (loadbase + vaddr, contents + start, end - start); if (err) { @@ -1775,8 +1809,7 @@ NAME(_bfd_elf,bfd_from_remote_memory) /* If the segments visible in memory didn't include the section headers, then clear them from the file header. */ - if ((bfd_vma) contents_size < (i_ehdr.e_shoff - + i_ehdr.e_shnum * i_ehdr.e_shentsize)) + if (high_offset < shdr_end) { memset (&x_ehdr.e_shoff, 0, sizeof x_ehdr.e_shoff); memset (&x_ehdr.e_shnum, 0, sizeof x_ehdr.e_shnum); @@ -1792,7 +1825,6 @@ NAME(_bfd_elf,bfd_from_remote_memory) if (bim == NULL) { free (contents); - bfd_set_error (bfd_error_no_memory); return NULL; } nbfd = _bfd_new_bfd (); @@ -1800,12 +1832,11 @@ NAME(_bfd_elf,bfd_from_remote_memory) { free (bim); free (contents); - bfd_set_error (bfd_error_no_memory); return NULL; } nbfd->filename = xstrdup (""); nbfd->xvec = templ->xvec; - bim->size = contents_size; + bim->size = high_offset; bim->buffer = contents; nbfd->iostream = bim; nbfd->flags = BFD_IN_MEMORY; diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1803a0af83..b9539403fc 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,11 @@ +2014-04-02 Alan Modra + + * symfile-mem.c (symbol_file_add_from_memory): Add size parameter. + Pass to bfd_elf_bfd_from_remote_memory. Adjust all callers. + (struct symbol_file_add_from_memory_args): Add size field. + (find_vdso_size): New function. + (add_vsyscall_page): Attempt to find vdso size. + 2014-04-01 Doug Evans * dwarf2read.c (read_cutu_die_from_dwo): Improve comment. diff --git a/gdb/symfile-mem.c b/gdb/symfile-mem.c index e3230de85d..3f09c4d076 100644 --- a/gdb/symfile-mem.c +++ b/gdb/symfile-mem.c @@ -76,13 +76,14 @@ target_read_memory_bfd (bfd_vma memaddr, bfd_byte *myaddr, bfd_size_type len) } /* Read inferior memory at ADDR to find the header of a loaded object file - and read its in-core symbols out of inferior memory. TEMPL is a bfd + and read its in-core symbols out of inferior memory. SIZE, if + non-zero, is the known size of the object. TEMPL is a bfd representing the target's format. NAME is the name to use for this symbol file in messages; it can be NULL or a malloc-allocated string which will be attached to the BFD. */ static struct objfile * -symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name, - int from_tty) +symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, + size_t size, char *name, int from_tty) { struct objfile *objf; struct bfd *nbfd; @@ -95,7 +96,7 @@ symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name, if (bfd_get_flavour (templ) != bfd_target_elf_flavour) error (_("add-symbol-file-from-memory not supported for this target")); - nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase, + nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, size, &loadbase, target_read_memory_bfd); if (nbfd == NULL) error (_("Failed to read a valid object file image from memory.")); @@ -158,7 +159,7 @@ add_symbol_file_from_memory_command (char *args, int from_tty) error (_("Must use symbol-file or exec-file " "before add-symbol-file-from-memory.")); - symbol_file_add_from_memory (templ, addr, NULL, from_tty); + symbol_file_add_from_memory (templ, addr, 0, NULL, from_tty); } /* Arguments for symbol_file_add_from_memory_wrapper. */ @@ -167,6 +168,7 @@ struct symbol_file_add_from_memory_args { struct bfd *bfd; CORE_ADDR sysinfo_ehdr; + size_t size; char *name; int from_tty; }; @@ -179,8 +181,25 @@ symbol_file_add_from_memory_wrapper (struct ui_out *uiout, void *data) { struct symbol_file_add_from_memory_args *args = data; - symbol_file_add_from_memory (args->bfd, args->sysinfo_ehdr, args->name, - args->from_tty); + symbol_file_add_from_memory (args->bfd, args->sysinfo_ehdr, args->size, + args->name, args->from_tty); + return 0; +} + +/* Rummage through mappings to find the vsyscall page size. */ + +static int +find_vdso_size (CORE_ADDR vaddr, unsigned long size, + int read, int write, int exec, int modified, + void *data) +{ + struct symbol_file_add_from_memory_args *args = data; + + if (vaddr == args->sysinfo_ehdr) + { + args->size = size; + return 1; + } return 0; } @@ -217,6 +236,11 @@ add_vsyscall_page (struct target_ops *target, int from_tty) } args.bfd = bfd; args.sysinfo_ehdr = sysinfo_ehdr; + args.size = 0; + if (gdbarch_find_memory_regions_p (target_gdbarch ())) + (void) gdbarch_find_memory_regions (target_gdbarch (), + find_vdso_size, &args); + args.name = xstrprintf ("system-supplied DSO at %s", paddress (target_gdbarch (), sysinfo_ehdr)); /* Pass zero for FROM_TTY, because the action of loading the -- 2.34.1