Avoid scanning through all relocations in write_reloc.
[ksplice.git] / ksplice-create.in
blobfec812a1aded8da7587a62634d6ef968a85d029a
1 #!/usr/bin/perl
3 # Copyright (C) 2007-2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
4 # Copyright (C) 2008 Anders Kaseorg <andersk@mit.edu>,
5 # Tim Abbott <tabbott@mit.edu>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License, version 2.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
18 # 02110-1301, USA.
20 use strict;
21 use warnings;
22 use lib 'KSPLICE_DATA_DIR';
23 use Ksplice;
25 my ($patchfile, $diffext, $orig_config_dir, $jobs, $kid);
26 my @only_targets;
27 my @extra_match;
28 my $standalone;
29 my ($prebuild, $skip_prebuild) = (0, 0);
30 my @patch_opt = "-p1";
31 GetOptions(@common_options,
32 "id=s" => \$kid,
33 "patch=s" => \$patchfile,
34 "diffext=s" => \$diffext,
35 "prebuild" => \$prebuild,
36 "only-targets=s" => \@only_targets,
37 "extra-match=s" => \@extra_match,
38 "standalone!" => \$standalone,
39 "skip-prebuild" => \$skip_prebuild,
40 "jobs|j:i" => \$jobs,
41 "config=s" => \$orig_config_dir,
42 "patch-opt=s" => \@patch_opt) or pod2usage(1);
44 pod2usage(1) if($help || scalar(@ARGV) != 1);
45 my $actions = (defined $patchfile) + (defined $diffext) + ($prebuild);
46 pod2usage(1) if($actions != 1);
48 my ($linuxtree) = (abs_path($ARGV[0]));
50 if(!defined $orig_config_dir) {
51 $orig_config_dir = "$linuxtree/ksplice";
53 else {
54 $orig_config_dir = abs_path($orig_config_dir);
55 if($orig_config_dir =~ $linuxtree) {
56 die "Aborting: User-specified ORIG_CONFIG cannot be KERNEL_SOURCE or a subdirectory";
59 if(!defined $orig_config_dir || ! -d $orig_config_dir) {
60 die "Failed to find ORIG_CONFIG directory ($orig_config_dir)";
62 if(! -e "$orig_config_dir/.config") {
63 die "Failed to find .config file in ORIG_CONFIG directory";
65 if(! -e "$orig_config_dir/System.map") {
66 die "Failed to find System.map file in ORIG_CONFIG directory";
69 my $kernel_headers_dir = "$orig_config_dir/build";
70 $kernel_headers_dir = $linuxtree unless(-d $kernel_headers_dir);
72 my @kbuild_flags = ();
73 if(-e "$orig_config_dir/flags") {
74 open(FLAGS, '<', "$orig_config_dir/flags") or die;
75 local $/;
76 @kbuild_flags = shellwords(scalar <FLAGS>);
77 close(FLAGS);
80 $ENV{KSPLICE_VERBOSE} = $Verbose::level;
81 $ENV{KSPLICE_CONFIG_DIR} = $orig_config_dir;
83 my @chars = ('a'..'z', 0..9);
84 $kid = join '', map { $chars[int(rand(36))] } 0..7 if(!defined $kid);
85 my $ksplice = "ksplice-$kid";
86 $ENV{KSPLICE_KID} = $kid;
88 # Some versions of Fedora have System.map files whose symbol addresses disagree
89 # with the running kernel by a constant address offset. Here, Ksplice notes the
90 # System.map address for printk so that it can later compare this address against
91 # the kernel's address for printk. This comparison helps Ksplice work around
92 # this Fedora problem, and this comparison also helps Ksplice detect whether
93 # the user has provided an incorrect System.map file.
94 my $map_printk = runstr("$datadir/ksplice-obj.pl", "system_map_lookup", "printk");
96 print "Starting kernel builds (this process might take a long time)...\n"
97 if($Verbose::level >= 0);
99 $patchfile = abs_path($patchfile) if(defined $patchfile);
101 my $origdir = getcwd();
102 chdir($linuxtree);
104 my @make = ("make", "-rR");
105 if(defined $jobs) {
106 push @make, "-j$jobs";
107 } elsif(defined $ENV{CONCURRENCY_LEVEL}) {
108 push @make, "-j$ENV{CONCURRENCY_LEVEL}";
111 if($Verbose::level >= 2) {
112 push @make, "V=1";
113 } elsif($Verbose::level < 0) {
114 push @make, "-s";
117 $ENV{PATH} = "$datadir:$ENV{PATH}";
118 my @make_ksplice = (@make, "-f", "$datadir/Makefile.ksplice", @kbuild_flags);
120 sub revert_orig() {
121 for(split(/\0/, runstr(qw(find -name *.KSPLICE* -print0)))) {
122 if(my ($file) = m/^(.*)\.KSPLICE_pre$/) {
123 rename($_, $file);
124 runval("$datadir/ksplice-obj.pl", "snap", "$file.KSPLICE") if(-e "$file.KSPLICE");
125 } elsif(m/\.KSPLICE_(?:primary|helper)$/) {
126 unlink($_) if(-e $_);
130 revert_orig();
132 if(!$skip_prebuild) {
133 copy("$orig_config_dir/.config", "$linuxtree/.config");
134 utime((stat("$orig_config_dir/.config"))[8, 9], "$linuxtree/.config");
135 my @snap_flags = ("KSPLICE_MODE=snap");
136 if(-e "include/config/kernel.release") {
137 push(@snap_flags, "-o", "include/config/kernel.release");
139 runval_raw(@make_ksplice, @snap_flags) == 0 or
140 die "Aborting: Prebuild failed";
141 sleep(1);
143 exit(0) if($prebuild);
145 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
146 copy($patchfile, "$tmpdir/patch") if(defined $patchfile);
147 $patchfile = "$tmpdir/patch";
149 if(defined $diffext) {
150 open(PATCH, '>', $patchfile) or die;
151 for(split(/\0/, runstr("find", "-name", "*$diffext", "-print0"))) {
152 my ($file) = /^(.*)\Q$diffext\E/ or die;
153 print PATCH runstr("diff", "-u", "--", $file, $_);
155 close(PATCH) or die;
156 @patch_opt = ("-p0");
159 my $kmodsrc = "$tmpdir/kmodsrc";
160 runval("cp", "-a", "--", "$datadir/kmodsrc", $kmodsrc);
161 $ENV{KSPLICE_KMODSRC} = $kmodsrc;
163 my @make_kmodsrc = (@make, "-C", $kernel_headers_dir, "M=$kmodsrc", "KSPLICE_KID=$kid", "KSPLICE_VERSION=PACKAGE_VERSION", "map_printk=$map_printk");
165 if (!defined($standalone)) {
166 $standalone = (runval_raw(qw(grep -q ^CONFIG_KSPLICE=[ym]$), "$linuxtree/.config") != 0);
168 push(@make_kmodsrc, "KSPLICE_STANDALONE=1") if ($standalone);
170 runval(@make_kmodsrc);
172 @patch_opt = ("-s", @patch_opt) if ($Verbose::level < 0);
174 runval_infile($patchfile, "patch", @patch_opt, "-bz", ".KSPLICE_pre");
175 push @make_ksplice, "KSPLICE_EXTRA_MATCH=@extra_match" if (@extra_match);
176 push @make_ksplice, "KSPLICE_ONLY_TARGETS=@only_targets" if (@only_targets);
177 if(runval_raw(@make_ksplice, "KSPLICE_MODE=diff") != 0) {
178 revert_orig();
179 die "Aborting: Applying the patch appears to break the kernel build";
182 sub copy_debug {
183 my ($file) = @_;
184 my ($dir, $base) = (dirname($file), basename($file));
185 -d "$tmpdir/objects/$dir" or mkpath("$tmpdir/objects/$dir");
186 copy($file, "$tmpdir/objects/$file");
187 my $cmdfile = "$dir/.$base.cmd";
188 copy($cmdfile, "$tmpdir/objects/$cmdfile") if(-e $cmdfile);
191 mkdir("$tmpdir/objects");
192 for (split(/\0/, runstr(qw(find -name *.KSPLICE* ! ( -name *.KSPLICE -empty ) ! -name .*.KSPLICE.cmd -print0)))) {
193 copy_debug($_);
194 copy_debug($1) if (m/^(.*)\.KSPLICE_pre$/);
197 my @modules = ();
198 foreach(glob(".tmp_versions/*.mod.KSPLICE")) {
199 open MOD, '<', $_;
200 chomp(my $mod = <MOD>);
201 close MOD;
202 foreach my $collect ("$mod.o.KSPLICE", "$mod.o.KSPLICE_primary",
203 "$mod.o.KSPLICE_helper") {
204 copy($collect, "$kmodsrc/" . basename($collect));
206 push @modules, basename($mod);
209 if(!@modules) {
210 revert_orig();
211 die "Aborting: No changes detected";
214 revert_orig();
216 runval(@make_kmodsrc, "modules", "KSPLICE_MODULES=@modules");
218 chdir($tmpdir);
219 mkdir($ksplice);
220 move($patchfile, $ksplice);
221 foreach my $mod (@modules) {
222 (my $target = $mod) =~ s/-/_/g;
223 my $mid = "${kid}_$target";
224 my $module = "ksplice-$mid";
225 rename("$kmodsrc/$module.ko", "$ksplice/$module.ko");
226 rename("$kmodsrc/$module-helper.ko", "$ksplice/$module-helper.ko");
228 rename("$kmodsrc/ksplice-$kid.ko", "$ksplice/ksplice-$kid.ko") if ($standalone);
230 mkdir("$ksplice/debug");
231 rename("objects", "$ksplice/debug/objects");
232 rename("$kmodsrc", "$ksplice/debug/kmodsrc");
233 runval("tar", "czf", "$ksplice.tar.gz", "--", $ksplice);
234 copy("$ksplice.tar.gz", "$origdir/$ksplice.tar.gz");
236 print "Ksplice update tarball written to $ksplice.tar.gz\n";
237 exit(0);
239 =head1 NAME
241 ksplice-create - Create a set of kernel modules for a rebootless kernel update
243 =head1 SYNOPSIS
245 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--patch=>I<PATCH_FILE> I<KERNEL_SOURCE>
247 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--diffext=>I<EXTENSION> I<KERNEL_SOURCE>
249 B<ksplice-create> [B<--config=>I<ORIG_CONFIG>] B<--prebuild> I<KERNEL_SOURCE>
251 =head1 DESCRIPTION
253 B<ksplice-create> creates a set of Ksplice kernel modules that, when loaded,
254 will apply a user-specified source code patch to the running binary kernel.
256 Before you use B<ksplice-create> on a patch, you should confirm that the
257 desired source code change does not make any semantic changes to kernel data
258 structures--that is, changes that would require existing instances of kernel
259 data structures to be transformed (e.g., a patch that adds a field to a global
260 data structure would require the existing data structures to change). If you
261 use Ksplice on a patch that changes data structure semantics, Ksplice will not
262 detect the problem and you could experience kernel problems as a result.
264 The to-be-applied source code patch can be specified by providing a L<patch(1)>
265 file (B<--patch=>I<PATCH_FILE>) or by providing a file extension
266 (B<--diffext=>I<EXTENSION>).
268 If a file extension is specified, then the desired source code patch will be
269 determined by comparing all of the files in the I<KERNEL_SOURCE> directory tree
270 whose names end with the extra extension I<EXTENSION> against the corresponding
271 files without the extra extension. Only the new files containing the extra
272 extension in their filenames should be modified.
274 Here is an example of using a file extension to specify a patch:
276 $ cp KERNEL_SOURCE/kernel/sys.c KERNEL_SOURCE/kernel/sys.c.prctl_fixed
277 [edit sys.c.prctl_fixed to include the desired changes]
278 $ ksplice-create --diffext=.prctl_fixed KERNEL_SOURCE
280 KERNEL_SOURCE must be a directory containing the to-be-updated kernel's
281 original source code. If your Linux distribution applies patches to the Linux
282 kernel during the kernel build process, then those patches must be applied to
283 the I<KERNEL_SOURCE> directory before invoking B<ksplice-create> on that
284 directory. B<ksplice-create> will not modify the source code in the
285 I<KERNEL_SOURCE> directory tree, but it will perform a kernel build in that
286 directory tree.
288 I<ORIG_CONFIG> can be used to specify the directory containing the
289 to-be-updated kernel's original F<.config> file and original F<System.map> file
290 (the files should have exactly those names). I<ORIG_CONFIG> defaults to
291 I<KERNEL_SOURCE>B</ksplice>.
293 The default L<gcc(1)> compiler and L<as(1)> assembler on the system should be as
294 close to the compiler and assembler originally used to build the running kernel
295 as possible. If the current compiler and linker are too different from the
296 original compiler and linker, B<ksplice-apply> will abort when applying the
297 update.
299 B<ksplice-create> outputs a L<tar(1)> file, compressed with L<gzip(1)>,
300 containing the desired Ksplice update modules. This tarball will be created in
301 the current directory, and it can be manipulated using the other Ksplice
302 utilities, such as B<ksplice-apply>.
304 The first time that B<ksplice-create> is invoked on a I<KERNEL_SOURCE>
305 directory, it must build that kernel from scratch, which is much slower than
306 the rest of the update-creation process. B<--prebuild> can be used to perform
307 this initial kernel build without providing a source code patch.
309 In order to patch a function that has previously been patched by Ksplice, the
310 user needs to ensure that the I<KERNEL_SOURCE> directory provided to Ksplice
311 contains the source for the currently running kernel, including any patches
312 that have previously been applied by Ksplice.
314 =head1 OPTIONS
316 =over 8
318 =item B<-v>, B<--verbose>
320 Causes B<ksplice-create> to print debugging messages about its progress. Using
321 multiple -v options increases the verbosity. The maximum is 2.
323 =item B<-j> I<JOBS>, B<--jobs=>I<JOBS>
325 Specifies the number of jobs to run simultaneously while performing kernel
326 builds. B<ksplice-create> also honors the environment variable
327 CONCURRENCY_LEVEL.
329 =item B<--patch-opt=>I<OPTIONS>
331 Can be used to pass options to L<patch(1)>. If this option is NOT specified, then
332 B<-p1> is passed to B<patch>. If this option is specified, then only the
333 specified options will be passed to B<patch>. This option can be repeated in
334 order to pass multiple options to B<patch>. This option is ignored when the
335 to-be-applied source code patch is specified using B<--diffext>.
337 =item B<--id=>I<ID>
339 Specifies the unique value that will be used as the identifier of the
340 Ksplice update. This identifier will, for example, appear in the name
341 of the update tarball. By default, a random 8-character ID will be
342 generated.
344 =back
346 =head1 SEE ALSO
348 L<ksplice-apply(8)>, L<ksplice-view(8)>, L<ksplice-undo(8)>
350 =head1 BUGS
352 Please report bugs to <PACKAGE_BUGREPORT>.
354 =head1 COPYRIGHT
356 Copyright (C) 2007-2008 Jeffrey Brian Arnold <jbarnold@mit.edu>
358 Copyright (C) 2008 Anders Kaseorg <andersk@mit.edu>,
359 Tim Abbott <tabbott@mit.edu>
361 This is free software and documentation. You can redistribute and/or modify it
362 under the terms of the GNU General Public License, version 2.
364 =cut