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]

a patch to set ELF header flags for empty linker output files


This fixes a problem that showed up with ia64 linux kernel builds.
If you call the linker with only archives, like so
	ld -r -o foo.o bar.a
then we end up with an empty foo.o file, and we never call
bfd_merge_private_bfd_data for it.  The result of this is that the ELF
machine dependent header flags never get set.  We then get a linker error
if we try to use this empty foo.o file.

An example of this occurs in the linux kernel if you configure in the DRI
support (CONFIG_DRM) but do not configure in any of the video cards that use
the DRI support.  The linux Makefile builds all of the DRI object files,
and then does something like this:
	ar drmlib.a drm*.o
	ld -r -o drm.o [card1.o card2.o ... ] drmlib.a
The net effect is that if there are no cards, then drm.o is an empty object
file, as we don't need any of the drm*.o object files linked into the kernel.
We then get these errors for the final kernel link:
ld: drivers/char/drm/drm.o: linking 64-bit files with 32-bit files
ld: drivers/char/drm/drm.o: linking constant-gp files with non-constant-gp files
Bad value: failed to merge target specific data of file drivers/char/drm/drm.o

Since the ia64 linux kernel does not use the default setting of the ELF header
flags, no attempt to fix this by forcing default values will work.

I have included a small testcase at the end as a shar file.  If you use an
ia64-linux toolchain and type make, you get link errors.

I propose to fix this problem by adding code to the linker to notice when all
of the input files are archives, and then add one bfd_merge_private_bfd_data
call using the first member of the first archive.  A patch to do this is
included.

While looking at this, I noticed that it was possible to put inconsistent
object files in an archive.  I am hoping that is not recommended usage.
If this is valid, then an alternative here is to call
bfd_merge_private_bfd_data for every member of every archive.  That way
the result is unambiguous.  I can rewrite the patch this way if people think
this is better.

Another alternative is to change bfd_merge_private_bfd_data so that it
ignores empty input files.  Thus we get no error when we link in the
empty drm.o file even though it has the wrong machine dependent ELF header
flag bits set.  I don't like this suggestion though, as it may hide real
errors.  You can also get an empty file if you assemble an empty assembly
file, and we should complain if the wrong assembler options are used for that.

If I don't get any comments, I will go ahead and check in this patch in
a day or two.

2001-01-15  Jim Wilson  <wilson@redhat.com>

	* ldlang.c (lang_check): If file_chain.head is NULL, then find first
	member of first archive, and call bfd_merge_private_bfd_data on it.

Index: ldlang.c
===================================================================
RCS file: /cvs/cvsfiles/devo/ld/ldlang.c,v
retrieving revision 1.289
diff -p -r1.289 ldlang.c
*** ldlang.c	2000/07/10 11:46:54	1.289
--- ldlang.c	2001/01/16 00:03:58
*************** lang_check ()
*** 3477,3482 ****
--- 3477,3507 ----
  	    bfd_set_error_handler (pfn);
  	}
      }
+ 
+   /* There can be no used input files if the only inputs are archives.
+      In that case, arbitrarily pick the first element of the first archive
+      and use that one to initialize private bfd data.  This construct occurs
+      in the linux kernel, and if we don't have the bfd_merge_private_bfd_data
+      call, we can get errors in the final link.  */
+   if (file_chain.head == NULL)
+     {
+       lang_input_statement_type *f;
+ 
+       for (f = (lang_input_statement_type *) input_file_chain.head;
+ 	   f != NULL;
+ 	   f = (lang_input_statement_type *) f->next_real_file)
+ 	{
+ 	  if (f->the_bfd
+ 	      && bfd_check_format (f->the_bfd, bfd_archive))
+ 	    {
+ 	      bfd *member;
+ 
+ 	      member = bfd_openr_next_archived_file (f->the_bfd, (bfd *) NULL);
+ 	      bfd_merge_private_bfd_data (member, output_bfd);
+ 	      break;
+ 	    }
+ 	}
+     }
  }
  
  /* Look through all the global common symbols and attach them to the

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2001-01-15 16:17 PST by <wilson@bletchleypark.cygnus.com>.
# Source directory was `/blp/wilson/tmp'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#    180 -rw-rw-r-- Makefile
#     37 -rw-rw-r-- bar.c
#     38 -rw-rw-r-- foo.c
#    410 -rw-rw-r-- output
#     31 -rw-rw-r-- tmp.c
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    set `$dir/gettext --version 2>&1`
    if test "$3" = GNU
    then
      gettext_dir=$dir
    fi
  fi
  if test "$locale_dir" = FAILED && test -f $dir/shar \
     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
  then
    locale_dir=`$dir/shar --print-text-domain-dir`
  fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
  echo=echo
else
  TEXTDOMAINDIR=$locale_dir
  export TEXTDOMAINDIR
  TEXTDOMAIN=sharutils
  export TEXTDOMAIN
  echo="$gettext_dir/gettext -s"
fi
if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f 200112312359.59 -a -f $$.touch; then
  shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f 123123592001.59 -a ! -f 123123592001.5 -a -f $$.touch; then
  shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f 1231235901 -a -f $$.touch; then
  shar_touch='touch -am $3$4$5$6$2 "$8"'
else
  shar_touch=:
  echo
  $echo 'WARNING: not restoring timestamps.  Consider getting and'
  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch
#
if mkdir _sh16029; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= Makefile ==============
if test -f 'Makefile' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'Makefile' '(file already exists)'
else
  $echo 'x -' extracting 'Makefile' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
tmp: tmp.o foobar.o
X	gcc tmp.o foobar.o
X
foobar.o: foobar.a
X	ld -r -o foobar.o foobar.a
X
foobar.a: foo.o bar.o
X	ar rcs foobar.a foo.o bar.o
X
clean:
X	-rm -rf *.o *.a tmp core a.out
SHAR_EOF
  (set 20 01 01 15 16 03 01 'Makefile'; eval "$shar_touch") &&
  chmod 0664 'Makefile' ||
  $echo 'restore of' 'Makefile' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'Makefile:' 'MD5 check failed'
aa48668d8829143c35e538d5d70231fb  Makefile
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'Makefile'`"
    test 180 -eq "$shar_count" ||
    $echo 'Makefile:' 'original size' '180,' 'current size' "$shar_count!"
  fi
fi
# ============= bar.c ==============
if test -f 'bar.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'bar.c' '(file already exists)'
else
  $echo 'x -' extracting 'bar.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'bar.c' &&
int
sub (int a)
{
X  return a + 10;
}
SHAR_EOF
  (set 20 01 01 12 15 42 59 'bar.c'; eval "$shar_touch") &&
  chmod 0664 'bar.c' ||
  $echo 'restore of' 'bar.c' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'bar.c:' 'MD5 check failed'
eaf10718b2f6e487af9b7f164d355b67  bar.c
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'bar.c'`"
    test 37 -eq "$shar_count" ||
    $echo 'bar.c:' 'original size' '37,' 'current size' "$shar_count!"
  fi
fi
# ============= foo.c ==============
if test -f 'foo.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'foo.c' '(file already exists)'
else
  $echo 'x -' extracting 'foo.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'foo.c' &&
int
sub2 (int b)
{
X  return b - 10;
}
SHAR_EOF
  (set 20 01 01 12 16 26 03 'foo.c'; eval "$shar_touch") &&
  chmod 0664 'foo.c' ||
  $echo 'restore of' 'foo.c' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'foo.c:' 'MD5 check failed'
7473ca997a51a88aabca23571ac6d9f8  foo.c
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'foo.c'`"
    test 38 -eq "$shar_count" ||
    $echo 'foo.c:' 'original size' '38,' 'current size' "$shar_count!"
  fi
fi
# ============= output ==============
if test -f 'output' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'output' '(file already exists)'
else
  $echo 'x -' extracting 'output' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'output' &&
cc    -c -o tmp.o tmp.c
cc    -c -o foo.o foo.c
cc    -c -o bar.o bar.c
ar rcs foobar.a foo.o bar.o
ld -r -o foobar.o foobar.a
gcc tmp.o foobar.o
/usr/lib/gcc-lib/ia64-redhat-linux/2.96-ia64-000717/../../../../ia64-redhat-linux/bin/ld: foobar.o: linking 64-bit files with 32-bit files
Bad value: failed to merge target specific data of file foobar.o
collect2: ld returned 1 exit status
make: *** [tmp] Error 1
SHAR_EOF
  (set 20 01 01 15 16 16 56 'output'; eval "$shar_touch") &&
  chmod 0664 'output' ||
  $echo 'restore of' 'output' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'output:' 'MD5 check failed'
b7d71e6e0f37a95140282ed093cae6da  output
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'output'`"
    test 410 -eq "$shar_count" ||
    $echo 'output:' 'original size' '410,' 'current size' "$shar_count!"
  fi
fi
# ============= tmp.c ==============
if test -f 'tmp.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'tmp.c' '(file already exists)'
else
  $echo 'x -' extracting 'tmp.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'tmp.c' &&
int
main(void)
{
X  return 0;
}
SHAR_EOF
  (set 20 01 01 12 16 26 15 'tmp.c'; eval "$shar_touch") &&
  chmod 0664 'tmp.c' ||
  $echo 'restore of' 'tmp.c' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'tmp.c:' 'MD5 check failed'
3f95b957acb7bed7783e7c5914ff68e1  tmp.c
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'tmp.c'`"
    test 31 -eq "$shar_count" ||
    $echo 'tmp.c:' 'original size' '31,' 'current size' "$shar_count!"
  fi
fi
rm -fr _sh16029
exit 0

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