readelf: use fseeko64 or fseeko if possible

Message ID 20221117143419.19571-1-bwerl.dev@gmail.com
State Accepted
Headers
Series readelf: use fseeko64 or fseeko if possible |

Checks

Context Check Description
snail/binutils-gdb-check success Github commit url

Commit Message

Brett Werling Nov. 17, 2022, 2:34 p.m. UTC
  Changes readelf to make use first of fseeko64 and then fseeko,
depending on which of those is available. If neither is available,
reverts to the previous behavior of using fseek.

This is necessary when building readelf for LLP64 systems, where a
long will only be 32 bits wide. If the elf file in question is >= 2 GiB,
that is greater than the max long value and therefore fseek will fail
indicating that the offset is negative. On such systems, making use of
fseeko64 or fseeko will result in the ability so seek past the 2 GiB
max long boundary.
---
 binutils/config.in |  6 ++++++
 binutils/configure |  2 +-
 binutils/readelf.c | 30 +++++++++++++++++++-----------
 3 files changed, 26 insertions(+), 12 deletions(-)
  

Comments

Alan Modra Nov. 21, 2022, 9:52 p.m. UTC | #1
On Thu, Nov 17, 2022 at 08:34:19AM -0600, Brett Werling via Binutils wrote:
> Changes readelf to make use first of fseeko64 and then fseeko,
> depending on which of those is available. If neither is available,
> reverts to the previous behavior of using fseek.

Committed, thanks.
  
Michael Matz Nov. 22, 2022, 1:46 p.m. UTC | #2
Hey Alan,

On Tue, 22 Nov 2022, Alan Modra via Binutils wrote:

> On Thu, Nov 17, 2022 at 08:34:19AM -0600, Brett Werling via Binutils wrote:
> > Changes readelf to make use first of fseeko64 and then fseeko,
> > depending on which of those is available. If neither is available,
> > reverts to the previous behavior of using fseek.
> 
> Committed, thanks.

I've integrated the change into configure.ac and regen'ed 
config{ure,.in} and pushed d7229d6a561 for this.

(Brett: in case you wonder: configure and config.in are generated files, 
by autoconf, so editing them directly would eventually lose the changes on 
the next regeneration, configure.ac is the source file for them)


Ciao,
Michael.
  
Alan Modra Nov. 22, 2022, 9:55 p.m. UTC | #3
On Tue, Nov 22, 2022 at 01:46:56PM +0000, Michael Matz wrote:
> I've integrated the change into configure.ac and regen'ed 
> config{ure,.in} and pushed d7229d6a561 for this.

Thanks, I'll blame it on covid, which finally caught up with me last
week.
  
Alan Modra Nov. 22, 2022, 9:57 p.m. UTC | #4
Replace the macros with a small wrapper function that verifies the fseek
offset arg isn't overlarge.

	* readelf.c (FSEEK_FUNC): Delete, replace uses with..
	(fseek64): ..this new function.
	(process_program_headers): Don't cast p_offset to long.

diff --git a/binutils/readelf.c b/binutils/readelf.c
index 044022ec334..291bc13e0d0 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -178,14 +178,6 @@
 #define offsetof(TYPE, MEMBER) ((size_t) &(((TYPE *) 0)->MEMBER))
 #endif
 
-#if defined (HAVE_FSEEKO64)
-#define FSEEK_FUNC fseeko64
-#elif defined (HAVE_FSEEKO)
-#define FSEEK_FUNC fseeko
-#else
-#define FSEEK_FUNC fseek
-#endif
-
 typedef struct elf_section_list
 {
   Elf_Internal_Shdr *        hdr;
@@ -373,6 +365,36 @@ enum versioned_symbol_info
   symbol_public
 };
 
+static int
+fseek64 (FILE *stream, int64_t offset, int whence)
+{
+#if defined (HAVE_FSEEKO64)
+  off64_t o = offset;
+  if (o != offset)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  return fseeko64 (stream, o, whence);
+#elif defined (HAVE_FSEEKO)
+  off_t o = offset;
+  if (o != offset)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  return fseeko (stream, o, whence);
+#else
+  long o = offset;
+  if (o != offset)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  return fseek (stream, o, whence);
+#endif
+}
+
 static const char * get_symbol_version_string
   (Filedata *, bool, const char *, unsigned long, unsigned,
    Elf_Internal_Sym *, enum versioned_symbol_info *, unsigned short *);
@@ -490,8 +512,8 @@ get_data (void *var,
       return NULL;
     }
 
-  if (FSEEK_FUNC (filedata->handle, filedata->archive_file_offset + offset,
-	     SEEK_SET))
+  if (fseek64 (filedata->handle, filedata->archive_file_offset + offset,
+	       SEEK_SET))
     {
       if (reason)
 	error (_("Unable to seek to 0x%lx for %s\n"),
@@ -6291,9 +6313,9 @@ the .dynamic section is not the same as the dynamic segment\n"));
 	  if (segment->p_offset >= filedata->file_size
 	      || segment->p_filesz > filedata->file_size - segment->p_offset
 	      || segment->p_filesz - 1 >= (size_t) -2
-	      || FSEEK_FUNC (filedata->handle,
-			filedata->archive_file_offset + (long) segment->p_offset,
-			SEEK_SET))
+	      || fseek64 (filedata->handle,
+			  filedata->archive_file_offset + segment->p_offset,
+			  SEEK_SET))
 	    error (_("Unable to find program interpreter name\n"));
 	  else
 	    {
@@ -11064,11 +11086,12 @@ get_num_dynamic_syms (Filedata * filedata)
 	  && filedata->file_header.e_ident[EI_CLASS] == ELFCLASS64)
 	hash_ent_size = 8;
 
-      if (FSEEK_FUNC (filedata->handle,
-		 (filedata->archive_file_offset
-		  + offset_from_vma (filedata, filedata->dynamic_info[DT_HASH],
-				     sizeof nb + sizeof nc)),
-		 SEEK_SET))
+      if (fseek64 (filedata->handle,
+		   (filedata->archive_file_offset
+		    + offset_from_vma (filedata,
+				       filedata->dynamic_info[DT_HASH],
+				       sizeof nb + sizeof nc)),
+		   SEEK_SET))
 	{
 	  error (_("Unable to seek to start of dynamic information\n"));
 	  goto no_hash;
@@ -11117,12 +11140,12 @@ get_num_dynamic_syms (Filedata * filedata)
       uint64_t buckets_vma;
       unsigned long hn;
 
-      if (FSEEK_FUNC (filedata->handle,
-		 (filedata->archive_file_offset
-		  + offset_from_vma (filedata,
-				     filedata->dynamic_info_DT_GNU_HASH,
-				     sizeof nb)),
-		 SEEK_SET))
+      if (fseek64 (filedata->handle,
+		   (filedata->archive_file_offset
+		    + offset_from_vma (filedata,
+				       filedata->dynamic_info_DT_GNU_HASH,
+				       sizeof nb)),
+		   SEEK_SET))
 	{
 	  error (_("Unable to seek to start of dynamic information\n"));
 	  goto no_gnu_hash;
@@ -11143,10 +11166,10 @@ get_num_dynamic_syms (Filedata * filedata)
       else
 	buckets_vma += bitmaskwords * 8;
 
-      if (FSEEK_FUNC (filedata->handle,
-		 (filedata->archive_file_offset
-		  + offset_from_vma (filedata, buckets_vma, 4)),
-		 SEEK_SET))
+      if (fseek64 (filedata->handle,
+		   (filedata->archive_file_offset
+		    + offset_from_vma (filedata, buckets_vma, 4)),
+		   SEEK_SET))
 	{
 	  error (_("Unable to seek to start of dynamic information\n"));
 	  goto no_gnu_hash;
@@ -11173,13 +11196,13 @@ get_num_dynamic_syms (Filedata * filedata)
 
       maxchain -= filedata->gnusymidx;
 
-      if (FSEEK_FUNC (filedata->handle,
-		 (filedata->archive_file_offset
-		  + offset_from_vma (filedata,
-				     buckets_vma + 4 * (filedata->ngnubuckets
-							+ maxchain),
-				      4)),
-		 SEEK_SET))
+      if (fseek64 (filedata->handle,
+		   (filedata->archive_file_offset
+		    + offset_from_vma (filedata,
+				       buckets_vma + 4 * (filedata->ngnubuckets
+							  + maxchain),
+				       4)),
+		   SEEK_SET))
 	{
 	  error (_("Unable to seek to start of dynamic information\n"));
 	  goto no_gnu_hash;
@@ -11200,12 +11223,12 @@ get_num_dynamic_syms (Filedata * filedata)
 	}
       while ((byte_get (nb, 4) & 1) == 0);
 
-      if (FSEEK_FUNC (filedata->handle,
-		 (filedata->archive_file_offset
-		  + offset_from_vma (filedata, (buckets_vma
-						+ 4 * filedata->ngnubuckets),
-				     4)),
-		 SEEK_SET))
+      if (fseek64 (filedata->handle,
+		   (filedata->archive_file_offset
+		    + offset_from_vma (filedata, (buckets_vma
+						  + 4 * filedata->ngnubuckets),
+				       4)),
+		   SEEK_SET))
 	{
 	  error (_("Unable to seek to start of dynamic information\n"));
 	  goto no_gnu_hash;
@@ -11219,12 +11242,12 @@ get_num_dynamic_syms (Filedata * filedata)
 
       if (filedata->dynamic_info_DT_MIPS_XHASH)
 	{
-	  if (FSEEK_FUNC (filedata->handle,
-		     (filedata->archive_file_offset
-		      + offset_from_vma (filedata, (buckets_vma
-						    + 4 * (filedata->ngnubuckets
-							   + maxchain)), 4)),
-		     SEEK_SET))
+	  if (fseek64 (filedata->handle,
+		       (filedata->archive_file_offset
+			+ offset_from_vma (filedata, (buckets_vma
+						      + 4 * (filedata->ngnubuckets
+							     + maxchain)), 4)),
+		       SEEK_SET))
 	    {
 	      error (_("Unable to seek to start of dynamic information\n"));
 	      goto no_gnu_hash;
@@ -22618,7 +22641,7 @@ process_archive (Filedata * filedata, bool is_thin_archive)
 	      ret = false;
 	    }
 
-	  if (FSEEK_FUNC (filedata->handle, current_pos, SEEK_SET) != 0)
+	  if (fseek64 (filedata->handle, current_pos, SEEK_SET) != 0)
 	    {
 	      error (_("%s: failed to seek back to start of object files "
 		       "in the archive\n"),
@@ -22645,7 +22668,7 @@ process_archive (Filedata * filedata, bool is_thin_archive)
       char * qualified_name;
 
       /* Read the next archive header.  */
-      if (FSEEK_FUNC (filedata->handle, arch.next_arhdr_offset, SEEK_SET) != 0)
+      if (fseek64 (filedata->handle, arch.next_arhdr_offset, SEEK_SET) != 0)
 	{
 	  error (_("%s: failed to seek to next archive header\n"),
 		 arch.file_name);
@@ -22755,8 +22778,8 @@ process_archive (Filedata * filedata, bool is_thin_archive)
 
 	  /* The nested archive file will have been opened and setup by
 	     get_archive_member_name.  */
-	  if (FSEEK_FUNC (nested_arch.file, filedata->archive_file_offset,
-		     SEEK_SET) != 0)
+	  if (fseek64 (nested_arch.file, filedata->archive_file_offset,
+		       SEEK_SET) != 0)
 	    {
 	      error (_("%s: failed to seek to archive member.\n"),
 		     nested_arch.file_name);
  

Patch

diff --git a/binutils/config.in b/binutils/config.in
index 4d22a80971b..91fe00af777 100644
--- a/binutils/config.in
+++ b/binutils/config.in
@@ -67,6 +67,12 @@ 
 /* Define to 1 if you have the <fcntl.h> header file. */
 #undef HAVE_FCNTL_H
 
+/* Define to 1 if you have the `fseeko' function. */
+#undef HAVE_FSEEKO
+
+/* Define to 1 if you have the `fseeko64' function. */
+#undef HAVE_FSEEKO64
+
 /* Define to 1 if you have the `getc_unlocked' function. */
 #undef HAVE_GETC_UNLOCKED
 
diff --git a/binutils/configure b/binutils/configure
index 6176d699e57..46519a31701 100755
--- a/binutils/configure
+++ b/binutils/configure
@@ -13155,7 +13155,7 @@  $as_echo "#define HAVE_MMAP 1" >>confdefs.h
 fi
 rm -f conftest.mmap conftest.txt
 
-for ac_func in getc_unlocked mkdtemp mkstemp utimensat utimes
+for ac_func in getc_unlocked mkdtemp mkstemp utimensat utimes fseeko fseeko64
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 6b2cbbcbb1b..044022ec334 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -178,6 +178,14 @@ 
 #define offsetof(TYPE, MEMBER) ((size_t) &(((TYPE *) 0)->MEMBER))
 #endif
 
+#if defined (HAVE_FSEEKO64)
+#define FSEEK_FUNC fseeko64
+#elif defined (HAVE_FSEEKO)
+#define FSEEK_FUNC fseeko
+#else
+#define FSEEK_FUNC fseek
+#endif
+
 typedef struct elf_section_list
 {
   Elf_Internal_Shdr *        hdr;
@@ -482,7 +490,7 @@  get_data (void *var,
       return NULL;
     }
 
-  if (fseek (filedata->handle, filedata->archive_file_offset + offset,
+  if (FSEEK_FUNC (filedata->handle, filedata->archive_file_offset + offset,
 	     SEEK_SET))
     {
       if (reason)
@@ -6283,7 +6291,7 @@  the .dynamic section is not the same as the dynamic segment\n"));
 	  if (segment->p_offset >= filedata->file_size
 	      || segment->p_filesz > filedata->file_size - segment->p_offset
 	      || segment->p_filesz - 1 >= (size_t) -2
-	      || fseek (filedata->handle,
+	      || FSEEK_FUNC (filedata->handle,
 			filedata->archive_file_offset + (long) segment->p_offset,
 			SEEK_SET))
 	    error (_("Unable to find program interpreter name\n"));
@@ -11056,7 +11064,7 @@  get_num_dynamic_syms (Filedata * filedata)
 	  && filedata->file_header.e_ident[EI_CLASS] == ELFCLASS64)
 	hash_ent_size = 8;
 
-      if (fseek (filedata->handle,
+      if (FSEEK_FUNC (filedata->handle,
 		 (filedata->archive_file_offset
 		  + offset_from_vma (filedata, filedata->dynamic_info[DT_HASH],
 				     sizeof nb + sizeof nc)),
@@ -11109,7 +11117,7 @@  get_num_dynamic_syms (Filedata * filedata)
       uint64_t buckets_vma;
       unsigned long hn;
 
-      if (fseek (filedata->handle,
+      if (FSEEK_FUNC (filedata->handle,
 		 (filedata->archive_file_offset
 		  + offset_from_vma (filedata,
 				     filedata->dynamic_info_DT_GNU_HASH,
@@ -11135,7 +11143,7 @@  get_num_dynamic_syms (Filedata * filedata)
       else
 	buckets_vma += bitmaskwords * 8;
 
-      if (fseek (filedata->handle,
+      if (FSEEK_FUNC (filedata->handle,
 		 (filedata->archive_file_offset
 		  + offset_from_vma (filedata, buckets_vma, 4)),
 		 SEEK_SET))
@@ -11165,7 +11173,7 @@  get_num_dynamic_syms (Filedata * filedata)
 
       maxchain -= filedata->gnusymidx;
 
-      if (fseek (filedata->handle,
+      if (FSEEK_FUNC (filedata->handle,
 		 (filedata->archive_file_offset
 		  + offset_from_vma (filedata,
 				     buckets_vma + 4 * (filedata->ngnubuckets
@@ -11192,7 +11200,7 @@  get_num_dynamic_syms (Filedata * filedata)
 	}
       while ((byte_get (nb, 4) & 1) == 0);
 
-      if (fseek (filedata->handle,
+      if (FSEEK_FUNC (filedata->handle,
 		 (filedata->archive_file_offset
 		  + offset_from_vma (filedata, (buckets_vma
 						+ 4 * filedata->ngnubuckets),
@@ -11211,7 +11219,7 @@  get_num_dynamic_syms (Filedata * filedata)
 
       if (filedata->dynamic_info_DT_MIPS_XHASH)
 	{
-	  if (fseek (filedata->handle,
+	  if (FSEEK_FUNC (filedata->handle,
 		     (filedata->archive_file_offset
 		      + offset_from_vma (filedata, (buckets_vma
 						    + 4 * (filedata->ngnubuckets
@@ -22610,7 +22618,7 @@  process_archive (Filedata * filedata, bool is_thin_archive)
 	      ret = false;
 	    }
 
-	  if (fseek (filedata->handle, current_pos, SEEK_SET) != 0)
+	  if (FSEEK_FUNC (filedata->handle, current_pos, SEEK_SET) != 0)
 	    {
 	      error (_("%s: failed to seek back to start of object files "
 		       "in the archive\n"),
@@ -22637,7 +22645,7 @@  process_archive (Filedata * filedata, bool is_thin_archive)
       char * qualified_name;
 
       /* Read the next archive header.  */
-      if (fseek (filedata->handle, arch.next_arhdr_offset, SEEK_SET) != 0)
+      if (FSEEK_FUNC (filedata->handle, arch.next_arhdr_offset, SEEK_SET) != 0)
 	{
 	  error (_("%s: failed to seek to next archive header\n"),
 		 arch.file_name);
@@ -22747,7 +22755,7 @@  process_archive (Filedata * filedata, bool is_thin_archive)
 
 	  /* The nested archive file will have been opened and setup by
 	     get_archive_member_name.  */
-	  if (fseek (nested_arch.file, filedata->archive_file_offset,
+	  if (FSEEK_FUNC (nested_arch.file, filedata->archive_file_offset,
 		     SEEK_SET) != 0)
 	    {
 	      error (_("%s: failed to seek to archive member.\n"),