aboutsummaryrefslogtreecommitdiff
blob: 10e49367195258b6053e88b87309be54b0ad46ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# Copyright 2010 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 3 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, see <http://www.gnu.org/licenses/>.

# This test only works on GNU/Linux.
if { ![isnative] || [is_remote host] || ![istarget *-linux*] || [skip_shlib_tests]} {
    continue
}

load_lib prelink-support.exp

set test "attach-pie-misread"
set srcfile ${test}.c
set genfile ${objdir}/${subdir}/${test}-gen.h
set executable ${test}
set binfile ${objdir}/${subdir}/${executable}

if {[build_executable_own_libs ${test}.exp $executable $srcfile [list "additional_flags=-fPIE -pie"]] == ""} {
    return -1
}

# Program Headers:
#   Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
#   LOAD           0x000000 0x0000000000400000 0x0000000000400000 0x134f5ec 0x134f5ec R E 0x200000
#   LOAD           0x134f5f0 0x000000000194f5f0 0x000000000194f5f0 0x1dbc60 0x214088 RW  0x200000
#   DYNAMIC        0x134f618 0x000000000194f618 0x000000000194f618 0x000200 0x000200 RW  0x8
#
proc read_phdr {binfile test} {
    set readelf_program [transform readelf]
    set command "exec $readelf_program -Wl $binfile"
    verbose -log "command is $command"
    set result [catch $command output]
    verbose -log "result is $result"
    verbose -log "output is $output"
    if {$result != 0} {
	fail $test
	return
    }
    if ![regexp {\nProgram Headers:\n *Type [^\n]* Align\n(.*?)\n\n} $output trash phdr] {
	fail "$test (no Program Headers)"
	return
    }
    if ![regexp -line {^ *DYNAMIC +0x[0-9a-f]+ +(0x[0-9a-f]+) } $phdr trash dynamic_vaddr] {
	fail "$test (no DYNAMIC found)"
	return
    }
    verbose -log "dynamic_vaddr is $dynamic_vaddr"
    set align_max -1
    foreach {trash align} [regexp -line -all -inline {^ *LOAD .* (0x[0-9]+)$} $phdr] {
	if {$align_max < $align} {
	    set align_max $align
	}
    }
    verbose -log "align_max is $align_max"
    if {$align_max == -1} {
	fail "$test (no LOAD found)"
	return
    }
    pass $test
    return [list $dynamic_vaddr $align_max]
}

set phdr [read_phdr $binfile "readelf initial scan"]
set dynamic_vaddr [lindex $phdr 0]
set align_max [lindex $phdr 1]

set stub_size [format 0x%x [expr "2 * $align_max - ($dynamic_vaddr & ($align_max - 1))"]]
verbose -log "stub_size is $stub_size"

# On x86_64 it is commonly about 4MB.
if {$stub_size > 25000000} {
    xfail "stub size $stub_size is too large"
    return
}

set test "generate stub"
set command "exec $binfile $stub_size >$genfile"
verbose -log "command is $command"
set result [catch $command output]
verbose -log "result is $result"
verbose -log "output is $output"
if {$result == 0} {
    pass $test
} else {
    fail $test
}

set prelink_args [build_executable_own_libs ${test}.exp $executable $srcfile [list "additional_flags=-fPIE -pie -DGEN=\"$genfile\""]]
if {$prelink_args == ""} {
    return -1
}

# x86_64 file has 25MB, no need to keep it.
file delete -- $genfile

set phdr [read_phdr $binfile "readelf rebuilt with stub_size"]
set dynamic_vaddr_prelinkno [lindex $phdr 0]

if ![prelink_yes $prelink_args] {
    return -1
}

set phdr [read_phdr $binfile "readelf with prelink -R"]
set dynamic_vaddr_prelinkyes [lindex $phdr 0]

set first_offset [format 0x%x [expr $dynamic_vaddr_prelinkyes - $dynamic_vaddr_prelinkno]]
verbose -log "first_offset is $first_offset"

set test "first offset is non-zero"
if {$first_offset == 0} {
    fail "$test (-fPIE -pie in effect?)"
} else {
    pass $test
}

set test "start inferior"
gdb_exit

set res [remote_spawn host $binfile];
if { $res < 0 || $res == "" } {
    perror "Spawning $binfile failed."
    fail $test
    return
}
set pid [exp_pid -i $res]
gdb_expect {
    -re "sleeping\r\n" {
	pass $test
    }
    eof {
	fail "$test (eof)"
	remote_exec host "kill -9 $pid"
	return
    }
    timeout {
	fail "$test (timeout)"
	remote_exec host "kill -9 $pid"
	return
    }
}

# Due to alignments it was reproducible with 1 on x86_64 but 2 on i686.
foreach align_mult {1 2} {
    set old_ldprefix $pf_prefix
    lappend pf_prefix "shift-by-$align_mult:"

    # FIXME: We believe there is enough room under FIRST_OFFSET.
    set shifted_offset [format 0x%x [expr "$first_offset - $align_mult * $align_max"]]
    verbose -log "shifted_offset is $shifted_offset"

    # For normal prelink (prelink_yes call), we need to supply $prelink_args.
    # For the prelink `-r' option below, $prelink_args is not required.
    # Moreover, if it was used, the problem would not longer be reproducible
    # as the libraries would also get relocated.
    set command "exec /usr/sbin/prelink -q -N --no-exec-shield -r $shifted_offset $binfile"
    verbose -log "command is $command"
    set result [catch $command output]
    verbose -log "result is $result"
    verbose -log "output is $output"

    set test "prelink -r"
    if {$result == 0 && $output == ""} {
	pass $test
    } else {
	fail $test
    }

    clean_restart $executable

    set test "attach"
    gdb_test_multiple "attach $pid" $test {
	-re "Attaching to program: .*, process $pid\r\n" {
	    # Missing "$gdb_prompt $" is intentional.
	    pass $test
	}
    }

    set test "error on Cannot access memory at address"
    gdb_test_multiple "" $test {
	-re "\r\nCannot access memory at address .*$gdb_prompt $" {
	    fail $test
	}
	-re "$gdb_prompt $" {
	    pass $test
	}
    }

    gdb_test "detach" "Detaching from program: .*"

    set pf_prefix $old_ldprefix
}

remote_exec host "kill -9 $pid"