Commit | Line | Data |
---|---|---|
b8040f19 JK |
1 | # Copyright 2010 Free Software Foundation, Inc. |
2 | ||
3 | # This program is free software; you can redistribute it and/or modify | |
4 | # it under the terms of the GNU General Public License as published by | |
5 | # the Free Software Foundation; either version 3 of the License, or | |
6 | # (at your option) any later version. | |
7 | # | |
8 | # This program is distributed in the hope that it will be useful, | |
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | # GNU General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU General Public License | |
14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | ||
16 | # This test only works on GNU/Linux. | |
17 | if { ![isnative] || [is_remote host] || ![istarget *-linux*] } { | |
18 | continue | |
19 | } | |
20 | ||
21 | set test "break-interp" | |
22 | set binprefix ${objdir}/${subdir}/${test} | |
23 | # Only to get the $interp_system name. | |
24 | set srcfile_test "start.c" | |
25 | set binfile_test ${test}-test | |
26 | set srcfile "start.c" | |
27 | if {[build_executable ${test}.exp $binfile_test ${srcfile_test} {}] == -1} { | |
28 | return -1 | |
29 | } | |
30 | ||
31 | # Return the interpreter filename string. | |
32 | # Return "" if no interpreter was found. | |
33 | proc section_get {exec section} { | |
34 | global objdir | |
35 | global subdir | |
36 | set tmp "${objdir}/${subdir}/break-interp.interp" | |
37 | set objcopy_program [transform objcopy] | |
38 | ||
39 | set command "exec $objcopy_program -O binary --set-section-flags $section=A --change-section-address $section=0 -j $section $exec $tmp" | |
40 | verbose -log "command is $command" | |
41 | set result [catch $command output] | |
42 | verbose -log "result is $result" | |
43 | verbose -log "output is $output" | |
44 | if {$result == 1} { | |
45 | return "" | |
46 | } | |
47 | set fi [open $tmp] | |
48 | fconfigure $fi -translation binary | |
49 | set data [read $fi] | |
50 | close $fi | |
51 | #file delete $tmp | |
52 | # .interp has size $len + 1 but .gnu_debuglink contains garbage after \000. | |
53 | set len [string first \000 $data] | |
54 | if {$len < 0} { | |
55 | verbose -log "section $section not found" | |
56 | return "" | |
57 | } | |
58 | set retval [string range $data 0 [expr $len - 1]] | |
59 | verbose -log "section $section is <$retval>" | |
60 | return $retval | |
61 | } | |
62 | ||
63 | # Note: The separate debug info file content build-id/crc32 are not verified | |
64 | # contrary to the GDB search algorithm skipping non-matching ones. | |
65 | proc system_debug_get {exec} { | |
66 | global debug_root | |
67 | ||
68 | set exec_build_id_debug [build_id_debug_filename_get $exec] | |
69 | set debug_base "[file tail $exec].debug" | |
70 | set exec_dir [file dirname $exec] | |
71 | ||
72 | # isfile returns 1 even for symlinks to files. | |
73 | set retval $debug_root/$exec_build_id_debug | |
74 | if [file isfile $retval] { | |
75 | return $retval | |
76 | } | |
77 | set retval $exec_dir/$debug_base | |
78 | if [file isfile $retval] { | |
79 | return $retval | |
80 | } | |
81 | set retval $exec_dir/.debug/$debug_base | |
82 | if [file isfile $retval] { | |
83 | return $retval | |
84 | } | |
85 | set retval $debug_root/$exec_dir/$debug_base | |
86 | if [file isfile $retval] { | |
87 | return $retval | |
88 | } | |
89 | return "" | |
90 | } | |
91 | ||
92 | gdb_exit | |
93 | gdb_start | |
94 | set debug_root "" | |
95 | set test "show debug-file-directory" | |
96 | gdb_test_multiple $test $test { | |
97 | -re "The directory where separate debug symbols are searched for is \"(.*)\".\r\n$gdb_prompt $" { | |
98 | set debug_root $expect_out(1,string) | |
99 | } | |
100 | } | |
101 | ||
102 | set interp_system [section_get ${objdir}/${subdir}/$binfile_test .interp] | |
103 | set interp_system_debug [system_debug_get $interp_system] | |
104 | verbose -log "$interp_system has debug $interp_system_debug" | |
105 | ||
106 | proc prelinkNO_run {arg} { | |
107 | set command "exec /usr/sbin/prelink -uN $arg" | |
108 | verbose -log "command is $command" | |
109 | set result [catch $command output] | |
110 | verbose -log "result is $result" | |
111 | verbose -log "output is $output" | |
112 | return [list $result $output] | |
113 | } | |
114 | ||
115 | proc prelinkNO {arg {name {}}} { | |
116 | if {$name == ""} { | |
117 | set name [file tail $arg] | |
118 | } | |
119 | set test "unprelink $name" | |
120 | set run [prelinkNO_run $arg] | |
121 | set result [lindex $run 0] | |
122 | set output [lindex $run 1] | |
123 | if {$result == 0 && $output == ""} { | |
124 | verbose -log "$name has been now unprelinked" | |
125 | set run [prelinkNO_run $arg] | |
126 | set result [lindex $run 0] | |
127 | set output [lindex $run 1] | |
128 | } | |
129 | # Last line does miss the trailing \n. | |
130 | if {$result == 1 && [regexp {^(/usr/sbin/prelink: [^ ]* does not have .gnu.prelink_undo section\n?)*$} $output]} { | |
131 | pass $test | |
132 | return 1 | |
133 | } else { | |
134 | fail $test | |
135 | return 0 | |
136 | } | |
137 | } | |
138 | ||
139 | proc prelinkYES {arg {name ""}} { | |
140 | if {$name == ""} { | |
141 | set name [file tail $arg] | |
142 | } | |
143 | set test "prelink $name" | |
144 | set command "exec /usr/sbin/prelink -qNR --no-exec-shield $arg" | |
145 | verbose -log "command is $command" | |
146 | set result [catch $command output] | |
147 | verbose -log "result is $result" | |
148 | verbose -log "output is $output" | |
149 | if {$result == 0 && $output == ""} { | |
150 | pass $test | |
151 | return 1 | |
152 | } else { | |
153 | fail $test | |
154 | return 0 | |
155 | } | |
156 | } | |
157 | ||
158 | # Resolve symlinks. | |
159 | proc symlink_resolve {file} { | |
160 | set loop 0 | |
161 | while {[file type $file] == "link"} { | |
162 | set target [file readlink $file] | |
163 | if {[file pathtype $target] == "relative"} { | |
164 | set src2 [file dirname $file]/$target | |
165 | } else { | |
166 | set src2 $target | |
167 | } | |
168 | verbose -log "Resolved symlink $file targetting $target as $src2" | |
169 | set file $src2 | |
170 | ||
171 | set loop [expr $loop + 1] | |
172 | if {$loop > 30} { | |
173 | fail "Looping symlink resolution for $file" | |
174 | return "" | |
175 | } | |
176 | } | |
177 | return $file | |
178 | } | |
179 | ||
180 | proc copy {src dest} { | |
181 | set src [symlink_resolve $src] | |
182 | # Test name would contain build-id hash for symlink-unresolved $src. | |
183 | set test "copy [file tail $src] to [file tail $dest]" | |
184 | set command "file copy -force $src $dest" | |
185 | verbose -log "command is $command" | |
186 | if [catch $command] { | |
187 | fail $test | |
188 | return 0 | |
189 | } else { | |
190 | pass $test | |
191 | return 1 | |
192 | } | |
193 | } | |
194 | ||
195 | proc strip_debug {dest} { | |
196 | set test "strip [file tail $dest]" | |
197 | set strip_program [transform strip] | |
198 | set command "exec $strip_program --strip-debug $dest" | |
199 | verbose -log "command is $command" | |
200 | if [catch $command] { | |
201 | fail $test | |
202 | return 0 | |
203 | } else { | |
204 | pass $test | |
205 | return 1 | |
206 | } | |
207 | } | |
208 | ||
209 | # `runto' does not check we stopped really at the function we specified. | |
210 | proc reach {func command} { | |
211 | global gdb_prompt | |
212 | ||
213 | if [gdb_breakpoint $func allow-pending] { | |
214 | set test "reach $func" | |
215 | gdb_test_multiple $command $test { | |
216 | -re "Breakpoint \[0-9\]+, $func \\(.*\\) at .*:\[0-9\]+\r\n.*$gdb_prompt $" { | |
217 | pass $test | |
218 | } | |
219 | -re "Breakpoint \[0-9\]+, \[0-9xa-f\]+ in $func \\(\\)( from .*)?\r\n$gdb_prompt $" { | |
220 | pass $test | |
221 | } | |
222 | } | |
223 | } | |
224 | } | |
225 | ||
226 | proc test_ld {file ifmain} { | |
227 | global srcdir subdir gdb_prompt | |
228 | ||
229 | # First test normal `file'-command loaded $FILE with symbols. | |
230 | ||
231 | gdb_exit | |
232 | gdb_start | |
233 | # Clear it to never find any separate debug infos in $debug_root. | |
234 | gdb_test "set debug-file-directory" | |
235 | gdb_reinitialize_dir $srcdir/$subdir | |
236 | gdb_load $file | |
237 | ||
238 | reach "dl_main" run | |
239 | if $ifmain { | |
240 | reach "main" continue | |
241 | } | |
242 | } | |
243 | ||
244 | # Create separate binaries for each testcase - to make the possible reported | |
245 | # problem reproducible after the whole test run finishes. | |
246 | ||
247 | set old_ldprefix $pf_prefix | |
248 | foreach ldprelink {NO YES} { | |
249 | foreach ldsepdebug {NO IN SEP} { | |
250 | # Skip running the ldsepdebug test if we do not have system separate | |
251 | # debug info available. | |
252 | if {$interp_system_debug == "" && $ldsepdebug == "SEP"} { | |
253 | continue | |
254 | } | |
255 | ||
256 | set ldname "LDprelink${ldprelink}debug${ldsepdebug}" | |
257 | set interp $binprefix-$ldname | |
258 | ||
259 | # prelink needs to always prelink all the dependencies to do any file | |
260 | # modifications of its files. ld.so also needs all the dependencies to | |
261 | # be prelinked to omit the relocation process. In-memory file offsets | |
262 | # are not dependent whether ld.so went the prelink way or through the | |
263 | # relocation process. | |
264 | # | |
265 | # For GDB we are not interested whether prelink succeeds as it is | |
266 | # transparent to GDB. GDB is being tested for differences of file | |
267 | # offsets vs. in-memory offsets. So we have to prelink even ld.so for | |
268 | # the BIN modification to happen but we need to restore the original | |
269 | # possibly unprelinked ld.so to test all the combinations for GDB. | |
270 | set interp_saved ${interp}-saved | |
271 | ||
272 | set pf_prefix $old_ldprefix | |
273 | lappend pf_prefix "$ldname:" | |
274 | ||
275 | if {$ldsepdebug == "NO"} { | |
276 | copy $interp_system $interp | |
277 | # Never call strip-debug before unprelink: | |
278 | # prelink: ...: Section .note.gnu.build-id created after prelinking | |
279 | if ![prelinkNO $interp] { | |
280 | continue | |
281 | } | |
282 | strip_debug $interp | |
283 | } elseif {$ldsepdebug == "IN" && $interp_system_debug == ""} { | |
284 | copy $interp_system $interp | |
285 | } elseif {$ldsepdebug == "IN" && $interp_system_debug != ""} { | |
286 | copy $interp_system $interp | |
287 | copy $interp_system_debug "${interp}.debug" | |
288 | # eu-unstrip: DWARF data in '...' not adjusted for prelinking bias; consider prelink -u | |
289 | if {![prelinkNO $interp] || ![prelinkNO "${interp}.debug"]} { | |
290 | continue | |
291 | } | |
292 | set test "eu-unstrip unprelinked:[file tail $interp_system] + [file tail $interp_system_debug] to [file tail $interp]" | |
293 | set command "exec eu-unstrip -o $interp $interp ${interp}.debug" | |
294 | verbose -log "command is $command" | |
295 | if [catch $command] { | |
296 | setup_xfail *-*-* | |
297 | fail $test | |
298 | continue | |
299 | } else { | |
300 | pass $test | |
301 | } | |
302 | } elseif {$ldsepdebug == "SEP" && $interp_system_debug == ""} { | |
303 | copy $interp_system $interp | |
304 | # eu-unstrip: DWARF data in '...' not adjusted for prelinking bias; consider prelink -u | |
305 | if ![prelinkNO $interp] { | |
306 | continue | |
307 | } | |
308 | gdb_gnu_strip_debug $interp | |
309 | } elseif {$ldsepdebug == "SEP" && $interp_system_debug != ""} { | |
310 | copy $interp_system $interp | |
311 | copy $interp_system_debug "${interp}.debug" | |
312 | } | |
313 | ||
314 | if {$ldsepdebug == "SEP"} { | |
315 | if ![prelinkNO "${interp}.debug"] { | |
316 | continue | |
317 | } | |
318 | } else { | |
319 | file delete "${interp}.debug" | |
320 | } | |
321 | ||
322 | if ![prelink$ldprelink $interp] { | |
323 | continue | |
324 | } | |
325 | test_ld $interp 0 | |
326 | ||
327 | if ![copy $interp $interp_saved] { | |
328 | continue | |
329 | } | |
330 | set old_binprefix $pf_prefix | |
331 | foreach binprelink {NO YES} { | |
332 | foreach binsepdebug {NO IN SEP} { | |
333 | foreach binpie {NO YES} { | |
334 | # This combination is not possible, non-PIE (fixed address) | |
335 | # binary cannot be prelinked to any (other) address. | |
336 | if {$binprelink == "YES" && $binpie == "NO"} { | |
337 | continue | |
338 | } | |
339 | ||
340 | set binname "BINprelink${binprelink}debug${binsepdebug}pie${binpie}" | |
341 | set exec $binprefix-$binname | |
342 | set dir ${exec}.d | |
343 | ||
344 | set pf_prefix $old_binprefix | |
345 | lappend pf_prefix "$binname:" | |
346 | ||
347 | set opts "additional_flags=-Wl,--dynamic-linker,$interp,-rpath,$dir" | |
348 | if {$binsepdebug != "NO"} { | |
349 | lappend opts {debug} | |
350 | } | |
351 | if {$binpie == "YES"} { | |
352 | lappend opts {additional_flags=-fPIE -pie} | |
353 | } | |
354 | if {[build_executable ${test}.exp [file tail $exec] $srcfile $opts] == -1} { | |
355 | continue; | |
356 | } | |
357 | if {$binsepdebug == "SEP"} { | |
358 | gdb_gnu_strip_debug $exec | |
359 | # Just a sanity check. As gdb_gnu_strip_debug uses the | |
360 | # "[file dirname $exec]/.debug/[file tail $exec].debug" | |
361 | # variant delete the higher-priority exec.debug file. | |
362 | file delete "$exec.debug" | |
363 | } | |
364 | ||
365 | # Supply a self-sufficent directory $dir with the required | |
366 | # libraries. To make an executable properly prelinked all | |
367 | # its dependencies on libraries must be also prelinked. If | |
368 | # some of the system libraries is currently not prelinked | |
369 | # we have no right to prelink (modify it) at its current | |
370 | # system place. | |
371 | ||
372 | file delete -force $dir | |
373 | file mkdir $dir | |
374 | ||
375 | set command "ldd $exec" | |
376 | set result [catch "exec $command" output] | |
377 | verbose -log "result of $command is $result" | |
378 | verbose -log "output of $command is $output" | |
379 | if {$result != 0 || $output == ""} { | |
380 | fail $command | |
381 | } else { | |
382 | pass $command | |
383 | } | |
384 | ||
385 | # gdb testsuite will put there also needless -lm. | |
386 | set test "$command output contains libc" | |
387 | set libc [regexp -all -inline -line {^.* => (/[^ ]+).*$} $output] | |
388 | if {[llength $libc] == 0} { | |
389 | fail $test | |
390 | } else { | |
391 | pass $test | |
392 | } | |
393 | ||
394 | set dests {} | |
395 | for {set i 1} {$i < [llength $libc]} {incr i 2} { | |
396 | set abspath [lindex $libc $i] | |
397 | set dest "$dir/[file tail $abspath]" | |
398 | copy $abspath $dest | |
399 | lappend dests $dest | |
400 | } | |
401 | ||
402 | if {[prelink$binprelink "--dynamic-linker=$interp --ld-library-path=$dir $exec $interp [concat $dests]" $exec] | |
403 | && [copy $interp_saved $interp]} { | |
404 | test_ld $exec 1 | |
405 | } | |
406 | } | |
407 | } | |
408 | } | |
409 | ||
410 | file delete $interp_saved | |
411 | } | |
412 | } | |
413 | set pf_prefix $old_ldprefix |