[rfc patch script] treewide conversion of __section(foo) to section("foo");

From: Joe Perches
Date: Mon Sep 09 2019 - 00:32:33 EST


This is an example script that could be applied to
-next to get compiler coverage testing for a treewide
conversion of __section(foo) to __section("foo")

I compiled an x86 defconfig but the coverage for this
script output, but it's possible an arch or two may have
asm compilation without including compiler-attributes.h where
conversion of __attribute__((section("foo") to __section("foo")
may fail.

---

Here's a perl script that converts the existing macro
and uses of __section to remove quoting from the macro
and add quoting to the uses.

It also converts __attribute__((section("foo"))) and
the multiple list forms of __attribute__ that have a
section("foo") entry.

The commit that this script produces is also attached
for today's -next.

$ cat section.pl
# convert linux-kernel __section uses from unquoted string to quoted string
# convert __attribute__((section("foo"))) to __section("foo")
# convert __attribute__((foo, section=("bar"), baz))
# to __section("bar") attribute((foo, baz))

use strict;

# patch compiler_attributes.h to remove quoting of section name

my $result = qx{patch -p1 <<"EOF"
include/linux/compiler_attributes.h | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h
index 9ca040fa1cc6..88d3eea1cbba 100644
--- a/include/linux/compiler_attributes.h
+++ b/include/linux/compiler_attributes.h
@@ -225,21 +225,11 @@
#define __pure __attribute__((__pure__))

/*
- * Note: Since this macro makes use of the "stringification operator" `#`,
- * a quoted string literal should not be passed to it. eg. prefer:
- * __section(.foo)
- * to:
- * __section(".foo")
- * unless the section name is dynamically built up, in which case the
- * verbose __attribute__((__section__(".foo" x))) should be preferred.
- *
- * See also: https://bugs.llvm.org/show_bug.cgi?id=42950
- *
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-section-function-attribute
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-section-variable-attribute
* clang: https://clang.llvm.org/docs/AttributeReference.html#section-declspec-allocate
*/
-#define __section(S) __attribute__((__section__(#S)))
+#define __section(section) __attribute__((__section__(section)))

/*
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-unused-function-attribute
EOF};

# Get the list of files to modify (contains __section or __attribute__.*section
# (ignore scripts, tools, uapi, and compiler_attributes.h)

my $output = `git grep --name-only -P "(?:\\b__section\\b\|\\b__attribute__\\b.*section)" | grep -vP '^(?:include/linux/compiler_attributes\\.h|scripts/|tools/|/uapi/)'`;
my @files = split("\n", $output);

# Modify each possible file
foreach (@files) {
chomp;
my $file = $_;

# read the original file
open(FH, '<', $file) or die $!;
my @lines = <FH>;
close FH;

# write the modified file line by line
open (FH, '>', $file) or die $!;
foreach my $line (@lines) {
chomp $line;
my $newline = $line;

# Convert __section(foo) to __section("foo")
# if "foo" uses token pasting, pre and post tokens are also quoted
if ($line =~ m/\b__section\s*\(\s*(?!")([^\)]+)\)/) {
my $oldsection = $1;
my $newsection = $1;
if ($oldsection =~ /(.*)##(.*)##(.*)/) {
$newsection = '"' . trim($1) . '" ## ' . trim($2) . ' ## "' . trim($3) . '"';
} else {
$newsection = '"' . trim($oldsection) . '"';
}
$newline =~ s/__section\s*\(\s*\Q$oldsection\E\s*\)/__section($newsection)/;
}

# convert __attribute__((section("foo"))) to __section("foo")
$newline =~ s/\b__attribute__\s*\(\s*\(\s*_*section_*\s*\(\s*("[^"]+")\s*\)\s*\)\s*\)/__section($1)/;

# convert __attribute__((foo, section=("bar"), baz))
# to __section("bar") attribute((foo, baz))
if ($newline =~ /(\b__attribute__\s*\(\s*\(([^,_]+)?(\s*,?\s*_*section_*\s*\(\s*("[^"]+")\s*\)\s*,?\s*)(.*)\s*\)\s*\))/) {
my $section = $3;
my $comma = "";
$comma = ", " if ($section =~ /^\s*,/ && $section =~ /,\s*$/);
$newline =~ s/\Q$section\E/$comma/;
$section =~ s/^[^"]*//;
$section =~ s/^("[^"]*").*/$1/;
$newline =~ s/\b__attribute__/__section($section) __attribute__/;
}

# if the line ended with a line continuation \, try to move the
# continuation to the same location by removing or adding tabs
if ($line =~ /\\$/) {
my $olen = length(expand_tabs($line));
my $nlen = length(expand_tabs($newline));
if ($newline =~ /\t\\$/) {
if ($nlen > $olen) {
$newline =~ s/\t\\$/\\/;
} else {
while ($nlen < $olen) {
$newline =~ s/\\$/\t\\/;
$nlen = length(expand_tabs($newline));
}
}
}
}
print FH "$newline\n";
}
close FH;
}

# And git commit the changes
$result = qx{git commit -a --author='Joe Perches <joe\@perches.com>' -F- <<"EOF"
treewide: Convert macro and uses of __section(foo) to __section("foo")

Use a more generic form for __section that requires quotes to avoid
complications with clang and gcc differences.

Remove the quote operator # from compiler_attributes.h __section macro.

Convert all unquoted __section(foo) uses to quoted __section("foo").
Also convert __attribute__((section("foo"))) uses to __section("foo")
even if the __attribute__ has multiple list entry forms.

Signed-off-by: Joe Perches <joe\@perches.com>
EOF
};

# utility subroutines
sub trim {
my ($string) = @_;
$string =~ s/^\s+|\s+$//g;
return $string;
}

sub expand_tabs {
my ($str) = @_;

my $res = '';
my $n = 0;
for my $c (split(//, $str)) {
if ($c eq "\t") {
$res .= ' ';
$n++;
for (; ($n % 8) != 0; $n++) {
$res .= ' ';
}
next;
}
$res .= $c;
$n++;
}

return $res;
}

So running the script:

$ perl section.pl

produces a commit
---