Home | History | Annotate | Download | only in ld-ifunc
      1 # Expect script for linker support of IFUNC symbols and relocations.
      2 #
      3 #   Copyright (C) 2009-2014 Free Software Foundation, Inc.
      4 #   Contributed by Red Hat.
      5 #
      6 # This file is part of the GNU Binutils.
      7 #
      8 # This program is free software; you can redistribute it and/or modify
      9 # it under the terms of the GNU General Public License as published by
     10 # the Free Software Foundation; either version 3 of the License, or
     11 # (at your option) any later version.
     12 #
     13 # This program is distributed in the hope that it will be useful,
     14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 # GNU General Public License for more details.
     17 #
     18 # You should have received a copy of the GNU General Public License
     19 # along with this program; if not, write to the Free Software
     20 # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
     21 # MA 02110-1301, USA.
     22 #
     23 # Written by Nick Clifton <nickc (at) redhat.com>
     24 
     25 
     26 # IFUNC support has only been implemented for the ix86, x86_64, powerpc,
     27 # aarch64 and sparc so far.
     28 if {!(([istarget "i?86-*-*"]
     29        || [istarget "x86_64-*-*"]
     30        || [istarget "powerpc*-*-*"]
     31        || [istarget "aarch64*-*-*"]
     32        || [istarget "sparc*-*-*"])
     33       && ([istarget "*-*-elf*"]
     34 	  || [istarget "*-*-nacl*"]
     35 	  || (([istarget "*-*-linux*"]
     36 	       || [istarget "*-*-gnu*"])
     37 	      && ![istarget "*-*-*aout*"]
     38 	      && ![istarget "*-*-*oldld*"]))) } {
     39     verbose "IFUNC tests not run - target does not support IFUNC"
     40     return
     41 }
     42 
     43 # We need a native system.  FIXME: Strictly speaking this
     44 # is not true, we just need to know how to create a fully
     45 # linked executable, including the C and Z libraries, using
     46 # the linker that is under test.
     47 if ![isnative] {
     48     verbose "IFUNC tests not run - not a native toolchain"
     49     return
     50 }
     51 
     52 # We need a working compiler.  (Strictly speaking this is
     53 # not true, we could use target specific assembler files).
     54 if { [which $CC] == 0 } {
     55     verbose "IFUNC tests not run - no compiler available"
     56     return
     57 }
     58 
     59 # A procedure to check the OS/ABI field in the ELF header of a binary file.
     60 proc check_osabi { binary_file expected_osabi } {
     61     global READELF
     62     global READELFFLAGS
     63 
     64     catch "exec $READELF $READELFFLAGS --file-header $binary_file > readelf.out" got
     65 
     66     if ![string match "" $got] then {
     67 	verbose "proc check_osabi: Readelf produced unexpected out processing $binary_file: $got"
     68 	return 0
     69     }
     70 
     71     if { ![regexp "\n\[ \]*OS/ABI:\[ \]*(.+)\n\[ \]*ABI" \
     72 	   [file_contents readelf.out] nil osabi] } {
     73 	verbose "proc check_osabi: Readelf failed to extract an ELF header from $binary_file"
     74 	return 0
     75     }
     76 
     77     if { $osabi == $expected_osabi } {
     78 	return 1
     79     }
     80 
     81     verbose "Expected OSABI: $expected_osabi, Obtained osabi: $osabi"
     82 
     83     return 0
     84 }
     85 
     86 # A procedure to confirm that a file contains the IFUNC symbol.
     87 # Returns -1 upon error, 0 if the symbol was not found and 1 if it was found.
     88 proc contains_ifunc_symbol { binary_file } {
     89     global READELF
     90     global READELFFLAGS
     91 
     92     catch "exec $READELF $READELFFLAGS --symbols $binary_file > readelf.out" got
     93 
     94     if ![string match "" $got] then {
     95 	verbose "proc contains_ifunc_symbol: Readelf produced unexpected out processing $binary_file: $got"
     96 	return -1
     97     }
     98 
     99     # Look for a line like this:
    100     #    58: 0000000000400600    30 IFUNC   GLOBAL DEFAULT   12 library_func2
    101     # with perhaps some other info between the visibility and section
    102 
    103     if { ![regexp ".*\[ \]*IFUNC\[ \]+GLOBAL\[ \]+DEFAULT .* \[UND0-9\]+\[ \]+library_func2\n" [file_contents readelf.out]] } {
    104 	return 0
    105     }
    106 
    107     return 1
    108 }
    109 
    110 # A procedure to confirm that a file contains the R_*_IRELATIVE
    111 # relocation.
    112 # Returns -1 upon error, 0 if the relocation was not found and 1 if
    113 # it was found.
    114 proc contains_irelative_reloc { binary_file } {
    115     global READELF
    116     global READELFFLAGS
    117 
    118     catch "exec $READELF $READELFFLAGS --relocs --wide $binary_file > readelf.out" got
    119 
    120     if ![string match "" $got] then {
    121 	verbose "proc contains_irelative_reloc: Readelf produced unexpected out processing $binary_file: $got"
    122 	return -1
    123     }
    124 
    125     # Look for a line like this:
    126     #    0000000000600ab0  0000000000000025 R_X86_64_IRELATIVE      000000000040061c
    127     #    080496f4  0000002a R_386_IRELATIVE
    128 
    129 
    130     if { ![regexp "\[0-9a-f\]+\[ \]+\[0-9a-f\]+\[ \]+R_\[_0-9A-Z\]+_IREL(|ATIVE)\[ \]*\[0-9a-f\]*\n" [file_contents readelf.out]] } {
    131 	return 0
    132     }
    133 
    134     return 1
    135 }
    136 
    137 # A procedure to confirm that a file contains a relocation that references an IFUNC symbol.
    138 # Returns -1 upon error, 0 if the reloc was not found and 1 if it was found.
    139 proc contains_ifunc_reloc { binary_file } {
    140     global READELF
    141     global READELFFLAGS
    142 
    143     catch "exec $READELF $READELFFLAGS --relocs $binary_file > readelf.out" got
    144 
    145     if ![string match "" $got] then {
    146 	verbose "proc contains_ifunc_reloc: Readelf produced unexpected out processing $binary_file: $got"
    147 	return -1
    148     }
    149 
    150     if [string match "" [file_contents readelf.out]] then {
    151 	verbose "No relocs found in $binary_file"
    152 	return 0
    153     }
    154 
    155     if { ![regexp "\\(\\)" [file_contents readelf.out]] } {
    156 	return 0
    157     }
    158 
    159     return 1
    160 }
    161 
    162 set fails 0
    163 
    164 # Create the object files, libraries and executables.
    165 if ![ld_compile "$CC -c -fPIC" "$srcdir/$subdir/prog.c" "tmpdir/shared_prog.o"] {
    166     fail "Could not create a PIC object file"
    167     set fails [expr $fails + 1]
    168 }
    169 if ![ld_compile "$CC -c" "$srcdir/$subdir/prog.c" "tmpdir/static_prog.o"] {
    170     fail "Could not create a non-PIC object file"
    171     set fails [expr $fails + 1]
    172 }
    173 if ![ld_compile "$CC -c -fPIC -DWITH_IFUNC" "$srcdir/$subdir/lib.c" "tmpdir/shared_ifunc.o"] {
    174     fail "Could not create a PIC object file containing an IFUNC symbol"
    175     set fails [expr $fails + 1]
    176 }
    177 if ![ld_compile "$CC -c -DWITH_IFUNC" "$srcdir/$subdir/lib.c" "tmpdir/static_ifunc.o"] {
    178     fail "Could not create a non-PIC object file containing an IFUNC symbol"
    179     set fails [expr $fails + 1]
    180 }
    181 if ![ld_compile "$CC -c -DWITHOUT_IFUNC" "$srcdir/$subdir/lib.c" "tmpdir/static_noifunc.o"] {
    182     fail "Could not create an ordinary non-PIC object file"
    183     set fails [expr $fails + 1]
    184 }
    185 if ![ld_assemble $as "$srcdir/ld-elf/empty.s" "tmpdir/empty.o"] {
    186     fail "Could not create an empty object file"
    187     set fails [expr $fails + 1]
    188 }
    189 if ![ld_compile "$CC -c" "$srcdir/$subdir/test-1.c" "tmpdir/test-1.o"] {
    190     fail "Could not create test-1.o"
    191     set fails [expr $fails + 1]
    192 }
    193 if ![ld_compile "$CC -fPIC -c" "$srcdir/$subdir/test-2.c" "tmpdir/test-2.o"] {
    194     fail "Could not create test-2.o"
    195     set fails [expr $fails + 1]
    196 }
    197 
    198 if { $fails != 0 } {
    199     return
    200 }
    201 
    202 if ![ld_simple_link $ld "tmpdir/libshared_ifunc.so" "-shared tmpdir/shared_ifunc.o"] {
    203     fail "Could not create a shared library containing an IFUNC symbol"
    204     set fails [expr $fails + 1]
    205 }
    206 if ![ar_simple_create $ar "" "tmpdir/libifunc.a" "tmpdir/static_ifunc.o"] {
    207     fail "Could not create a static library containing an IFUNC symbol"
    208     set fails [expr $fails + 1]
    209 }
    210 
    211 if { $fails != 0 } {
    212     return
    213 }
    214 
    215 if ![default_ld_link $ld "tmpdir/dynamic_prog" "-Ltmpdir tmpdir/shared_prog.o -Bdynamic -lshared_ifunc -rpath ./tmpdir"] {
    216     fail "Could not link a dynamic executable"
    217     set fails [expr $fails + 1]
    218 }
    219 if ![default_ld_link $ld "tmpdir/local_prog" "-Ltmpdir tmpdir/static_prog.o -lifunc"] {
    220     fail "Could not link a dynamic executable using local ifunc"
    221     set fails [expr $fails + 1]
    222 }
    223 if ![default_ld_link $ld "tmpdir/static_prog" "-static -Ltmpdir tmpdir/static_prog.o -lifunc"] {
    224     fail "Could not link a static executable"
    225     set fails [expr $fails + 1]
    226 }
    227 if ![ld_simple_link $ld "tmpdir/static_nonifunc_prog" "-static tmpdir/empty.o"] {
    228     fail "Could not link a non-ifunc using static executable"
    229     set fails [expr $fails + 1]
    230 }
    231 if ![default_ld_link $ld "tmpdir/test-1" "tmpdir/test-1.o tmpdir/libshared_ifunc.so"] {
    232     fail "Could not link test-1"
    233     set fails [expr $fails + 1]
    234 }
    235 if ![ld_simple_link $ld "tmpdir/libtest-2.so" "-shared tmpdir/test-2.o"] {
    236     fail "Could not link libtest-2.so"
    237     set fails [expr $fails + 1]
    238 }
    239 
    240 if { $fails == 0 } {
    241   pass "Building ifunc binaries"
    242   set fails 0
    243 } else {
    244     return
    245 }
    246 
    247 # Check the executables and shared libraries
    248 #
    249 # The linked ifunc using executables and the shared library containing
    250 # ifunc should have an OSABI field of GNU.  The linked non-ifunc using
    251 # executable should have an OSABI field of NONE (aka System V).
    252 
    253 if {! [check_osabi tmpdir/libshared_ifunc.so {UNIX - GNU}]} {
    254     fail "Shared libraries containing ifunc does not have an OS/ABI field of GNU"
    255     set fails [expr $fails + 1]
    256 }
    257 if {! [check_osabi tmpdir/local_prog {UNIX - GNU}]} {
    258     fail "Local ifunc-using executable does not have an OS/ABI field of GNU"
    259     set fails [expr $fails + 1]
    260 }
    261 if {! [check_osabi tmpdir/static_prog {UNIX - GNU}]} {
    262     fail "Static ifunc-using executable does not have an OS/ABI field of GNU"
    263     set fails [expr $fails + 1]
    264 }
    265 if {! [check_osabi tmpdir/dynamic_prog {UNIX - System V}]} {
    266     fail "Dynamic ifunc-using executable does not have an OS/ABI field of System V"
    267     set fails [expr $fails + 1]
    268 }
    269 if {! [check_osabi tmpdir/static_nonifunc_prog {UNIX - System V}]} {
    270     fail "Static non-ifunc-using executable does not have an OS/ABI field of System V"
    271     set fails [expr $fails + 1]
    272 }
    273 
    274 # The linked ifunc using executables and the shared library containing
    275 # ifunc should contain an IFUNC symbol.  The non-ifunc using executable
    276 # should not.
    277 
    278 if {[contains_ifunc_symbol tmpdir/libshared_ifunc.so] != 1} {
    279     fail "Shared libraries containing ifunc does not contain an IFUNC symbol"
    280     set fails [expr $fails + 1]
    281 }
    282 if {[contains_ifunc_symbol tmpdir/local_prog] != 1} {
    283     fail "Local ifunc-using executable does not contain an IFUNC symbol"
    284     set fails [expr $fails + 1]
    285 }
    286 if {[contains_ifunc_symbol tmpdir/static_prog] != 1} {
    287     fail "Static ifunc-using executable does not contain an IFUNC symbol"
    288     set fails [expr $fails + 1]
    289 }
    290 if {[contains_ifunc_symbol tmpdir/dynamic_prog] != 0} {
    291     fail "Dynamic ifunc-using executable contains an IFUNC symbol"
    292     set fails [expr $fails + 1]
    293 }
    294 if {[contains_ifunc_symbol tmpdir/static_nonifunc_prog] != 0} {
    295     fail "Static non-ifunc-using executable contains an IFUNC symbol"
    296     set fails [expr $fails + 1]
    297 }
    298 if {[contains_ifunc_symbol tmpdir/test-1] != 0} {
    299     fail "test-1 contains IFUNC symbols"
    300     set fails [expr $fails + 1]
    301 }
    302 if {[contains_ifunc_symbol tmpdir/libtest-2.so] != 0} {
    303     fail "libtest-2.so contains IFUNC symbols"
    304     set fails [expr $fails + 1]
    305 }
    306 
    307 # The linked ifunc using executables and shared libraries should contain
    308 # a dynamic reloc referencing the IFUNC symbol.  (Even the static
    309 # executable which should have a dynamic section created for it).  The
    310 # non-ifunc using executable should not.
    311 
    312 if {[contains_irelative_reloc tmpdir/libshared_ifunc.so] != 1} {
    313     fail "ifunc-using shared library does not contain R_*_IRELATIVE relocation"
    314     set fails [expr $fails + 1]
    315 }
    316 if {[contains_irelative_reloc tmpdir/local_prog] != 1} {
    317     fail "Local ifunc-using executable does not contain R_*_IRELATIVE relocation"
    318     set fails [expr $fails + 1]
    319 }
    320 if {[contains_irelative_reloc tmpdir/static_prog] != 1} {
    321     fail "Static ifunc-using executable does not contain R_*_IRELATIVE relocation"
    322     set fails [expr $fails + 1]
    323 }
    324 if {[contains_ifunc_reloc tmpdir/dynamic_prog] != 0} {
    325     fail "Dynamic ifunc-using executable contains a reloc against an IFUNC symbol"
    326     set fails [expr $fails + 1]
    327 }
    328 if {[contains_ifunc_reloc tmpdir/static_nonifunc_prog] == 1} {
    329     fail "Static non-ifunc-using executable contains a reloc against an IFUNC symbol!"
    330     set fails [expr $fails + 1]
    331 }
    332 
    333 if { $fails == 0 } {
    334   pass "Checking ifunc binaries"
    335 }
    336 
    337 # Clean up, unless we are being verbose, in which case we leave the files available.
    338 if { $verbose < 1 } {
    339     remote_file host delete "tmpdir/shared_prog.o"
    340     remote_file host delete "tmpdir/static_prog.o"
    341     remote_file host delete "tmpdir/shared_ifunc.o"
    342     remote_file host delete "tmpdir/static_ifunc.o"
    343     remote_file host delete "tmpdir/static_noifunc.o"
    344     remote_file host delete "tmpdir/libshared_ifunc.so"
    345     remote_file host delete "tmpdir/libifunc.a"
    346     remote_file host delete "tmpdir/dynamic_prog"
    347     remote_file host delete "tmpdir/local_prog"
    348     remote_file host delete "tmpdir/static_prog"
    349     remote_file host delete "tmpdir/static_nonifunc_prog"
    350 }
    351 
    352 run_cc_link_tests [list \
    353     [list \
    354 	"Build libpr16467a.so" \
    355 	"-shared -Wl,--version-script=pr16467a.map" \
    356 	"-fPIC" \
    357 	{ pr16467a.c } \
    358 	{} \
    359 	"libpr16467a.so" \
    360     ] \
    361     [list \
    362 	"Build libpr16467b.a" \
    363 	"" \
    364 	"-fPIC" \
    365 	{ pr16467b.c } \
    366 	{} \
    367 	"libpr16467b.a" \
    368     ] \
    369     [list \
    370 	"Build libpr16467b.so" \
    371 	"-shared tmpdir/pr16467b.o tmpdir/libpr16467a.so \
    372 	 -Wl,--version-script=pr16467b.map" \
    373 	"-fPIC" \
    374 	{ dummy.c } \
    375 	{} \
    376 	"libpr16467b.so" \
    377     ] \
    378     [list \
    379 	"Build libpr16467c.a" \
    380 	"" \
    381 	"" \
    382 	{ pr16467c.c } \
    383 	{} \
    384 	"libpr16467c.a" \
    385     ] \
    386 ]
    387 
    388 run_ld_link_exec_tests [] [list \
    389     [list \
    390 	"Common symbol override ifunc test 1a" \
    391 	"-static" \
    392 	"" \
    393 	{ ifunc-common-1a.c ifunc-common-1b.c } \
    394 	"ifunc-common-1a" \
    395 	"ifunc-common-1.out" \
    396 	"-g" \
    397     ] \
    398     [list \
    399 	"Common symbol override ifunc test 1b" \
    400 	"-static" \
    401 	"" \
    402 	{ ifunc-common-1b.c ifunc-common-1a.c } \
    403 	"ifunc-common-1b" \
    404 	"ifunc-common-1.out" \
    405 	"-g" \
    406     ] \
    407     [list \
    408 	"Run pr16467" \
    409 	"tmpdir/pr16467c.o tmpdir/libpr16467b.so tmpdir/libpr16467a.so" \
    410 	"" \
    411 	{ dummy.c } \
    412 	"pr16467" \
    413 	"pr16467.out" \
    414 	"" \
    415     ] \
    416 ]
    417 
    418 set test_list [lsort [glob -nocomplain $srcdir/$subdir/*.d]]
    419 foreach t $test_list {
    420     # We need to strip the ".d", but can leave the dirname.
    421     verbose [file rootname $t]
    422     run_dump_test [file rootname $t]
    423 }
    424