This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [davidm@napali.hpl.hp.com: readelf question]


> Hi Roland,
> 
> > readelf is full of bogus assumptions about the ELF file layout.  There
> > are many places that extract a virtual address from some header, and
> > then subtract LOADADDR from this and expect the difference to be a
> > valid file offset corresponding to that virtual address.  This only
> > holds true when those addresses lie in the first PT_LOAD segment and
> > that segment starts at the beginning of the file.  That is the usual
> > situation in executables and DSOs, but it is certainly not a
> > requirement of the ELF format.  readelf, even more than other tools,
> > needs to accept and grok all files that are valid under the spec, not
> > just "the way it usually looks".  Each place that currently says "foo -
> > loadaddr" should be changed to "offset_from_vaddr (foo)" and that
> > function defined to search through all PT_LOAD segments to find the one
> > that actually contains the virtual address, and use its p_offset.
> 
> Would you like to contribute a patch that does this ?

Well if you're going to make such callous threats to my life and the lives
of my pets, I guess I'll just have to comply.  I don't want any trouble.


2003-06-23  Roland McGrath  <roland@redhat.com>

	* readelf.c (loadaddr): Variable removed.
	(dynamic_info, version_info): Fix type long -> bfd_vma.
	(program_headers): New variable.
	(get_program_headers): New function, broken out of
	process_program_headers.
	(process_program_headers): Call it.  Don't set `loadaddr'.
	(slurp_ia64_unwind_table): Use get_program_headers.
	(process_corefile_note_segments):  Likewise.
	(offset_from_vma): New function.
	(process_relocs, process_dynamic_segment): Call that instead of
	subtracting `loadaddr'.
	(process_version_sections, process_symbol_table): Likewise.
	(process_mips_specific): Likewise.

--- readelf.c.~1.211~	Mon Jun 23 18:05:11 2003
+++ readelf.c	Mon Jun 23 18:53:26 2003
@@ -106,11 +106,11 @@ Elf_Internal_Syminfo *dynamic_syminfo;
 unsigned long dynamic_syminfo_offset;
 unsigned int dynamic_syminfo_nent;
 char program_interpreter[64];
-long dynamic_info[DT_JMPREL + 1];
-long version_info[16];
-long loadaddr = 0;
+bfd_vma dynamic_info[DT_JMPREL + 1];
+bfd_vma version_info[16];
 Elf_Internal_Ehdr elf_header;
 Elf_Internal_Shdr *section_headers;
+Elf_Internal_Phdr *program_headers;
 Elf_Internal_Dyn *dynamic_segment;
 Elf_Internal_Shdr *symtab_shndx_hdr;
 int show_name;
@@ -282,6 +282,8 @@ static int get_32bit_program_headers
   PARAMS ((FILE *, Elf_Internal_Phdr *));
 static int get_64bit_program_headers
   PARAMS ((FILE *, Elf_Internal_Phdr *));
+static int get_program_headers
+  PARAMS ((FILE *));
 static int get_file_header
   PARAMS ((FILE *));
 static Elf_Internal_Sym *get_32bit_elf_symbols
@@ -296,6 +298,8 @@ static int get_32bit_dynamic_segment
   PARAMS ((FILE *));
 static int get_64bit_dynamic_segment
   PARAMS ((FILE *));
+static long offset_from_vma
+  PARAMS ((FILE *, bfd_vma vma, bfd_size_type size));
 #ifdef SUPPORT_DISASSEMBLY
 static int disassemble_section
   PARAMS ((Elf_Internal_Shdr *, FILE *));
@@ -3179,13 +3183,45 @@ get_64bit_program_headers (file, program
   return 1;
 }
 
+/* Returns 1 if the program headers were read into `program_headers'.  */
+
+static int
+get_program_headers (file)
+     FILE *file;
+{
+  Elf_Internal_Phdr *phdrs;
+
+  /* Check cache of prior read.  */
+  if (program_headers != NULL)
+    return 1;
+
+  phdrs = (Elf_Internal_Phdr *) malloc
+    (elf_header.e_phnum * sizeof (Elf_Internal_Phdr));
+
+  if (phdrs == NULL)
+    {
+      error (_("Out of memory\n"));
+      return 0;
+    }
+
+  if (is_32bit_elf
+      ? get_32bit_program_headers (file, phdrs)
+      : get_64bit_program_headers (file, phdrs))
+    {
+      program_headers = phdrs;
+      return 1;
+    }
+
+  free (phdrs);
+  return 0;
+}
+
 /* Returns 1 if the program headers were loaded.  */
 
 static int
 process_program_headers (file)
      FILE *file;
 {
-  Elf_Internal_Phdr *program_headers;
   Elf_Internal_Phdr *segment;
   unsigned int i;
 
@@ -3207,25 +3243,8 @@ process_program_headers (file)
       printf ("\n");
     }
 
-  program_headers = (Elf_Internal_Phdr *) malloc
-    (elf_header.e_phnum * sizeof (Elf_Internal_Phdr));
-
-  if (program_headers == NULL)
-    {
-      error (_("Out of memory\n"));
-      return 0;
-    }
-
-  if (is_32bit_elf)
-    i = get_32bit_program_headers (file, program_headers);
-  else
-    i = get_64bit_program_headers (file, program_headers);
-
-  if (i == 0)
-    {
-      free (program_headers);
+  if (! get_program_headers (file))
       return 0;
-    }
 
   if (do_segments)
     {
@@ -3249,7 +3268,6 @@ process_program_headers (file)
 	}
     }
 
-  loadaddr = -1;
   dynamic_addr = 0;
   dynamic_size = 0;
 
@@ -3337,18 +3355,6 @@ process_program_headers (file)
 
       switch (segment->p_type)
 	{
-	case PT_LOAD:
-	  if (loadaddr == -1)
-	    {
-	      unsigned long align_mask = -segment->p_align;
-
-	      if (align_mask == 0)
-		--align_mask;
-	      loadaddr = ((segment->p_vaddr & align_mask)
-			  - (segment->p_offset & align_mask));
-	    }
-	  break;
-
 	case PT_DYNAMIC:
 	  if (dynamic_addr)
 	    error (_("more than one dynamic segment\n"));
@@ -3376,12 +3382,6 @@ process_program_headers (file)
 	putc ('\n', stdout);
     }
 
-  if (loadaddr == -1)
-    {
-      /* Very strange.  */
-      loadaddr = 0;
-    }
-
   if (do_segments && section_headers != NULL)
     {
       printf (_("\n Section to Segment mapping:\n"));
@@ -3418,12 +3418,43 @@ process_program_headers (file)
 	}
     }
 
-  free (program_headers);
-
   return 1;
 }
 
 
+/* Find the file offset corresponding to VMA by using the program headers.  */
+static long
+offset_from_vma (file, vma, size)
+     FILE *file;
+     bfd_vma vma;
+     bfd_size_type size;
+{
+  Elf_Internal_Phdr *seg;
+
+  if (! get_program_headers (file))
+    {
+      warn (_("Cannot interpret virtual addresses without program headers.\n"));
+      return (long) vma;
+    }
+
+  for (seg = program_headers;
+       seg < program_headers + elf_header.e_phnum;
+       ++seg)
+    {
+      if (seg->p_type != PT_LOAD)
+	continue;
+
+      if (vma >= (seg->p_vaddr & -seg->p_align)
+	  && vma + size <= seg->p_vaddr + seg->p_filesz)
+	return vma - seg->p_vaddr + seg->p_offset;
+    }
+
+  warn (_("Virtual address 0x%lx not located in any PT_LOAD segment.\n"),
+	(long) vma);
+  return (long) vma;
+}
+
+
 static int
 get_32bit_section_headers (file, num)
      FILE *file;
@@ -3996,7 +4027,9 @@ process_relocs (file)
 		(_("\n'%s' relocation section at offset 0x%lx contains %ld bytes:\n"),
 		 name, rel_offset, rel_size);
 
-	      dump_relocations (file, rel_offset - loadaddr, rel_size,
+	      dump_relocations (file,
+				offset_from_vma (file, rel_offset, rel_size),
+				rel_size,
 				dynamic_symbols, num_dynamic_syms,
 				dynamic_strings, is_rela);
 	    }
@@ -4222,14 +4255,13 @@ slurp_ia64_unwind_table (file, aux, sec)
      Elf_Internal_Shdr *sec;
 {
   unsigned long size, addr_size, nrelas, i;
-  Elf_Internal_Phdr *prog_hdrs, *seg;
+  Elf_Internal_Phdr *seg;
   struct unw_table_entry *tep;
   Elf_Internal_Shdr *relsec;
   Elf_Internal_Rela *rela, *rp;
   unsigned char *table, *tp;
   Elf_Internal_Sym *sym;
   const char *relname;
-  int result;
 
   addr_size = is_32bit_elf ? 4 : 8;
 
@@ -4238,21 +4270,12 @@ slurp_ia64_unwind_table (file, aux, sec)
 
   if (elf_header.e_phnum)
     {
-      prog_hdrs = (Elf_Internal_Phdr *)
-	xmalloc (elf_header.e_phnum * sizeof (Elf_Internal_Phdr));
-
-      if (is_32bit_elf)
-	result = get_32bit_program_headers (file, prog_hdrs);
-      else
-	result = get_64bit_program_headers (file, prog_hdrs);
-
-      if (!result)
-	{
-	  free (prog_hdrs);
+      if (! get_program_headers (file))
 	  return 0;
-	}
 
-      for (seg = prog_hdrs; seg < prog_hdrs + elf_header.e_phnum; ++seg)
+      for (seg = program_headers;
+	   seg < program_headers + elf_header.e_phnum;
+	   ++seg)
 	{
 	  if (seg->p_type != PT_LOAD)
 	    continue;
@@ -4264,8 +4287,6 @@ slurp_ia64_unwind_table (file, aux, sec)
 	      break;
 	    }
 	}
-
-      free (prog_hdrs);
     }
 
   /* Second, build the unwind table from the contents of the unwind section:  */
@@ -4818,7 +4839,7 @@ process_dynamic_segment (file)
 	     we default to reading in the entire file (!) and
 	     processing that.  This is overkill, I know, but it
 	     should work.  */
-	  section.sh_offset = entry->d_un.d_val - loadaddr;
+	  section.sh_offset = offset_from_vma (file, entry->d_un.d_val, 0);
 
 	  if (fseek (file, 0, SEEK_END))
 	    error (_("Unable to seek to end of file!"));
@@ -4860,7 +4881,7 @@ process_dynamic_segment (file)
 	     processing that.  This is overkill, I know, but it
 	     should work.  */
 
-	  offset = entry->d_un.d_val - loadaddr;
+	  offset = offset_from_vma (file, entry->d_un.d_val, 0);
 	  if (fseek (file, 0, SEEK_END))
 	    error (_("Unable to seek to end of file\n"));
 	  str_tab_len = ftell (file) - offset;
@@ -4896,7 +4917,8 @@ process_dynamic_segment (file)
 	  else if (entry->d_tag == DT_SYMINSZ)
 	    syminsz = entry->d_un.d_val;
 	  else if (entry->d_tag == DT_SYMINFO)
-	    dynamic_syminfo_offset = entry->d_un.d_val - loadaddr;
+	    dynamic_syminfo_offset = offset_from_vma (file, entry->d_un.d_val,
+						      syminsz);
 	}
 
       if (dynamic_syminfo_offset != 0 && syminsz != 0)
@@ -5585,7 +5607,9 @@ process_version_sections (file)
 	    edata =
 	      ((unsigned char *)
 	       get_data (NULL, file,
-			 version_info[DT_VERSIONTAGIDX (DT_VERSYM)] - loadaddr,
+			 offset_from_vma
+			 (file, version_info[DT_VERSIONTAGIDX (DT_VERSYM)],
+			  total * sizeof (short)),
 			 total * sizeof (short), _("version symbol data")));
 	    if (!edata)
 	      {
@@ -5641,8 +5665,9 @@ process_version_sections (file)
 			  Elf_Internal_Verneed ivn;
 			  unsigned long offset;
 
-			  offset = version_info[DT_VERSIONTAGIDX (DT_VERNEED)]
-			    - loadaddr;
+			  offset = offset_from_vma
+			    (file, version_info[DT_VERSIONTAGIDX (DT_VERNEED)],
+			     sizeof (Elf_External_Verneed));
 
 			  do
 			    {
@@ -5697,8 +5722,9 @@ process_version_sections (file)
 			  Elf_External_Verdef evd;
 			  unsigned long offset;
 
-			  offset = (version_info[DT_VERSIONTAGIDX (DT_VERDEF)]
-				    - loadaddr);
+			  offset = offset_from_vma
+			    (file, version_info[DT_VERSIONTAGIDX (DT_VERDEF)],
+			     sizeof evd);
 
 			  do
 			    {
@@ -5925,7 +5951,9 @@ process_symbol_table (file)
   if (dynamic_info[DT_HASH] && ((do_using_dynamic && dynamic_strings != NULL)
 				|| do_histogram))
     {
-      if (fseek (file, dynamic_info[DT_HASH] - loadaddr, SEEK_SET))
+      if (fseek (file, offset_from_vma (file, dynamic_info[DT_HASH],
+					sizeof nb + sizeof nc),
+		 SEEK_SET))
 	{
 	  error (_("Unable to seek to start of dynamic information"));
 	  return 0;
@@ -6056,8 +6084,9 @@ process_symbol_table (file)
 		  int is_nobits;
 		  int check_def;
 
-		  offset = version_info[DT_VERSIONTAGIDX (DT_VERSYM)]
-		    - loadaddr;
+		  offset = offset_from_vma
+		    (file, version_info[DT_VERSIONTAGIDX (DT_VERSYM)],
+		     sizeof data + si * sizeof (vers_data));
 
 		  get_data (&data, file, offset + si * sizeof (vers_data),
 			    sizeof (data), _("version data"));
@@ -6079,8 +6108,9 @@ process_symbol_table (file)
 			  Elf_Internal_Vernaux ivna;
 
 			  /* We must test both.  */
-			  offset = (version_info[DT_VERSIONTAGIDX (DT_VERNEED)]
-				    - loadaddr);
+			  offset = offset_from_vma
+			    (file, version_info[DT_VERSIONTAGIDX (DT_VERNEED)],
+			     sizeof evn);
 
 			  do
 			    {
@@ -6140,9 +6170,10 @@ process_symbol_table (file)
 			      Elf_External_Verdaux evda;
 			      unsigned long offset;
 
-			      offset
-				= (version_info[DT_VERSIONTAGIDX (DT_VERDEF)]
-				   - loadaddr);
+			      offset = offset_from_vma
+				(file,
+				 version_info[DT_VERSIONTAGIDX (DT_VERDEF)],
+				 sizeof (Elf_External_Verdef));
 
 			      do
 				{
@@ -9733,16 +9764,20 @@ process_mips_specific (file)
     switch (entry->d_tag)
       {
       case DT_MIPS_LIBLIST:
-	liblist_offset = entry->d_un.d_val - loadaddr;
+	liblist_offset
+	  = offset_from_vma (file, entry->d_un.d_val,
+			     liblistno * sizeof (Elf32_External_Lib));
 	break;
       case DT_MIPS_LIBLISTNO:
 	liblistno = entry->d_un.d_val;
 	break;
       case DT_MIPS_OPTIONS:
-	options_offset = entry->d_un.d_val - loadaddr;
+	options_offset = offset_from_vma (file, entry->d_un.d_val, 0);
 	break;
       case DT_MIPS_CONFLICT:
-	conflicts_offset = entry->d_un.d_val - loadaddr;
+	conflicts_offset
+	  = offset_from_vma (file, entry->d_un.d_val,
+			     conflictsno * sizeof (Elf32_External_Conflict));
 	break;
       case DT_MIPS_CONFLICTNO:
 	conflictsno = entry->d_un.d_val;
@@ -10394,30 +10429,12 @@ static int
 process_corefile_note_segments (file)
      FILE *file;
 {
-  Elf_Internal_Phdr *program_headers;
   Elf_Internal_Phdr *segment;
   unsigned int i;
   int res = 1;
 
-  program_headers = (Elf_Internal_Phdr *) malloc
-    (elf_header.e_phnum * sizeof (Elf_Internal_Phdr));
-
-  if (program_headers == NULL)
-    {
-      error (_("Out of memory\n"));
-      return 0;
-    }
-
-  if (is_32bit_elf)
-    i = get_32bit_program_headers (file, program_headers);
-  else
-    i = get_64bit_program_headers (file, program_headers);
-
-  if (i == 0)
-    {
-      free (program_headers);
+  if (! get_program_headers (file))
       return 0;
-    }
 
   for (i = 0, segment = program_headers;
        i < elf_header.e_phnum;
@@ -10429,8 +10446,6 @@ process_corefile_note_segments (file)
 					      (bfd_vma) segment->p_filesz);
     }
 
-  free (program_headers);
-
   return res;
 }
 
@@ -10647,6 +10662,12 @@ process_file (file_name)
 
   fclose (file);
 
+  if (program_headers)
+    {
+      free (program_headers);
+      program_headers = NULL;
+    }
+
   if (section_headers)
     {
       free (section_headers);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]