# Copyright 2002 # Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Please email any bugs, comments, and/or additions to this file to: # bug-gdb@prep.ai.mit.edu # This file was written by Jason Molenda (jmolenda@apple.com) if $tracelevel then { strace $tracelevel } # Test library slides. On MacOS X, most system libraries have set # addresses where they will load. Our libc-like library, libSystem, loads # at 0x90000000 or somesuch, for instance. If another shared library tries # to go at that same address, it gets shifted to another addr, and gdb needs # to (1) notice that shift, (2) update all the symbols and breakpoints. set timeout 30 set prms_id 0 set bug_id 0 set testfile "slipnslide" set binfile ${objdir}/${subdir}/${testfile} if { [gdb_compile "${srcdir}/${subdir}/slipnslide.c" "${binfile}" executable {debug}] != "" } { gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." } # Start with a fresh gdb gdb_exit gdb_start gdb_reinitialize_dir $srcdir/$subdir gdb_load ${binfile} # Figure out where libSystem normally loads, its full pathname, and its short # name. send_gdb "info sharedlibrary\n" gdb_expect { -re ".* (libSystem...dylib) *(-|$hex) *- *init Y Y (.*libSystem.B.dylib) at ($hex)( \\(offset (0x0)\\))?\[\r\n\].*" { pass "get libSystem pre-startup load address" set libsystem_shortname $expect_out(1,string) set libsystem_full_pathname $expect_out(3,string) set libsystem_load_addr $expect_out(4,string) } -re "$gdb_prompt $" { fail "get libSystem pre-startup load address" } timeout { fail "(timeout) get libSystem pre-startup load address" } } # See where printf() normally loads. set printf_address {} send_gdb "p printf\n" gdb_expect { -re ".*\\\$1 = .* ($hex) <printf>.*$gdb_prompt $" { pass "get printf pre-start load address" set printf_address $expect_out(1,string) } -re "$gdb_prompt $" { fail "get printf pre-start load address" } timeout { fail "(timeout) get printf pre-start load address" } } # See where puts() normally loads. send_gdb "p puts\n" gdb_expect { -re ".*\\\$2 = .* ($hex) <puts>.*$gdb_prompt $" { pass "get puts pre-start load address" set libsystem_puts_address $expect_out(1,string) } -re "$gdb_prompt $" { fail "get puts pre-start load address" } timeout { fail "(timeout) get puts pre-start load address" } } if ![runto_main] then { fail "slipnslide tests suppressed" return -1 } # Verify that nothing has slid with the default case. send_gdb "info sharedlibrary\n" gdb_expect { -re ".* $decimal $libsystem_shortname *- $libsystem_load_addr *dyld Y Y $libsystem_full_pathname at $libsystem_load_addr \\(offset 0x0\\).*$gdb_prompt $" { pass "default libSystem didn't slide" } -re "$gdb_prompt $" { fail "default libSystem didn't slide" } timeout { fail "(timeout) default libSystem didn't slide" } } # Verify that printf is where it should be. gdb_test "p printf" "\\\$3 = .* $printf_address <printf>" "printf is where it should be on a non-slide" gdb_exit ## ## Now we verify all that via MI just for fun. ## load_lib mi-support.exp set MIFLAGS "-i=mi1" mi_gdb_start mi_delete_breakpoints mi_gdb_reinitialize_dir $srcdir/$subdir mi_gdb_load ${binfile} mi_gdb_test "10-file-sharedlibrary-info" "10\\^done.*shlib-info=\\\[num=\"$decimal\",name=\"$libsystem_shortname\",kind=\"-\",dyld-addr=\"-\",reason=\"init\",requested-state=\"Y\",state=\"Y\",path=\"$libsystem_full_pathname\",slide=\"\",loaded_addr=\"$libsystem_load_addr\",prefix=\"\"\\\].*" "verify pre-start libsystem via mi1" mi_gdb_test "20-break-insert main" "20\\^done,bkpt=.*" mi_gdb_test "30-gdb-set stop-on-solib-events 1" "30\\^done" "gdb shows solib events" send_gdb "40-exec-run\n" gdb_expect { -re "(40\\^running\[\r\n\]|=shlibs-updated\[\r\n\]|$mi_gdb_prompt)+" { pass "first part of run startup.." } timeout { fail "(timeout) first part of run startup.. in mi1" } } gdb_expect { -re ".*\\\*stopped,reason=\"shlib-event\".*$mi_gdb_prompt$" { pass "catch shlib-event during startup" } timeout { fail "(timeout) catch shlib-event during startup in mi1" } } mi_gdb_test "50-file-sharedlibrary-info" "50\\^done.*shlib-info=\\\[num=\"$decimal\",name=\"$libsystem_shortname\",kind=\"-\",dyld-addr=\"$libsystem_load_addr\",reason=\"dyld\",requested-state=\"Y\",state=\"Y\",path=\"$libsystem_full_pathname\",loaded_addr=\"$libsystem_load_addr\",slide=\"0x0\",prefix=\"\"\\\].*" "verify pre-start libsystem via mi1" mi_gdb_exit ## ## Now create a shared library that conflicts with libSystem. ## set testlibfile "libmylib" set libbinfile ${objdir}/${subdir}/${testlibfile}.dylib set additional_flags "additional_flags=-dynamiclib -seg1addr $libsystem_load_addr" if { [gdb_compile "${srcdir}/${subdir}/${testlibfile}.c" "${libbinfile}" executable [list debug $additional_flags]] != "" } { gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." } ## ## Mix it all up and see what happens.. ## gdb_start gdb_reinitialize_dir $srcdir/$subdir gdb_test "set env DYLD_INSERT_LIBRARIES $libbinfile" "" gdb_load ${binfile} # Verify that libSystem and mylib are both going to try loading at the same addr. send_gdb "info sharedlibrary\n" gdb_expect { -re ".* $decimal ${testlibfile}.dylib *- *- *init Y Y $libbinfile at $libsystem_load_addr.* $decimal $libsystem_shortname *- *- *init Y Y $libsystem_full_pathname at $libsystem_load_addr.*$gdb_prompt $" { pass "libmylib and libsystem will both try to load at same addr" } -re "$gdb_prompt $" { fail "libmylib and libsystem will both try to load at same addr" } timeout { fail "(timeout) libmylib and libsystem will both try to load at same addr" } } # Get address of function 'foo' in mylib, pre-slid # See where foo() normally loads. send_gdb "p foo\n" gdb_expect { -re ".*\\\$1 = .* ($hex) <foo>.*$gdb_prompt $" { pass "get foo pre-start load address" set foo_address $expect_out(1,string) } -re "$gdb_prompt $" { fail "get foo pre-start load address" set foo_address -1 } timeout { fail "(timeout) get foo pre-start load address" set foo_address -1 } } # See where puts() thinks it's going to load this time.. send_gdb "p puts\n" gdb_expect { -re ".*\\\$2 = .* ($hex) <puts>.*$gdb_prompt $" { pass "get puts pre-start load address" set puts_address $expect_out(1,string) } -re "$gdb_prompt $" { fail "get puts pre-start load address" set puts_address -1 } timeout { fail "(timeout) get puts pre-start load address" set puts_address -1 } } # Set three breakpoints: One on a function only in mylib.dylib, one # that exists in both mylib.dylib and libSystem, and one in only libSystem. send_gdb "b foo\n" gdb_expect { -re "Breakpoint 1 at ($hex): file .*$gdb_prompt $" { set foo_breakpoint_address $expect_out(1,string) } timeout { fail "could not set breakpoint on foo" } } send_gdb "b puts\n" gdb_expect { -re "Breakpoint 2 at ($hex): file .*$gdb_prompt $" { set puts_breakpoint_address $expect_out(1,string) } timeout { fail "could not set breakpoint on puts" } } send_gdb "b printf\n" gdb_expect { -re "Breakpoint 3 at ($hex).*$gdb_prompt $" { set printf_breakpoint_address $expect_out(1,string) } timeout { fail "could not set breakpoint on printf" } } set foo_offset_pre_start [expr $foo_address - $libsystem_load_addr] set puts_offset_pre_start [expr $puts_address - $libsystem_load_addr] set foo_breakpoint_offset_pre_start [expr $foo_breakpoint_address - $libsystem_load_addr] set puts_breakpoint_offset_pre_start [expr $puts_breakpoint_address - $libsystem_load_addr] set printf_breakpoint_offset_pre_start [expr $printf_breakpoint_address - $libsystem_load_addr] gdb_test "b main" "Breakpoint 4 at .*" gdb_test "run" "Starting program:.*" # Verify that mydlib slid like it should have and libSystem stayed put. send_gdb "info sharedlibrary\n" gdb_expect { -re ".* $decimal ${testlibfile}.dylib *- ($hex) *dyld Y Y $libbinfile at ($hex) \\(offset (0x\[^0\]\[0-9a-fA-F\]+)\\).* $decimal $libsystem_shortname *- $libsystem_load_addr *dyld Y Y $libsystem_full_pathname at $libsystem_load_addr \\(offset 0x0\\).*$gdb_prompt $" { set mylib_slid_addr $expect_out(1,string) set mylib_slid_addr_duplicate $expect_out(1,string) set mylib_slid_offset $expect_out(2,string) pass "mylib slid, libsystem stayed put" if {$mylib_slid_addr != $libsystem_load_addr} { pass "mylib is at address other than libsystem" } else { fail "mylib is at address other than libsystem" } if {$mylib_slid_addr == $mylib_slid_addr_duplicate} { pass "load addresses from info sharedlibrary match" } else { fail "load addresses from info sharedlibrary match" } } -re "$gdb_prompt $" { fail "mylib slid, libsystem stayed put" set mylib_slid_addr -1 set mylib_slid_addr_duplicate -1 set mylib_slid_offset -1 } timeout { fail "(timeout) mylib slid, libsystem stayed put" set mylib_slid_addr -1 set mylib_slid_addr_duplicate -1 set mylib_slid_offset -1 } } # See where foo actually loaded. send_gdb "p foo\n" gdb_expect { -re ".*\\\$$decimal = .* ($hex) <foo>.*$gdb_prompt $" { pass "get foo post-start load address" set foo_loaded_address $expect_out(1,string) } -re "$gdb_prompt $" { fail "get foo pre-start load address" } timeout { fail "(timeout) get foo pre-start load address" } } if {$foo_address != $foo_loaded_address} { pass "address of foo must change when library slid" } else { fail "address of foo must change when library slid" } # See where puts actually loaded. send_gdb "p puts\n" gdb_expect { -re ".*\\\$$decimal = .* ($hex) <puts>.*$gdb_prompt $" { pass "get puts post-start load address" set puts_loaded_address $expect_out(1,string) } -re "$gdb_prompt $" { fail "get puts pre-start load address" } timeout { fail "(timeout) get puts pre-start load address" } } if {$puts_address != $puts_loaded_address} { pass "address of puts must change when library slid" } else { fail "address of puts must change when library slid" } if {$puts_address != $libsystem_puts_address || $puts_loaded_address != $libsystem_puts_address} { pass "evaluation of puts is mylib version, not libsystem" } else { fail "evaluation of puts is mylib version, not libsystem" } # See where printf actually loaded. send_gdb "p printf\n" gdb_expect { -re ".*\\\$$decimal = .* ($hex) <printf>.*$gdb_prompt $" { pass "get printf post-start load address" set printf_loaded_address $expect_out(1,string) } -re "$gdb_prompt $" { fail "get printf pre-start load address" } timeout { fail "(timeout) get printf pre-start load address" } } if {$printf_address == $printf_loaded_address} { pass "address of printf didn't changed when user library slid" } else { fail "address of printf didn't changed when user library slid" } set foo_offset_slid [expr $foo_loaded_address - $mylib_slid_addr] set puts_offset_slid [expr $puts_loaded_address - $mylib_slid_addr] set printf_offset_slid [expr $printf_loaded_address - $libsystem_load_addr] if {$foo_offset_pre_start == $foo_offset_slid} { pass "offset of foo in mylib.dylib stayed same after slide." } else { fail "offset of foo in mylib.dylib stayed same after slide." } if {$puts_offset_pre_start == $puts_offset_slid} { pass "offset of puts in mylib.dylib stayed same after slide." } else { fail "offset of puts in mylib.dylib stayed same after slide." } # See that 'foo' breakpoint moved. send_gdb "info br 1\n" gdb_expect { -re ".*$decimal *breakpoint *keep y *($hex) in foo.*$gdb_prompt $" { set foo_slid_address $expect_out(1,string) } -re "$gdb_prompt $" { set foo_slid_address -1 fail "could not examine foo breakpoint" } timeout { set foo_slid_address -1 fail "(timeout) could not examine foo breakpoint" } } if {$foo_slid_address != $foo_breakpoint_address } { pass "pass breakpoint on foo slid with library" } else { fail "pass breakpoint on foo slid with library" } # See that 'puts' breakpoint moved. send_gdb "info br 2\n" gdb_expect { -re ".*$decimal *breakpoint *keep y *($hex) in puts.*$gdb_prompt $" { set puts_slid_address $expect_out(1,string) } -re "$gdb_prompt $" { set puts_slid_address -1 fail "could not examine puts breakpoint" } timeout { set puts_slid_address -1 fail "(timeout) could not examine puts breakpoint" } } if {$puts_slid_address != $puts_breakpoint_address } { pass "pass breakpoint on puts slid with library" } else { fail "pass breakpoint on puts slid with library" } # See that 'printf' breakpoint didn't moved. send_gdb "info br 3\n" gdb_expect { -re ".*$decimal *breakpoint *keep y *($hex) <printf.*$gdb_prompt $" { set printf_slid_address $expect_out(1,string) } -re "$gdb_prompt $" { set printf_slid_address -1 fail "could not examine printf breakpoint" } timeout { set printf_slid_address -1 fail "(timeout) could not examine printf breakpoint" } } if {$printf_slid_address == $printf_breakpoint_address} { pass "pass breakpoint on printf in Libsystem didn't slide" } else { fail "pass breakpoint on printf in Libsystem didn't slide" } gdb_test "dis 3" "" gdb_test "continue" "Breakpoint 1, foo.*libmylib.c:.*" "continue to slid breakpoint on foo" gdb_exit return 0