summaryrefslogtreecommitdiff
blob: aecfd9adb7c05220dff97286d5a6e3c2c75dfa85 (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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# Copyright 1999-2019 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

if [[ ${CATEGORY}/${PN} == app-arch/xz-utils
   && ${EBUILD_PHASE} == setup
   && ${CPPFLAGS} == *-isystem*
]]; then
	# During bootstrap-prefix.sh we set CPPFLAGS="-isystem $EPREFIX/usr/include",
	# but on Cygwin xz-utils eventually does use the windres compiler,
	# which fails to understand -isystem.
	# As xz-utils has no need for -isystem here, we can use -I instead.
	CPPFLAGS=${CPPFLAGS//-isystem /-I}
fi

post_pkg_preinst() {
	cygwin-post_pkg_preinst
}

pre_pkg_postinst() {
	cygwin-pre_pkg_postinst
}

post_pkg_prerm() {
	cygwin-post_pkg_prerm
}

cygwin-post_pkg_preinst() {
	cygwin-rebase-post_pkg_preinst
}

cygwin-pre_pkg_postinst() {
	cygwin-rebase-pre_pkg_postinst
}

cygwin-post_pkg_prerm() {
	cygwin-rebase-post_pkg_prerm
}

###############################################################################
# To allow a Windows DLL to reside in memory just once for multiple processes,
# each process needs to be able to map that DLL at the same base address,
# without the need for a dynamic rebase.  However, this requires the DLL's
# base address to be unique across all DLLs potentially loaded into a single
# process.  Hence the PE/COFF binary format allows to define a preferred base
# address for DLLs, but leaves it up to the package manager to maintain that
# base address to be unique across all DLLs related together.
# (Not sure how exactly ASLR plays in here, though.)
#
# Furthermore, for the Cygwin fork, it is crucial that the child process is
# able to reload a DLL at the very same address as in the parent process.
# Having unique preferred base addresses across all related DLLs does help
# here as well.
#
# The Cygwin rebase utility does maintain some database holding the size and
# preferred base address for each DLL, and allows to update a DLL's preferred
# base address to not conflict with already installed DLLs.
#
# As updating the preferred base address for a DLL in use is a bad idea, we
# need to update the base address while the DLL is in staging directory, and
# update the rebase database after merging the DLL to the live file system.
#
# This allows to define a new preferred base address for a DLL that would
# replace an existing one, because during fork we really want to use the
# old version in the child process, which is verified using the preferred
# base address value to be identical in parent and child process.
#
# Otherwise, the new DLL may have identical size and preferred base address
# as the old DLL, and we may not detect a different DLL in the fork child.
#
# For unmerging a DLL: The Cygwin rebase utility does check if a DLL found
# in the database does still exist, removing that database entry otherwise.
###############################################################################

cygwin-rebase-get_pendingdir() {
	echo "var/db/rebase/pending"
}

cygwin-rebase-get_mergeddir() {
	echo "var/db/rebase/merged"
}

cygwin-rebase-get_listname() {
	echo "dlls_${CATEGORY}_${P}${PR:+-}${PR}"
}

cygwin-rebase-get_rebase_program() {
	[[ ${CHOST} == "${CBUILD}" ]] || return 1
	local pfx
	for pfx in "${EPREFIX}" "${BROOT:-${PORTAGE_OVERRIDE_EPREFIX}}"
	do
		[[ -x ${pfx}/usr/bin/rebase ]] || continue
		echo "${pfx}/usr/bin/rebase"
		return 0
	done
	return 1
}

cygwin-rebase-post_pkg_preinst() {
	# Ensure database is up to date for when dlls were merged but
	# subsequent cygwin-rebase-merge-pending was not executed.
	einfo "Cygwin: Merging pending files into rebase database..."
	cygwin-rebase-merge pending
	eend $?

	local listname=$(cygwin-rebase-get_listname)
	local pendingdir=$(cygwin-rebase-get_pendingdir)
	local rebase_program=$(cygwin-rebase-get_rebase_program)

	if [[ ${CATEGORY}/${PN} == 'app-admin/cygwin-rebase' ]]
	then
		local mergeddir=$(cygwin-rebase-get_mergeddir)
		keepdir "/${pendingdir}"
		keepdir "/${mergeddir}"
	fi

	einfo "Cygwin: Rebasing new files..."
	(
		set -e
		cd "${ED}"

		# The list of suffixes is found in the rebaseall script.
		find . -type f \
			'(' -name '*.dll' \
			 -o -name '*.so' \
			 -o -name '*.oct' \
			')' \
		| sed -e "s|^\.|${EPREFIX}|" > "${T}/rebase-filelist"
		[[ "${PIPESTATUS[*]}" == '0 0' ]]

		# Nothing found to rebase in this package.
		[[ -s ${T}/rebase-filelist ]] || exit 0

		mkdir -p "./${pendingdir}"
		cp -f "${T}/rebase-filelist" "./${pendingdir}/${listname}"

		# Without the rebase program, do not perform a rebase.
		[[ ${rebase_program} ]] || exit 0

		sed -ne "/^${EPREFIX//\//\\/}\\//{s|^${EPREFIX}/||;p}" "./${pendingdir}/${listname}" \
		| "${rebase_program}" --verbose --oblivious --database --filelist=-
		[[ "${PIPESTATUS[*]}" == '0 0' ]]
	)
	eend $?
}

cygwin-rebase-pre_pkg_postinst() {
	if [[ ${CATEGORY}/${PN} == 'app-admin/cygwin-rebase' ]]
	then
		einfo "Cygwin: Updating rebase database with installed files..."
		cygwin-rebase-merge merged
		eend $?
	fi
	einfo "Cygwin: Merging updated files into rebase database..."
	cygwin-rebase-merge pending
	eend $?
}

cygwin-rebase-merge() {
	local mode=${1}

	local rebase_program=$(cygwin-rebase-get_rebase_program)
	[[ ${rebase_program} ]] || return 0

	local pendingdir=''
	local mergeddir=''
	case ${mode} in
	pending)
		pendingdir=$(cygwin-rebase-get_pendingdir)
		mergeddir=$(cygwin-rebase-get_mergeddir)
		;;
	merged)
		pendingdir=$(cygwin-rebase-get_mergeddir)
		mergeddir=''
		;;
	*)
		die "Invalid mode '${mode}'."
		;;
	esac

	(
		set -e
		cd "${EROOT}"

		[[ -r ./${pendingdir}/. ]]
		[[ -r ./${mergeddir}/. ]]

		find ./"${pendingdir}" -mindepth 1 -maxdepth 1 -type f -name 'dlls_*' \
			-exec sed -ne "/^${EPREFIX//\//\\/}\\//{s|^${EPREFIX}/||;p}" {} + \
			| "${rebase_program}" --verbose --merge-files --database --filelist=-
		[[ "${PIPESTATUS[*]}" == '0 0' ]]

		[[ ${mode} == 'pending' ]] || exit 0

		find "./${pendingdir}" -maxdepth 1 -type f \
			-exec mv -f -t "./${mergeddir}/" {} +
	)
	[[ $? == 0 ]] || die "Merging ${mode} files into rebase database failed."
}

cygwin-rebase-post_pkg_prerm() {
	# The pending list is installed as part of the package, but
	# the merged list is not.  Move from merged back to pending,
	# in case the unmerge fails...
	local pendingdir=$(cygwin-rebase-get_pendingdir)
	local mergeddir=$(cygwin-rebase-get_mergeddir)
	local listname=$(cygwin-rebase-get_listname)
	(
		set -e
		cd "${EROOT}"
		[[ -w ./${mergeddir}/. ]]
		[[ -w ./${pendingdir}/. ]]
		if [[ -s ./${mergeddir}/${listname} ]]
		then
			mv -f "./${mergeddir}/${listname}" "./${pendingdir}/${listname}" || :
		fi
		rm -f "./${mergeddir}/${listname}"
	)
}