Revert "ANDROID: init: ensure initcall ordering with LTO"

This reverts commit 6c5ad8b10e as the LTO
feature causes merge issues with 5.8-rc1.  So remove it for now and
allow the developer to add the latest version of the patches later on.

Cc: Sami Tolvanen <samitolvanen@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I0b67a8a0d4b54c5be30f0bfc5abdd1422b7ab2f1
This commit is contained in:
Greg Kroah-Hartman 2020-06-24 08:46:09 +02:00
parent 5c1311f758
commit 0472716d99
3 changed files with 21 additions and 294 deletions

View File

@ -192,32 +192,10 @@ extern bool initcall_debug;
".long " #fn " - . \n" \
".previous \n");
#else
#ifdef CONFIG_LTO_CLANG
/*
* With LTO, the compiler doesn't necessarily obey link order for
* initcalls, and the initcall variable needs to be globally unique
* to avoid naming collisions. In order to preserve the correct
* order, we add each variable into its own section and generate a
* linker script (in scripts/link-vmlinux.sh) to ensure the order
* remains correct. We also add a __COUNTER__ prefix to the name,
* so we can retain the order of initcalls within each compilation
* unit, and __LINE__ to make the names more unique.
*/
#define ___lto_initcall(c, l, fn, id, __sec) \
static initcall_t __initcall_##c##_##l##_##fn##id __used \
__attribute__((__section__( #__sec \
__stringify(.init..##c##_##l##_##fn)))) = fn;
#define __lto_initcall(c, l, fn, id, __sec) \
___lto_initcall(c, l, fn, id, __sec)
#define ___define_initcall(fn, id, __sec) \
__lto_initcall(__COUNTER__, __LINE__, fn, id, __sec)
#else
#define ___define_initcall(fn, id, __sec) \
#define ___define_initcall(fn, id, __sec) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(#__sec ".init"))) = fn;
#endif
#endif
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
@ -258,7 +236,7 @@ extern bool initcall_debug;
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
#define console_initcall(fn) ___define_initcall(fn, con, .con_initcall)
#define console_initcall(fn) ___define_initcall(fn,, .con_initcall)
struct obs_kernel_param {
const char *str;

View File

@ -1,250 +0,0 @@
#!/usr/bin/env perl
# SPDX-License-Identifier: GPL-2.0
#
# Generates a linker script that specifies the correct initcall order.
#
# Copyright (C) 2019 Google LLC
use strict;
use warnings;
use IO::Handle;
my $nm = $ENV{'LLVM_NM'} || "llvm-nm";
my $ar = $ENV{'AR'} || "llvm-ar";
my $objtree = $ENV{'objtree'} || ".";
## list of all object files to process, in link order
my @objects;
## currently active child processes
my $jobs = {}; # child process pid -> file handle
## results from child processes
my $results = {}; # object index -> { level, function }
## reads _NPROCESSORS_ONLN to determine the number of processes to start
sub get_online_processors {
open(my $fh, "getconf _NPROCESSORS_ONLN 2>/dev/null |")
or die "$0: failed to execute getconf: $!";
my $procs = <$fh>;
close($fh);
if (!($procs =~ /^\d+$/)) {
return 1;
}
return int($procs);
}
## finds initcalls defined in an object file, parses level and function name,
## and prints it out to the parent process
sub find_initcalls {
my ($object) = @_;
die "$0: object file $object doesn't exist?" if (! -f $object);
open(my $fh, "\"$nm\" -just-symbol-name -defined-only \"$object\" 2>/dev/null |")
or die "$0: failed to execute \"$nm\": $!";
my $initcalls = {};
while (<$fh>) {
chomp;
my ($counter, $line, $symbol) = $_ =~ /^__initcall_(\d+)_(\d+)_(.*)$/;
if (!defined($counter) || !defined($line) || !defined($symbol)) {
next;
}
my ($function, $level) = $symbol =~
/^(.*)((early|rootfs|con|security|[0-9])s?)$/;
die "$0: duplicate initcall counter value in object $object: $_"
if exists($initcalls->{$counter});
$initcalls->{$counter} = {
'level' => $level,
'line' => $line,
'function' => $function
};
}
close($fh);
# sort initcalls in each object file numerically by the counter value
# to ensure they are in the order they were defined
foreach my $counter (sort { $a <=> $b } keys(%{$initcalls})) {
print $initcalls->{$counter}->{"level"} . " " .
$counter . " " .
$initcalls->{$counter}->{"line"} . " " .
$initcalls->{$counter}->{"function"} . "\n";
}
}
## waits for any child process to complete, reads the results, and adds them to
## the $results array for later processing
sub wait_for_results {
my $pid = wait();
if ($pid > 0) {
my $fh = $jobs->{$pid};
# the child process prints out results in the following format:
# line 1: <object file index>
# line 2..n: <level> <counter> <line> <function>
my $index = <$fh>;
chomp($index);
if (!($index =~ /^\d+$/)) {
die "$0: child $pid returned an invalid index: $index";
}
$index = int($index);
while (<$fh>) {
chomp;
my ($level, $counter, $line, $function) = $_ =~
/^([^\ ]+)\ (\d+)\ (\d+)\ (.*)$/;
if (!defined($level) ||
!defined($counter) ||
!defined($line) ||
!defined($function)) {
die "$0: child $pid returned invalid data";
}
if (!exists($results->{$index})) {
$results->{$index} = [];
}
push (@{$results->{$index}}, {
'level' => $level,
'counter' => $counter,
'line' => $line,
'function' => $function
});
}
close($fh);
delete($jobs->{$pid});
}
}
## launches child processes to find initcalls from the object files, waits for
## each process to complete and collects the results
sub process_objects {
my $index = 0; # link order index of the object file
my $njobs = get_online_processors();
while (scalar(@objects) > 0) {
my $object = shift(@objects);
# fork a child process and read it's stdout
my $pid = open(my $fh, '-|');
if (!defined($pid)) {
die "$0: failed to fork: $!";
} elsif ($pid) {
# save the child process pid and the file handle
$jobs->{$pid} = $fh;
} else {
STDOUT->autoflush(1);
print "$index\n";
find_initcalls("$objtree/$object");
exit;
}
$index++;
# if we reached the maximum number of processes, wait for one
# to complete before launching new ones
if (scalar(keys(%{$jobs})) >= $njobs && scalar(@objects) > 0) {
wait_for_results();
}
}
# wait for the remaining children to complete
while (scalar(keys(%{$jobs})) > 0) {
wait_for_results();
}
}
## gets a list of actual object files from thin archives, and adds them to
## @objects in link order
sub find_objects {
while (my $file = shift(@ARGV)) {
my $pid = open (my $fh, "\"$ar\" t \"$file\" 2>/dev/null |")
or die "$0: failed to execute $ar: $!";
my @output;
while (<$fh>) {
chomp;
push(@output, $_);
}
close($fh);
# if $ar failed, assume we have an object file
if ($? != 0) {
push(@objects, $file);
next;
}
# if $ar succeeded, read the list of object files
foreach (@output) {
push(@objects, $_);
}
}
}
## START
find_objects();
process_objects();
## process results and add them to $sections in the correct order
my $sections = {};
foreach my $index (sort { $a <=> $b } keys(%{$results})) {
foreach my $result (@{$results->{$index}}) {
my $level = $result->{'level'};
if (!exists($sections->{$level})) {
$sections->{$level} = [];
}
my $fsname = $result->{'counter'} . '_' .
$result->{'line'} . '_' .
$result->{'function'};
push(@{$sections->{$level}}, $fsname);
}
}
if (!keys(%{$sections})) {
exit(0); # no initcalls...?
}
## print out a linker script that defines the order of initcalls for each
## level
print "SECTIONS {\n";
foreach my $level (sort(keys(%{$sections}))) {
my $section;
if ($level eq 'con') {
$section = '.con_initcall.init';
} elsif ($level eq 'security') {
$section = '.security_initcall.init';
} else {
$section = ".initcall${level}.init";
}
print "\t${section} : {\n";
foreach my $fsname (@{$sections->{$level}}) {
print "\t\t*(${section}..${fsname}) ;\n"
}
print "\t}\n";
}
print "}\n";

View File

@ -39,30 +39,28 @@ info()
fi
}
# If CONFIG_LTO_CLANG is selected, generate a linker script to ensure correct
# ordering of initcalls, and with CONFIG_MODVERSIONS also enabled, collect the
# previously generated symbol versions into the same script.
lto_lds()
# If CONFIG_LTO_CLANG is selected, collect generated symbol versions into
# .tmp_symversions
modversions()
{
if [ -z "${CONFIG_LTO_CLANG}" ]; then
return
fi
${srctree}/scripts/generate_initcall_order.pl \
${KBUILD_VMLINUX_OBJS} ${KBUILD_VMLINUX_LIBS} \
> .tmp_lto.lds
if [ -n "${CONFIG_MODVERSIONS}" ]; then
for a in ${KBUILD_VMLINUX_OBJS} ${KBUILD_VMLINUX_LIBS}; do
for o in $(${AR} t $a 2>/dev/null); do
if [ -f ${o}.symversions ]; then
cat ${o}.symversions >> .tmp_lto.lds
fi
done
done
if [ -z "${CONFIG_MODVERSIONS}" ]; then
return
fi
echo "-T .tmp_lto.lds"
rm -f .tmp_symversions
for a in ${KBUILD_VMLINUX_OBJS} ${KBUILD_VMLINUX_LIBS}; do
for o in $(${AR} t $a); do
if [ -f ${o}.symversions ]; then
cat ${o}.symversions >> .tmp_symversions
fi
done
done
echo "-T .tmp_symversions"
}
# Link of vmlinux.o used for section mismatch analysis
@ -86,7 +84,7 @@ modpost_link()
info LD ${1}
fi
${LD} ${KBUILD_LDFLAGS} -r -o ${1} $(lto_lds) ${objects}
${LD} ${KBUILD_LDFLAGS} -r -o ${1} $(modversions) ${objects}
}
# If CONFIG_LTO_CLANG is selected, we postpone running recordmcount until
@ -277,7 +275,8 @@ cleanup()
{
rm -f .btf.*
rm -f .tmp_System.map
rm -f .tmp_lto.lds
rm -f .tmp_kallsyms*
rm -f .tmp_symversions
rm -f .tmp_vmlinux*
rm -f System.map
rm -f vmlinux