Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Apache/docs/manual/   (Apache Software Stiftung Version 2.4.65©)  Datei vom 11.0.2025 mit Größe 22 kB image not shown  

Quelle  generate_initcall_order.pl   Sprache: unbekannt

 
#!/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;
use IO::Select;
use POSIX ":sys_wait_h";

my $nm = $ENV{'NM'} || die "$0: ERROR: NM not set?";
my $objtree = $ENV{'objtree'} || '.';

## currently active child processes
my $jobs = {};  # child process pid -> file handle
## results from child processes
my $results = {}; # object index -> [ { level, secname }, ... ]

## reads _NPROCESSORS_ONLN to determine the maximum number of processes to
## start
sub get_online_processors {
 open(my $fh, "getconf _NPROCESSORS_ONLN 2>/dev/null |")
  or die "$0: ERROR: failed to execute getconf: $!";
 my $procs = <$fh>;
 close($fh);

 if (!($procs =~ /^\d+$/)) {
  return 1;
 }

 return int($procs);
}

## writes results to the parent process
## format: <file index> <initcall level> <base initcall section name>
sub write_results {
 my ($index, $initcalls) = @_;

 # sort by the counter value to ensure the order of initcalls within
 # each object file is correct
 foreach my $counter (sort { $a <=> $b } keys(%{$initcalls})) {
  my $level = $initcalls->{$counter}->{'level'};

  # section name for the initcall function
  my $secname = $initcalls->{$counter}->{'module'} . '__' .
         $counter . '_' .
         $initcalls->{$counter}->{'line'} . '_' .
         $initcalls->{$counter}->{'function'};

  print "$index $level $secname\n";
 }
}

## reads a result line from a child process and adds it to the $results array
sub read_results{
 my ($fh) = @_;

 # each child prints out a full line w/ autoflush and exits after the
 # last line, so even if buffered I/O blocks here, it shouldn't block
 # very long
 my $data = <$fh>;

 if (!defined($data)) {
  return 0;
 }

 chomp($data);

 my ($index, $level, $secname) = $data =~
  /^(\d+)\ ([^\ ]+)\ (.*)$/;

 if (!defined($index) ||
  !defined($level) ||
  !defined($secname)) {
  die "$0: ERROR: child process returned invalid data: $data\n";
 }

 $index = int($index);

 if (!exists($results->{$index})) {
  $results->{$index} = [];
 }

 push (@{$results->{$index}}, {
  'level'   => $level,
  'secname' => $secname
 });

 return 1;
}

## finds initcalls from an object file or all object files in an archive, and
## writes results back to the parent process
sub find_initcalls {
 my ($index, $file) = @_;

 die "$0: ERROR: file $file doesn't exist?" if (! -f $file);

 open(my $fh, "\"$nm\" --defined-only \"$file\" 2>/dev/null |")
  or die "$0: ERROR: failed to execute \"$nm\": $!";

 my $initcalls = {};

 while (<$fh>) {
  chomp;

  # check for the start of a new object file (if processing an
  # archive)
  my ($path)= $_ =~ /^(.+)\:$/;

  if (defined($path)) {
   write_results($index, $initcalls);
   $initcalls = {};
   next;
  }

  # look for an initcall
  my ($module, $counter, $line, $symbol) = $_ =~
   /[a-z]\s+__initcall__(\S*)__(\d+)_(\d+)_(.*)$/;

  if (!defined($module)) {
   $module = ''
  }

  if (!defined($counter) ||
   !defined($line) ||
   !defined($symbol)) {
   next;
  }

  # parse initcall level
  my ($function, $level) = $symbol =~
   /^(.*)((early|rootfs|con|[0-9])s?)$/;

  die "$0: ERROR: invalid initcall name $symbol in $file($path)"
   if (!defined($function) || !defined($level));

  $initcalls->{$counter} = {
   'module'   => $module,
   'line'     => $line,
   'function' => $function,
   'level'    => $level,
  };
 }

 close($fh);
 write_results($index, $initcalls);
}

## 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 ($select) = @_;

 my $pid = 0;
 do {
  # unblock children that may have a full write buffer
  foreach my $fh ($select->can_read(0)) {
   read_results($fh);
  }

  # check for children that have exited, read the remaining data
  # from them, and clean up
  $pid = waitpid(-1, WNOHANG);
  if ($pid > 0) {
   if (!exists($jobs->{$pid})) {
    next;
   }

   my $fh = $jobs->{$pid};
   $select->remove($fh);

   while (read_results($fh)) {
    # until eof
   }

   close($fh);
   delete($jobs->{$pid});
  }
 } while ($pid > 0);
}

## forks a child to process each file passed in the command line and collects
## the results
sub process_files {
 my $index = 0;
 my $njobs = $ENV{'PARALLELISM'} || get_online_processors();
 my $select = IO::Select->new();

 while (my $file = shift(@ARGV)) {
  # fork a child process and read it's stdout
  my $pid = open(my $fh, '-|');

  if (!defined($pid)) {
   die "$0: ERROR: failed to fork: $!";
  } elsif ($pid) {
   # save the child process pid and the file handle
   $select->add($fh);
   $jobs->{$pid} = $fh;
  } else {
   # in the child process
   STDOUT->autoflush(1);
   find_initcalls($index, "$objtree/$file");
   exit;
  }

  $index++;

  # limit the number of children to $njobs
  if (scalar(keys(%{$jobs})) >= $njobs) {
   wait_for_results($select);
  }
 }

 # wait for the remaining children to complete
 while (scalar(keys(%{$jobs})) > 0) {
  wait_for_results($select);
 }
}

sub generate_initcall_lds() {
 process_files();

 my $sections = {}; # level -> [ secname, ...]

 # sort results to retain link order and split to sections per
 # initcall level
 foreach my $index (sort { $a <=> $b } keys(%{$results})) {
  foreach my $result (@{$results->{$index}}) {
   my $level = $result->{'level'};

   if (!exists($sections->{$level})) {
    $sections->{$level} = [];
   }

   push(@{$sections->{$level}}, $result->{'secname'});
  }
 }

 die "$0: ERROR: no initcalls?" if (!keys(%{$sections}));

 # 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';
  } else {
   $section = ".initcall${level}.init";
  }

  print "\t${section} : {\n";

  foreach my $secname (@{$sections->{$level}}) {
   print "\t\t*(${section}..${secname}) ;\n";
  }

  print "\t}\n";
 }

 print "}\n";
}

generate_initcall_lds();

36%


[ zur Elbe Produktseite wechseln0.31Quellennavigators  Analyse erneut starten  ]