1*0Sstevel@tonic-gate#!/usr/perl5/bin/perl -w 2*0Sstevel@tonic-gate# 3*0Sstevel@tonic-gate# CDDL HEADER START 4*0Sstevel@tonic-gate# 5*0Sstevel@tonic-gate# The contents of this file are subject to the terms of the 6*0Sstevel@tonic-gate# Common Development and Distribution License, Version 1.0 only 7*0Sstevel@tonic-gate# (the "License"). You may not use this file except in compliance 8*0Sstevel@tonic-gate# with the License. 9*0Sstevel@tonic-gate# 10*0Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11*0Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing. 12*0Sstevel@tonic-gate# See the License for the specific language governing permissions 13*0Sstevel@tonic-gate# and limitations under the License. 14*0Sstevel@tonic-gate# 15*0Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each 16*0Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17*0Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the 18*0Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying 19*0Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner] 20*0Sstevel@tonic-gate# 21*0Sstevel@tonic-gate# CDDL HEADER END 22*0Sstevel@tonic-gate# 23*0Sstevel@tonic-gate# 24*0Sstevel@tonic-gate# ident "%Z%%M% %I% %E% SMI" 25*0Sstevel@tonic-gate# 26*0Sstevel@tonic-gate# Copyright 2004 Sun Microsystems, Inc. All rights reserved. 27*0Sstevel@tonic-gate# Use is subject to license terms. 28*0Sstevel@tonic-gate# 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gate# 31*0Sstevel@tonic-gate# This utility program creates the profiles of the binaries to be 32*0Sstevel@tonic-gate# checked. 33*0Sstevel@tonic-gate# 34*0Sstevel@tonic-gate# The dynamic profiling is done by running ldd -r on the binary with 35*0Sstevel@tonic-gate# LD_DEBUG=files,bindings and parsing the linker debug output. 36*0Sstevel@tonic-gate# 37*0Sstevel@tonic-gate# The static profiling (gathering of .text symbols) is done by calling 38*0Sstevel@tonic-gate# the utility program static_prof. 39*0Sstevel@tonic-gate# 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gaterequire 5.005; 42*0Sstevel@tonic-gateuse strict; 43*0Sstevel@tonic-gateuse locale; 44*0Sstevel@tonic-gateuse POSIX qw(locale_h); 45*0Sstevel@tonic-gateuse Sun::Solaris::Utils qw(textdomain gettext); 46*0Sstevel@tonic-gateuse File::Basename; 47*0Sstevel@tonic-gateuse File::Path; 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gateuse lib qw(/usr/lib/abi/appcert); 50*0Sstevel@tonic-gateuse AppcertUtil; 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gatesetlocale(LC_ALL, ""); 53*0Sstevel@tonic-gatetextdomain(TEXT_DOMAIN); 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gateuse vars qw( 56*0Sstevel@tonic-gate $tmp_prof_dir 57*0Sstevel@tonic-gate); 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gateset_clean_up_exit_routine(\&clean_up_exit); 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gateimport_vars_from_environment(); 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gatesignals('on', \&interrupted); 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gateset_working_dir(); 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gateprofile_objects(); 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gateclean_up(); 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gateexit 0; 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate# 74*0Sstevel@tonic-gate# working_dir has been imported by import_vars_from_environment() from 75*0Sstevel@tonic-gate# appcert. A sanity check is performed here to make sure it exists. 76*0Sstevel@tonic-gate# 77*0Sstevel@tonic-gatesub set_working_dir 78*0Sstevel@tonic-gate{ 79*0Sstevel@tonic-gate if (! defined($working_dir) || ! -d $working_dir) { 80*0Sstevel@tonic-gate exiter("$command_name: " . sprintf(gettext( 81*0Sstevel@tonic-gate "cannot locate working directory: %s\n"), $working_dir)); 82*0Sstevel@tonic-gate } 83*0Sstevel@tonic-gate} 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate# 86*0Sstevel@tonic-gate# Routine called when interrupted by user (e.g. SIGINT). 87*0Sstevel@tonic-gate# 88*0Sstevel@tonic-gatesub interrupted 89*0Sstevel@tonic-gate{ 90*0Sstevel@tonic-gate $SIG{$_[0]} = 'DEFAULT'; 91*0Sstevel@tonic-gate signals('off'); 92*0Sstevel@tonic-gate clean_up_exit(1); 93*0Sstevel@tonic-gate} 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gate# 96*0Sstevel@tonic-gate# Does the cleanup then exits with return code $rc. Note: The utility 97*0Sstevel@tonic-gate# routine exiter() calls this routine. 98*0Sstevel@tonic-gate# 99*0Sstevel@tonic-gatesub clean_up_exit 100*0Sstevel@tonic-gate{ 101*0Sstevel@tonic-gate my ($rc) = @_; 102*0Sstevel@tonic-gate $rc = 0 unless ($rc); 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate clean_up(); 105*0Sstevel@tonic-gate exit $rc; 106*0Sstevel@tonic-gate} 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate# 109*0Sstevel@tonic-gate# General cleanup activities. 110*0Sstevel@tonic-gate# 111*0Sstevel@tonic-gatesub clean_up 112*0Sstevel@tonic-gate{ 113*0Sstevel@tonic-gate if (defined($tmp_prof_dir) && -d $tmp_prof_dir) { 114*0Sstevel@tonic-gate rmtree($tmp_prof_dir); 115*0Sstevel@tonic-gate } 116*0Sstevel@tonic-gate} 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate# 119*0Sstevel@tonic-gate# Top level routine to loop over the objects and call the profiling 120*0Sstevel@tonic-gate# routines on each. 121*0Sstevel@tonic-gate# 122*0Sstevel@tonic-gatesub profile_objects 123*0Sstevel@tonic-gate{ 124*0Sstevel@tonic-gate # Make a tmp directory for the profiling work. 125*0Sstevel@tonic-gate $tmp_prof_dir = create_tmp_dir($tmp_dir); 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate if (! -d $tmp_prof_dir) { 128*0Sstevel@tonic-gate exiter(nocreatedir($tmp_prof_dir, $!)); 129*0Sstevel@tonic-gate } 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate my ($dir, $path_to_object); 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate # 134*0Sstevel@tonic-gate # Loop over each object item in the working_dir. 135*0Sstevel@tonic-gate # - $dir will be each one of these object directories. 136*0Sstevel@tonic-gate # - $path_to_object will be the corresponding actual path 137*0Sstevel@tonic-gate # to the the binary to be profiled. 138*0Sstevel@tonic-gate # Output will usually be placed down in $dir, e.g. "$dir/profile.static" 139*0Sstevel@tonic-gate # 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate my $cnt = -1; 142*0Sstevel@tonic-gate my $last_i; 143*0Sstevel@tonic-gate while (defined($dir = next_dir_name())) { 144*0Sstevel@tonic-gate $cnt++; 145*0Sstevel@tonic-gate if ($block_max ne '') { 146*0Sstevel@tonic-gate next if ($cnt < $block_min || $cnt >= $block_max); 147*0Sstevel@tonic-gate } 148*0Sstevel@tonic-gate 149*0Sstevel@tonic-gate $last_i = $cnt; 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate # Map object output directory to actual path of the object: 152*0Sstevel@tonic-gate $path_to_object = dir_name_to_path($dir); 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate if (! -f $path_to_object) { 155*0Sstevel@tonic-gate exiter(nopathexist($path_to_object, $!)); 156*0Sstevel@tonic-gate } 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate # Profile it: 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate emsg(gettext("profiling: %s\n"), $path_to_object); 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate static_profile($path_to_object, $dir); 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gate dynamic_profile($path_to_object, $dir); 165*0Sstevel@tonic-gate } 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate # Only try this after everything has been initially profiled. 168*0Sstevel@tonic-gate if (! $block_max || $last_i >= $binary_count - 1) { 169*0Sstevel@tonic-gate redo_unbound_profile(); 170*0Sstevel@tonic-gate } 171*0Sstevel@tonic-gate clean_up(); # Remove any tmp dirs and files. 172*0Sstevel@tonic-gate} 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gate# 175*0Sstevel@tonic-gate# Runs utility program static_prof on the object and places results in 176*0Sstevel@tonic-gate# output directory. 177*0Sstevel@tonic-gate# 178*0Sstevel@tonic-gatesub static_profile($$) 179*0Sstevel@tonic-gate{ 180*0Sstevel@tonic-gate my ($object, $output_dir) = @_; 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gate # This is the location of static_prof's output file: 183*0Sstevel@tonic-gate 184*0Sstevel@tonic-gate my $outfile = "$output_dir/profile.static"; 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate # It is consumed by static_check_object() in symcheck. 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate # 189*0Sstevel@tonic-gate # Do not run on *completely* statically linked objects. This 190*0Sstevel@tonic-gate # case will be caught and noted in the dynamic profiling and 191*0Sstevel@tonic-gate # checking. 192*0Sstevel@tonic-gate # 193*0Sstevel@tonic-gate my $skip_it; 194*0Sstevel@tonic-gate if (is_statically_linked($object)) { 195*0Sstevel@tonic-gate $skip_it = "STATICALLY_LINKED"; 196*0Sstevel@tonic-gate } elsif (! is_elf($object)) { 197*0Sstevel@tonic-gate $skip_it = "NON_ELF"; 198*0Sstevel@tonic-gate } 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate my $static_prof_fh = do { local *FH; *FH }; 201*0Sstevel@tonic-gate if (defined($skip_it)) { 202*0Sstevel@tonic-gate open($static_prof_fh, ">$outfile") || 203*0Sstevel@tonic-gate exiter(nofile($outfile, $!)); 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate print $static_prof_fh "#SKIPPED_TEST: $skip_it\n"; 206*0Sstevel@tonic-gate close($static_prof_fh); 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate return; 209*0Sstevel@tonic-gate } 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gate # 212*0Sstevel@tonic-gate # system() when run in the following manner will prevent the 213*0Sstevel@tonic-gate # shell from expanding any strange characters in $object. Quotes 214*0Sstevel@tonic-gate # around '$object' would be almost as safe. since excluded 215*0Sstevel@tonic-gate # earlier the cases where it contains the ' character. 216*0Sstevel@tonic-gate # 217*0Sstevel@tonic-gate system("$appcert_lib_dir/static_prof", '-p', '-s', '-o', $outfile, 218*0Sstevel@tonic-gate $object); 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate if ($? != 0) { 221*0Sstevel@tonic-gate open($static_prof_fh, ">$outfile") || 222*0Sstevel@tonic-gate exiter(nofile($outfile, $!)); 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate # 225*0Sstevel@tonic-gate # For completeness, we'll use elfdump to record the 226*0Sstevel@tonic-gate # static profile for 64 bit binaries, although the 227*0Sstevel@tonic-gate # static linking problems only occur for 32-bit 228*0Sstevel@tonic-gate # applications. 229*0Sstevel@tonic-gate # 230*0Sstevel@tonic-gate my ($prof, $sym); 231*0Sstevel@tonic-gate $prof = ''; 232*0Sstevel@tonic-gate my $elfdump_fh = do { local *FH; *FH }; 233*0Sstevel@tonic-gate if (open($elfdump_fh, "$cmd_elfdump -s -N .dynsym '$object' " . 234*0Sstevel@tonic-gate " 2>/dev/null |")) { 235*0Sstevel@tonic-gate while (<$elfdump_fh>) { 236*0Sstevel@tonic-gate chomp; 237*0Sstevel@tonic-gate if (/\s\.text\s+(\S+)$/) { 238*0Sstevel@tonic-gate $sym = $1; 239*0Sstevel@tonic-gate if (! /\bFUNC\b/) { 240*0Sstevel@tonic-gate next; 241*0Sstevel@tonic-gate } 242*0Sstevel@tonic-gate if (/\bGLOB\b/) { 243*0Sstevel@tonic-gate $prof .= "$object|TEXT|GLOB|" . 244*0Sstevel@tonic-gate "FUNC|$sym\n"; 245*0Sstevel@tonic-gate } else { 246*0Sstevel@tonic-gate $prof .= "$object|TEXT|WEAK|" . 247*0Sstevel@tonic-gate "FUNC|$sym\n"; 248*0Sstevel@tonic-gate } 249*0Sstevel@tonic-gate } 250*0Sstevel@tonic-gate } 251*0Sstevel@tonic-gate close($elfdump_fh); 252*0Sstevel@tonic-gate } 253*0Sstevel@tonic-gate if ($prof ne '') { 254*0Sstevel@tonic-gate my $line; 255*0Sstevel@tonic-gate print $static_prof_fh "#generated by symprof/elfdump\n"; 256*0Sstevel@tonic-gate print $static_prof_fh "#dtneeded:"; 257*0Sstevel@tonic-gate foreach $line (split(/\n/, cmd_output_dump($object))) { 258*0Sstevel@tonic-gate if ($line =~ /\bNEEDED\s+(\S+)/) { 259*0Sstevel@tonic-gate print $static_prof_fh " $1"; 260*0Sstevel@tonic-gate } 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate print $static_prof_fh "\n"; 263*0Sstevel@tonic-gate print $static_prof_fh $prof; 264*0Sstevel@tonic-gate } else { 265*0Sstevel@tonic-gate print $static_prof_fh "#SKIPPED_TEST: " . 266*0Sstevel@tonic-gate "PROFILER_PROGRAM_static_prof_RETURNED:$?\n"; 267*0Sstevel@tonic-gate } 268*0Sstevel@tonic-gate close($static_prof_fh); 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate 271*0Sstevel@tonic-gate return; 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate # Also store the dtneededs from the static profile output. 275*0Sstevel@tonic-gate my $dtneeded = "$output_dir/info.dtneeded"; 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate my $dtneeded_fh = do { local *FH; *FH }; 278*0Sstevel@tonic-gate open($dtneeded_fh, ">$dtneeded") || 279*0Sstevel@tonic-gate exiter(nofile($dtneeded, $!)); 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate open($static_prof_fh, "<$outfile") || 282*0Sstevel@tonic-gate exiter(nofile($outfile, $!)); 283*0Sstevel@tonic-gate 284*0Sstevel@tonic-gate my $lib; 285*0Sstevel@tonic-gate while (<$static_prof_fh>) { 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate next unless (/^\s*#/); 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate if (/^\s*#\s*dtneeded:\s*(\S.*)$/) { 290*0Sstevel@tonic-gate foreach $lib (split(/\s+/, $1)) { 291*0Sstevel@tonic-gate next if ($lib eq ''); 292*0Sstevel@tonic-gate print $dtneeded_fh "$lib\n"; 293*0Sstevel@tonic-gate } 294*0Sstevel@tonic-gate last; 295*0Sstevel@tonic-gate } 296*0Sstevel@tonic-gate } 297*0Sstevel@tonic-gate close($dtneeded_fh); 298*0Sstevel@tonic-gate close($static_prof_fh); 299*0Sstevel@tonic-gate} 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate# 302*0Sstevel@tonic-gate# Top level subroutine for doing a dynamic profile of an object. It 303*0Sstevel@tonic-gate# calls get_dynamic_profile() which handles the details of the actual 304*0Sstevel@tonic-gate# profiling and returns the newline separated "preprocessed format" to 305*0Sstevel@tonic-gate# this subroutine. 306*0Sstevel@tonic-gate# 307*0Sstevel@tonic-gate# The records are then processed and placed in the output directory. 308*0Sstevel@tonic-gate# 309*0Sstevel@tonic-gatesub dynamic_profile 310*0Sstevel@tonic-gate{ 311*0Sstevel@tonic-gate my ($object, $output_dir) = @_; 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate my ($profile, $line, $tmp); 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate # This is the profile output file. 316*0Sstevel@tonic-gate my $outfile = "$output_dir/profile.dynamic"; 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate $profile = get_dynamic_profile($object); 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate if ($profile =~ /^ERROR:\s*(.*)$/) { 321*0Sstevel@tonic-gate # There was some problem obtaining the dynamic profile 322*0Sstevel@tonic-gate my $msg = $1; 323*0Sstevel@tonic-gate my $errfile = "$output_dir/profile.dynamic.errors"; 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate my $profile_error_fh = do { local *FH; *FH }; 326*0Sstevel@tonic-gate open($profile_error_fh, ">>$errfile") || 327*0Sstevel@tonic-gate exiter(nofile($errfile, $!)); 328*0Sstevel@tonic-gate 329*0Sstevel@tonic-gate $msg =~ s/\n/ /g; 330*0Sstevel@tonic-gate $msg =~ s/;/,/g; 331*0Sstevel@tonic-gate print $profile_error_fh $msg, "\n"; 332*0Sstevel@tonic-gate close($profile_error_fh); 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate # Write a comment to the profile file as well: 335*0Sstevel@tonic-gate my $profile_fh = do { local *FH; *FH }; 336*0Sstevel@tonic-gate open($profile_fh, ">$outfile") || 337*0Sstevel@tonic-gate exiter(nofile($outfile, $!)); 338*0Sstevel@tonic-gate print $profile_fh "#NO_BINDINGS_FOUND $msg\n"; 339*0Sstevel@tonic-gate close($profile_fh); 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate return; 342*0Sstevel@tonic-gate } 343*0Sstevel@tonic-gate 344*0Sstevel@tonic-gate my ($filter, $filtee, $from, $to, $sym); 345*0Sstevel@tonic-gate my ($type, $saw_bindings, $all_needed); 346*0Sstevel@tonic-gate my (%filter_map, %symlink_map); 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate # Resolve the symlink of the object, if any. 349*0Sstevel@tonic-gate $symlink_map{$object} = follow_symlink($object); 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gate # 352*0Sstevel@tonic-gate # Collect the filter or static linking info first. Since the 353*0Sstevel@tonic-gate # filter info may be used to alias libraries, it is safest to do 354*0Sstevel@tonic-gate # it before any bindings processing. that is why we iterate 355*0Sstevel@tonic-gate # through $profile twice. 356*0Sstevel@tonic-gate # 357*0Sstevel@tonic-gate my @dynamic_profile_array = split(/\n/, $profile); 358*0Sstevel@tonic-gate 359*0Sstevel@tonic-gate foreach $line (@dynamic_profile_array) { 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate if ($line =~ /^FILTER_AUX:(.*)$/) { 362*0Sstevel@tonic-gate # 363*0Sstevel@tonic-gate # Here is the basic example of an auxiliary filter: 364*0Sstevel@tonic-gate # 365*0Sstevel@tonic-gate # FILTER: /usr/lib/libc.so.1 366*0Sstevel@tonic-gate # FILTEE: /usr/platform/sun4u/lib/libc_psr.so.1 367*0Sstevel@tonic-gate # 368*0Sstevel@tonic-gate # The app links against symbol memcpy() in 369*0Sstevel@tonic-gate # libc.so.1 at build time. Now, at run time IF 370*0Sstevel@tonic-gate # memcpy() is provided by libc_psr.so.1 then 371*0Sstevel@tonic-gate # that "code" is used, otherwise it backs off to 372*0Sstevel@tonic-gate # use the memcpy()in libc.so.1. The 373*0Sstevel@tonic-gate # libc_psr.so.1 doesn't even have to exist. 374*0Sstevel@tonic-gate # 375*0Sstevel@tonic-gate # The dynamic linker happily informs us that it 376*0Sstevel@tonic-gate # has found (and will bind to) memcpy() in 377*0Sstevel@tonic-gate # /usr/platform/sun4u/lib/libc_psr.so.1. We 378*0Sstevel@tonic-gate # want to alias libc_psr.so.1 => libc.so.1. 379*0Sstevel@tonic-gate # Why? 380*0Sstevel@tonic-gate # - less models to maintain. Note the symlink 381*0Sstevel@tonic-gate # situation in /usr/platform. 382*0Sstevel@tonic-gate # - libc_psr.so.1 is versioned, but we would be 383*0Sstevel@tonic-gate # incorrect since it has memcpy() as SUNWprivate 384*0Sstevel@tonic-gate # 385*0Sstevel@tonic-gate # Therefore we record this aliasing in the hash 386*0Sstevel@tonic-gate # %filter_map. This will be used below to 387*0Sstevel@tonic-gate # replace occurrences of the FILTEE string by 388*0Sstevel@tonic-gate # the FILTER string. Never the other way round. 389*0Sstevel@tonic-gate # 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate ($filter, $filtee) = split(/\|/, $1, 2); 392*0Sstevel@tonic-gate $filter_map{$filtee} = $filter; 393*0Sstevel@tonic-gate 394*0Sstevel@tonic-gate # Map the basenames too: 395*0Sstevel@tonic-gate $filter = basename($filter); 396*0Sstevel@tonic-gate $filtee = basename($filtee); 397*0Sstevel@tonic-gate $filter_map{$filtee} = $filter; 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate } elsif ($line =~ /^FILTER_STD:(.*)$/) { 400*0Sstevel@tonic-gate 401*0Sstevel@tonic-gate # 402*0Sstevel@tonic-gate # Here is the basic example(s) of a standard filter: 403*0Sstevel@tonic-gate # 404*0Sstevel@tonic-gate # FILTER: /usr/lib/libsys.so.1 405*0Sstevel@tonic-gate # FILTEE: /usr/lib/libc.so.1 406*0Sstevel@tonic-gate # 407*0Sstevel@tonic-gate # Here is another: 408*0Sstevel@tonic-gate # 409*0Sstevel@tonic-gate # FILTER: /usr/lib/libw.so.1 410*0Sstevel@tonic-gate # FILTEE: /usr/lib/libc.so.1 411*0Sstevel@tonic-gate # 412*0Sstevel@tonic-gate # Here is a more perverse one, libxnet.so.1 has 3 413*0Sstevel@tonic-gate # filtees: 414*0Sstevel@tonic-gate # 415*0Sstevel@tonic-gate # FILTER: /usr/lib/libxnet.so.1 416*0Sstevel@tonic-gate # FILTEE: /usr/lib/{libsocket.so.1,libnsl.so.1,libc.so.1} 417*0Sstevel@tonic-gate # 418*0Sstevel@tonic-gate # The important point to note about standard 419*0Sstevel@tonic-gate # filters is that they contain NO CODE AT ALL. 420*0Sstevel@tonic-gate # All of the symbols in the filter MUST be found 421*0Sstevel@tonic-gate # in (and bound to) the filtee(s) or there is a 422*0Sstevel@tonic-gate # relocation error. 423*0Sstevel@tonic-gate # 424*0Sstevel@tonic-gate # The app links against symbol getwc() in 425*0Sstevel@tonic-gate # libw.so.1 at build time. Now, at run time 426*0Sstevel@tonic-gate # getwc() is actually provided by libc.so.1. 427*0Sstevel@tonic-gate # 428*0Sstevel@tonic-gate # The dynamic linker happily informs us that it 429*0Sstevel@tonic-gate # has found (and will bind to) getwc() in 430*0Sstevel@tonic-gate # libc.so.1. IT NEVER DIRECTLY TELLS US getwc was 431*0Sstevel@tonic-gate # actually referred to in libw.so.1 432*0Sstevel@tonic-gate # 433*0Sstevel@tonic-gate # So, unless we open a model file while 434*0Sstevel@tonic-gate # PROFILING, we cannot figure out which ones 435*0Sstevel@tonic-gate # come from libw.so.1 and which ones come from 436*0Sstevel@tonic-gate # libc.so.1. In one sense this is too bad: the 437*0Sstevel@tonic-gate # libw.so.1 structure is lost. 438*0Sstevel@tonic-gate # 439*0Sstevel@tonic-gate # The bottom line is we should not alias 440*0Sstevel@tonic-gate # libc.so.1 => libw.so.1 (FILTEE => FILTER) as 441*0Sstevel@tonic-gate # we did above with FILTER_AUX. That would be a 442*0Sstevel@tonic-gate # disaster. (would say EVERYTHING in libc came 443*0Sstevel@tonic-gate # from libw!) 444*0Sstevel@tonic-gate # 445*0Sstevel@tonic-gate # So we DO NOT store the alias in this case, this 446*0Sstevel@tonic-gate # leads to: 447*0Sstevel@tonic-gate # - more models to maintain. 448*0Sstevel@tonic-gate # 449*0Sstevel@tonic-gate # Thus we basically skip this info. 450*0Sstevel@tonic-gate # EXCEPT for one case, libdl.so.1, see below. 451*0Sstevel@tonic-gate # 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gate ($filter, $filtee) = split(/\|/, $1, 2); 454*0Sstevel@tonic-gate 455*0Sstevel@tonic-gate # 456*0Sstevel@tonic-gate # The dlopen(), ... family of functions in 457*0Sstevel@tonic-gate # libdl.so.1 is implemented as a filter for 458*0Sstevel@tonic-gate # ld.so.1. We DO NOT want to consider a symbol 459*0Sstevel@tonic-gate # model for ld.so.1. So in this case alone we 460*0Sstevel@tonic-gate # want to alias ld.so.1 => libdl.so.1 461*0Sstevel@tonic-gate # 462*0Sstevel@tonic-gate # 463*0Sstevel@tonic-gate # We only need to substitute the standard filter 464*0Sstevel@tonic-gate # libdl.so.n. Record the alias in that case. 465*0Sstevel@tonic-gate # 466*0Sstevel@tonic-gate if ($filter =~ /\blibdl\.so\.\d+/) { 467*0Sstevel@tonic-gate $filter_map{$filtee} = $filter; 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate # Map basenames too: 470*0Sstevel@tonic-gate $filter = basename($filter); 471*0Sstevel@tonic-gate $filtee = basename($filtee); 472*0Sstevel@tonic-gate $filter_map{$filtee} = $filter; 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate 475*0Sstevel@tonic-gate } elsif ($line =~ /^DYNAMIC_PROFILE_SKIPPED_NOT_ELF/ || 476*0Sstevel@tonic-gate $line =~ /^STATICALLY_LINKED:/) { 477*0Sstevel@tonic-gate # 478*0Sstevel@tonic-gate # This info will go as a COMMENT into the 479*0Sstevel@tonic-gate # output. n.b.: there is no checking whether 480*0Sstevel@tonic-gate # this piece of info is consistent with the rest 481*0Sstevel@tonic-gate # of the profile output. 482*0Sstevel@tonic-gate # 483*0Sstevel@tonic-gate # The $message string will come right after the 484*0Sstevel@tonic-gate # header, and before the bindings (if any). See 485*0Sstevel@tonic-gate # below where we write to the PROF filehandle. 486*0Sstevel@tonic-gate # 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gate my $profile_msg_fh = do { local *FH; *FH }; 489*0Sstevel@tonic-gate open($profile_msg_fh, ">>$outfile") || 490*0Sstevel@tonic-gate exiter(nofile($outfile, $!)); 491*0Sstevel@tonic-gate print $profile_msg_fh "#$line\n"; 492*0Sstevel@tonic-gate close($profile_msg_fh); 493*0Sstevel@tonic-gate 494*0Sstevel@tonic-gate } elsif ($line =~ /^NEEDED_FOUND:(.*)$/) { 495*0Sstevel@tonic-gate # 496*0Sstevel@tonic-gate # These libraries are basically information 497*0Sstevel@tonic-gate # contained in the ldd "libfoo.so.1 => 498*0Sstevel@tonic-gate # /usr/lib/libfoo.so.1" output lines. It is the 499*0Sstevel@tonic-gate # closure of the neededs (not just the directly 500*0Sstevel@tonic-gate # needed ones). 501*0Sstevel@tonic-gate # 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate $all_needed .= $1 . "\n"; 504*0Sstevel@tonic-gate } 505*0Sstevel@tonic-gate } 506*0Sstevel@tonic-gate 507*0Sstevel@tonic-gate # 508*0Sstevel@tonic-gate # Now collect the bindings info: 509*0Sstevel@tonic-gate # 510*0Sstevel@tonic-gate # Each BINDING record refers to 1 symbol. After manipulation 511*0Sstevel@tonic-gate # here it will go into 1 record into the profile output. 512*0Sstevel@tonic-gate # 513*0Sstevel@tonic-gate # What sort of manipulations? Looking below reveals: 514*0Sstevel@tonic-gate # 515*0Sstevel@tonic-gate # - we apply the library FILTER_AUX aliases in %filter_map 516*0Sstevel@tonic-gate # - for shared objects we resolve symbolic links to the actual 517*0Sstevel@tonic-gate # files they point to. 518*0Sstevel@tonic-gate # - we may be in a mode where we do not store full paths of 519*0Sstevel@tonic-gate # the shared objects, e.g. /usr/lib/libc.so.1, but rather 520*0Sstevel@tonic-gate # just their basename "libc.so.1" 521*0Sstevel@tonic-gate # 522*0Sstevel@tonic-gate # There are exactly four(4) types of bindings that will be 523*0Sstevel@tonic-gate # returned to us by get_dynamic_profile(). See 524*0Sstevel@tonic-gate # get_dynamic_profile() and Get_ldd_Profile() for more details. 525*0Sstevel@tonic-gate # 526*0Sstevel@tonic-gate # Here are the 4 types: 527*0Sstevel@tonic-gate # 528*0Sstevel@tonic-gate # BINDING_DIRECT:from|to|sym 529*0Sstevel@tonic-gate # The object being profiled is the "from" here! 530*0Sstevel@tonic-gate # It directly calls "sym" in library "to". 531*0Sstevel@tonic-gate # 532*0Sstevel@tonic-gate # BINDING_INDIRECT:from|to|sym 533*0Sstevel@tonic-gate # The object being profiled is NOT the "from" here. 534*0Sstevel@tonic-gate # "from" is a shared object, and "from" calls "sym" in 535*0Sstevel@tonic-gate # library "to". 536*0Sstevel@tonic-gate # 537*0Sstevel@tonic-gate # BINDING_REVERSE:from|to|sym 538*0Sstevel@tonic-gate # The shared object "from" makes a reverse binding 539*0Sstevel@tonic-gate # all the way back to the object being profiled! We call 540*0Sstevel@tonic-gate # this *REVERSE*. "to" is the object being profiled. 541*0Sstevel@tonic-gate # 542*0Sstevel@tonic-gate # BINDING_UNBOUND:from|sym 543*0Sstevel@tonic-gate # object "from" wants to call "sym", but "sym" was 544*0Sstevel@tonic-gate # not found! We didn't find the "to", and so no 545*0Sstevel@tonic-gate # "to" is passed to us. 546*0Sstevel@tonic-gate # 547*0Sstevel@tonic-gate 548*0Sstevel@tonic-gate my $put_DIRECT_in_the_UNBOUND_record; 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate $saw_bindings = 0; 551*0Sstevel@tonic-gate # 552*0Sstevel@tonic-gate # Start the sorting pipeline that appends to the output file. 553*0Sstevel@tonic-gate # It will be written to in the following loop. 554*0Sstevel@tonic-gate # 555*0Sstevel@tonic-gate # Tracing back $outfile to $outdir to $working_dir, one sees $outfile 556*0Sstevel@tonic-gate # should have no single-quote characters. We double check it does not 557*0Sstevel@tonic-gate # before running the command. 558*0Sstevel@tonic-gate # 559*0Sstevel@tonic-gate if ($outfile =~ /'/) { 560*0Sstevel@tonic-gate exiter(norunprog("|$cmd_sort -t'|' +1 | $cmd_uniq >> '$outfile'")); 561*0Sstevel@tonic-gate } 562*0Sstevel@tonic-gate 563*0Sstevel@tonic-gate my $prof_fh = do { local *FH; *FH }; 564*0Sstevel@tonic-gate open($prof_fh, "|$cmd_sort -t'|' +1 | $cmd_uniq >> '$outfile'") || 565*0Sstevel@tonic-gate exiter(norunprog("|$cmd_sort -t'|' +1 | $cmd_uniq >> '$outfile'", 566*0Sstevel@tonic-gate $!)); 567*0Sstevel@tonic-gate local($SIG{'PIPE'}) = sub { 568*0Sstevel@tonic-gate exiter(norunprog( 569*0Sstevel@tonic-gate "|$cmd_sort -t'|' +1 | $cmd_uniq >> '$outfile'", $!)); 570*0Sstevel@tonic-gate }; 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate foreach $line (@dynamic_profile_array) { 573*0Sstevel@tonic-gate 574*0Sstevel@tonic-gate if ($line =~ /^BINDING_([^:]+):(.*)$/) { 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate $type = $1; 577*0Sstevel@tonic-gate 578*0Sstevel@tonic-gate if ($type eq 'UNBOUND') { 579*0Sstevel@tonic-gate # 580*0Sstevel@tonic-gate # If the symbol was unbound, there is no 581*0Sstevel@tonic-gate # "to" library. We make an empty "to" 582*0Sstevel@tonic-gate # value so as to avoid special casing 583*0Sstevel@tonic-gate # "to" all through the code that 584*0Sstevel@tonic-gate # follows. It is easy to verify no 585*0Sstevel@tonic-gate # matter what happens with the $to 586*0Sstevel@tonic-gate # variable, it will NOT be printed to the 587*0Sstevel@tonic-gate # profile output file in the UNBOUND 588*0Sstevel@tonic-gate # case. 589*0Sstevel@tonic-gate # 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate ($from, $sym) = split(/\|/, $2, 2); 592*0Sstevel@tonic-gate $to = ''; 593*0Sstevel@tonic-gate 594*0Sstevel@tonic-gate } else { 595*0Sstevel@tonic-gate # Otherwise, we have the full triple: 596*0Sstevel@tonic-gate 597*0Sstevel@tonic-gate ($from, $to, $sym) = split(/\|/, $2, 3); 598*0Sstevel@tonic-gate } 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate # 601*0Sstevel@tonic-gate # We record here information to be used in 602*0Sstevel@tonic-gate # writing out UNBOUND records, namely if the 603*0Sstevel@tonic-gate # "from" happened to also be the object being 604*0Sstevel@tonic-gate # profiled. In that case The string "*DIRECT*" 605*0Sstevel@tonic-gate # will be placed in the "*UNBOUND*" record, 606*0Sstevel@tonic-gate # otherwise the "from" will stand as is in the 607*0Sstevel@tonic-gate # "*UNBOUND*" record. We do this check here 608*0Sstevel@tonic-gate # before the filter_map is applied. The chances 609*0Sstevel@tonic-gate # of it making a difference is small, but we had 610*0Sstevel@tonic-gate # best to do it here. 611*0Sstevel@tonic-gate # 612*0Sstevel@tonic-gate if (files_equal($from, $object)) { 613*0Sstevel@tonic-gate # 614*0Sstevel@tonic-gate # Switch to indicate placing *DIRECT* in 615*0Sstevel@tonic-gate # the *UNBOUND* line, etc. 616*0Sstevel@tonic-gate # 617*0Sstevel@tonic-gate $put_DIRECT_in_the_UNBOUND_record = 1; 618*0Sstevel@tonic-gate } else { 619*0Sstevel@tonic-gate $put_DIRECT_in_the_UNBOUND_record = 0; 620*0Sstevel@tonic-gate } 621*0Sstevel@tonic-gate 622*0Sstevel@tonic-gate # 623*0Sstevel@tonic-gate # See if there is a filter name that "aliases" 624*0Sstevel@tonic-gate # either of the "from" or "to" libraries, if so 625*0Sstevel@tonic-gate # then rename it. 626*0Sstevel@tonic-gate # 627*0Sstevel@tonic-gate if ($to ne '' && $filter_map{$to}) { 628*0Sstevel@tonic-gate $to = $filter_map{$to}; 629*0Sstevel@tonic-gate } 630*0Sstevel@tonic-gate if ($type ne 'DIRECT' && $filter_map{$from}) { 631*0Sstevel@tonic-gate $from = $filter_map{$from}; 632*0Sstevel@tonic-gate } 633*0Sstevel@tonic-gate 634*0Sstevel@tonic-gate # 635*0Sstevel@tonic-gate # Record symlink information. 636*0Sstevel@tonic-gate # 637*0Sstevel@tonic-gate # Note that follow_symlink returns the file 638*0Sstevel@tonic-gate # name itself when the file is not a symlink. 639*0Sstevel@tonic-gate # 640*0Sstevel@tonic-gate # Work out if either "from" or "to" are 641*0Sstevel@tonic-gate # symlinks. For efficiency we keep them in the 642*0Sstevel@tonic-gate # %symlink_map hash. Recall that we are in a 643*0Sstevel@tonic-gate # loop here, so why do libc.so.1 200 times? 644*0Sstevel@tonic-gate # 645*0Sstevel@tonic-gate if ($from ne '') { 646*0Sstevel@tonic-gate if (! exists($symlink_map{$from})) { 647*0Sstevel@tonic-gate $symlink_map{$from} = 648*0Sstevel@tonic-gate follow_symlink($from); 649*0Sstevel@tonic-gate } 650*0Sstevel@tonic-gate } 651*0Sstevel@tonic-gate if ($to ne '') { 652*0Sstevel@tonic-gate if (! exists($symlink_map{$to})) { 653*0Sstevel@tonic-gate $symlink_map{$to} = 654*0Sstevel@tonic-gate follow_symlink($to); 655*0Sstevel@tonic-gate } 656*0Sstevel@tonic-gate } 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate # 659*0Sstevel@tonic-gate # Now make the actual profile output line. Construct 660*0Sstevel@tonic-gate # it in $tmp and then append it to $prof_fh pipeline. 661*0Sstevel@tonic-gate # 662*0Sstevel@tonic-gate $tmp = ''; 663*0Sstevel@tonic-gate 664*0Sstevel@tonic-gate if ($type eq "DIRECT") { 665*0Sstevel@tonic-gate $tmp = "$object|*DIRECT*|$to|$sym"; 666*0Sstevel@tonic-gate } elsif ($type eq "INDIRECT") { 667*0Sstevel@tonic-gate $tmp = "$object|$from|$to|$sym"; 668*0Sstevel@tonic-gate } elsif ($type eq "REVERSE") { 669*0Sstevel@tonic-gate $tmp = "$object|*REVERSE*|$from|$sym"; 670*0Sstevel@tonic-gate } elsif ($type eq "UNBOUND") { 671*0Sstevel@tonic-gate if ($put_DIRECT_in_the_UNBOUND_record) { 672*0Sstevel@tonic-gate $tmp = 673*0Sstevel@tonic-gate "$object|*DIRECT*|*UNBOUND*|$sym"; 674*0Sstevel@tonic-gate } else { 675*0Sstevel@tonic-gate $tmp = "$object|$from|*UNBOUND*|$sym"; 676*0Sstevel@tonic-gate } 677*0Sstevel@tonic-gate } else { 678*0Sstevel@tonic-gate exiter("$command_name: " . sprintf(gettext( 679*0Sstevel@tonic-gate "unrecognized ldd(1) LD_DEBUG " . 680*0Sstevel@tonic-gate "bindings line: %s\n"), $line)); 681*0Sstevel@tonic-gate } 682*0Sstevel@tonic-gate 683*0Sstevel@tonic-gate # write it to the sorting pipeline: 684*0Sstevel@tonic-gate print $prof_fh $tmp, "\n"; 685*0Sstevel@tonic-gate $saw_bindings = 1; 686*0Sstevel@tonic-gate } elsif ($line =~ /^DYNAMIC_PROFILE_SKIPPED_NOT_ELF/) { 687*0Sstevel@tonic-gate # ignore no bindings warning for non-ELF 688*0Sstevel@tonic-gate $saw_bindings = 1; 689*0Sstevel@tonic-gate } 690*0Sstevel@tonic-gate } 691*0Sstevel@tonic-gate 692*0Sstevel@tonic-gate if (! $saw_bindings) { 693*0Sstevel@tonic-gate print $prof_fh "#NO_BINDINGS_FOUND\n"; 694*0Sstevel@tonic-gate } 695*0Sstevel@tonic-gate close($prof_fh); 696*0Sstevel@tonic-gate if ($? != 0) { 697*0Sstevel@tonic-gate exiter(norunprog( 698*0Sstevel@tonic-gate "|$cmd_sort -t'|' +1 | $cmd_uniq >> '$outfile'", $!)); 699*0Sstevel@tonic-gate } 700*0Sstevel@tonic-gate 701*0Sstevel@tonic-gate # Print out the library location and symlink info. 702*0Sstevel@tonic-gate $outfile = "$output_dir/profile.dynamic.objects"; 703*0Sstevel@tonic-gate 704*0Sstevel@tonic-gate my $objects_fh = do { local *FH; *FH }; 705*0Sstevel@tonic-gate open($objects_fh, ">$outfile") || exiter(nofile($outfile, $!)); 706*0Sstevel@tonic-gate 707*0Sstevel@tonic-gate my ($var, $val); 708*0Sstevel@tonic-gate while (($var, $val) = each(%ENV)) { 709*0Sstevel@tonic-gate if ($var =~ /^LD_/) { 710*0Sstevel@tonic-gate print $objects_fh "#info: $var=$val\n"; 711*0Sstevel@tonic-gate } 712*0Sstevel@tonic-gate } 713*0Sstevel@tonic-gate 714*0Sstevel@tonic-gate my $obj; 715*0Sstevel@tonic-gate foreach $obj (sort(keys(%symlink_map))) { 716*0Sstevel@tonic-gate next if ($obj eq ''); 717*0Sstevel@tonic-gate print $objects_fh "$obj => $symlink_map{$obj}\n"; 718*0Sstevel@tonic-gate } 719*0Sstevel@tonic-gate close($objects_fh); 720*0Sstevel@tonic-gate 721*0Sstevel@tonic-gate # Print out ldd shared object resolution. 722*0Sstevel@tonic-gate $outfile = "$output_dir/profile.dynamic.ldd"; 723*0Sstevel@tonic-gate 724*0Sstevel@tonic-gate my $ldd_prof_fh = do { local *FH; *FH }; 725*0Sstevel@tonic-gate open($ldd_prof_fh, ">$outfile") || exiter(nofile($outfile, $!)); 726*0Sstevel@tonic-gate 727*0Sstevel@tonic-gate if (defined($all_needed)) { 728*0Sstevel@tonic-gate print $ldd_prof_fh $all_needed; 729*0Sstevel@tonic-gate } 730*0Sstevel@tonic-gate close($ldd_prof_fh); 731*0Sstevel@tonic-gate 732*0Sstevel@tonic-gate} 733*0Sstevel@tonic-gate 734*0Sstevel@tonic-gate# 735*0Sstevel@tonic-gate# If the users environment is not the same when running symprof as when 736*0Sstevel@tonic-gate# running their application, the dynamic linker cannot resolve all of 737*0Sstevel@tonic-gate# the dynamic bindings and we get "unbound symbols". 738*0Sstevel@tonic-gate# redo_unbound_profile attempts to alleviate this somewhat. In 739*0Sstevel@tonic-gate# particular, for shared objects that do not have all of their 740*0Sstevel@tonic-gate# dependencies recorded, it attempts to use binding information in the 741*0Sstevel@tonic-gate# other *executables* under test to supplement the binding information 742*0Sstevel@tonic-gate# for the shared object with unbound symbols. This is not the whole 743*0Sstevel@tonic-gate# story (e.g. dlopen(3L)), but it often helps considerably. 744*0Sstevel@tonic-gate# 745*0Sstevel@tonic-gatesub redo_unbound_profile 746*0Sstevel@tonic-gate{ 747*0Sstevel@tonic-gate my ($dir, $path_to_object); 748*0Sstevel@tonic-gate my ($profile, $total, $count); 749*0Sstevel@tonic-gate my (%unbound_bins); 750*0Sstevel@tonic-gate 751*0Sstevel@tonic-gate # 752*0Sstevel@tonic-gate # Find the objects with unbound symbols. Put them in the list 753*0Sstevel@tonic-gate # %unbound_bins. 754*0Sstevel@tonic-gate # 755*0Sstevel@tonic-gate $total = 0; 756*0Sstevel@tonic-gate while (defined($dir = next_dir_name())) { 757*0Sstevel@tonic-gate 758*0Sstevel@tonic-gate $profile = "$dir/profile.dynamic"; 759*0Sstevel@tonic-gate my $profile_fh = do { local *FH; *FH }; 760*0Sstevel@tonic-gate if (! -f $profile || ! open($profile_fh, "<$profile")) { 761*0Sstevel@tonic-gate next; 762*0Sstevel@tonic-gate } 763*0Sstevel@tonic-gate 764*0Sstevel@tonic-gate $count = 0; 765*0Sstevel@tonic-gate while (<$profile_fh>) { 766*0Sstevel@tonic-gate next if (/^\s*#/); 767*0Sstevel@tonic-gate $count++ if (/\|\*UNBOUND\*\|/); 768*0Sstevel@tonic-gate } 769*0Sstevel@tonic-gate close($profile_fh); 770*0Sstevel@tonic-gate 771*0Sstevel@tonic-gate $unbound_bins{$dir} = $count if ($count); 772*0Sstevel@tonic-gate $total += $count; 773*0Sstevel@tonic-gate } 774*0Sstevel@tonic-gate 775*0Sstevel@tonic-gate # we are done if no unbounds are detected. 776*0Sstevel@tonic-gate return unless (%unbound_bins); 777*0Sstevel@tonic-gate return if ($total == 0); 778*0Sstevel@tonic-gate 779*0Sstevel@tonic-gate my (%dtneededs_lookup_full, %dtneededs_lookup_base); 780*0Sstevel@tonic-gate 781*0Sstevel@tonic-gate # Read in *ALL* objects dt_neededs. 782*0Sstevel@tonic-gate 783*0Sstevel@tonic-gate my ($soname, $base, $full); 784*0Sstevel@tonic-gate while (defined($dir = next_dir_name())) { 785*0Sstevel@tonic-gate 786*0Sstevel@tonic-gate $profile = "$dir/profile.dynamic.ldd"; 787*0Sstevel@tonic-gate my $all_neededs_fh = do { local *FH; *FH }; 788*0Sstevel@tonic-gate if (! open($all_neededs_fh, "<$profile")) { 789*0Sstevel@tonic-gate # this is a heuristic, so we skip on to the next 790*0Sstevel@tonic-gate next; 791*0Sstevel@tonic-gate } 792*0Sstevel@tonic-gate 793*0Sstevel@tonic-gate while (<$all_neededs_fh>) { 794*0Sstevel@tonic-gate chop; 795*0Sstevel@tonic-gate next if (/^\s*#/); 796*0Sstevel@tonic-gate # save the dtneeded info: 797*0Sstevel@tonic-gate ($soname, $full) = split(/\s+=>\s+/, $_); 798*0Sstevel@tonic-gate 799*0Sstevel@tonic-gate if ($full !~ /not found|\)/) { 800*0Sstevel@tonic-gate $dtneededs_lookup_full{$full}{$dir} = 1; 801*0Sstevel@tonic-gate } 802*0Sstevel@tonic-gate if ($soname !~ /not found|\)/) { 803*0Sstevel@tonic-gate $base = basename($soname); 804*0Sstevel@tonic-gate $dtneededs_lookup_base{$base}{$dir} = 1; 805*0Sstevel@tonic-gate } 806*0Sstevel@tonic-gate } 807*0Sstevel@tonic-gate close($all_neededs_fh); 808*0Sstevel@tonic-gate } 809*0Sstevel@tonic-gate 810*0Sstevel@tonic-gate emsg("\n" . gettext( 811*0Sstevel@tonic-gate "re-profiling binary objects with unbound symbols") . " ...\n"); 812*0Sstevel@tonic-gate 813*0Sstevel@tonic-gate # Now combine the above info with each object having unbounds: 814*0Sstevel@tonic-gate 815*0Sstevel@tonic-gate my $uref = \%unbound_bins; 816*0Sstevel@tonic-gate foreach $dir (keys(%unbound_bins)) { 817*0Sstevel@tonic-gate 818*0Sstevel@tonic-gate # Map object output directory to the actual path of the object: 819*0Sstevel@tonic-gate $path_to_object = dir_name_to_path($dir); 820*0Sstevel@tonic-gate 821*0Sstevel@tonic-gate # 822*0Sstevel@tonic-gate # Here is the algorithm: 823*0Sstevel@tonic-gate # 824*0Sstevel@tonic-gate # 1) binary with unbounds must be a shared object. 825*0Sstevel@tonic-gate # 826*0Sstevel@tonic-gate # 2) check if it is in the dtneeded of other product binaries. 827*0Sstevel@tonic-gate # if so, use the dynamic profile of those binaries 828*0Sstevel@tonic-gate # to augment the bindings of the binary with unbounds 829*0Sstevel@tonic-gate # 830*0Sstevel@tonic-gate 831*0Sstevel@tonic-gate if (! -f $path_to_object) { 832*0Sstevel@tonic-gate exiter(nopathexist($path_to_object, $!)); 833*0Sstevel@tonic-gate } 834*0Sstevel@tonic-gate 835*0Sstevel@tonic-gate # only consider shared objects (e.g. with no DTNEEDED recorded) 836*0Sstevel@tonic-gate if (! is_shared_object($path_to_object)) { 837*0Sstevel@tonic-gate next; 838*0Sstevel@tonic-gate } 839*0Sstevel@tonic-gate 840*0Sstevel@tonic-gate $base = basename($path_to_object); 841*0Sstevel@tonic-gate 842*0Sstevel@tonic-gate my (@dirlist); 843*0Sstevel@tonic-gate 844*0Sstevel@tonic-gate my $result = 0; 845*0Sstevel@tonic-gate 846*0Sstevel@tonic-gate if (defined($dtneededs_lookup_base{$base})) { 847*0Sstevel@tonic-gate # the basename is on another's dtneededs: 848*0Sstevel@tonic-gate @dirlist = keys(%{$dtneededs_lookup_base{$base}}); 849*0Sstevel@tonic-gate # try using the bindings of these executables: 850*0Sstevel@tonic-gate $result = 851*0Sstevel@tonic-gate try_executables_bindings($dir, $uref, @dirlist); 852*0Sstevel@tonic-gate } 853*0Sstevel@tonic-gate if ($result) { 854*0Sstevel@tonic-gate # we achieved some improvements and so are done: 855*0Sstevel@tonic-gate next; 856*0Sstevel@tonic-gate } 857*0Sstevel@tonic-gate 858*0Sstevel@tonic-gate # Otherwise, try objects that have our full path in their 859*0Sstevel@tonic-gate # dtneededs: 860*0Sstevel@tonic-gate @dirlist = (); 861*0Sstevel@tonic-gate foreach $full (keys(%dtneededs_lookup_full)) { 862*0Sstevel@tonic-gate if (! files_equal($path_to_object, $full)) { 863*0Sstevel@tonic-gate next; 864*0Sstevel@tonic-gate } 865*0Sstevel@tonic-gate push(@dirlist, keys(%{$dtneededs_lookup_full{$full}})); 866*0Sstevel@tonic-gate } 867*0Sstevel@tonic-gate if (@dirlist) { 868*0Sstevel@tonic-gate $result = 869*0Sstevel@tonic-gate try_executables_bindings($dir, $uref, @dirlist); 870*0Sstevel@tonic-gate } 871*0Sstevel@tonic-gate } 872*0Sstevel@tonic-gate emsg("\n"); 873*0Sstevel@tonic-gate} 874*0Sstevel@tonic-gate 875*0Sstevel@tonic-gate# 876*0Sstevel@tonic-gate# We are trying to reduce unbound symbols of shared objects/libraries 877*0Sstevel@tonic-gate# under test that *have not* recorded their dependencies (i.e. 878*0Sstevel@tonic-gate# DTNEEDED's). So we look for Executables being checked that have *this* 879*0Sstevel@tonic-gate# binary ($path_to_object, a shared object) on *its* DTNEEDED. If we 880*0Sstevel@tonic-gate# find one, we use those bindings. 881*0Sstevel@tonic-gate# 882*0Sstevel@tonic-gatesub try_executables_bindings 883*0Sstevel@tonic-gate{ 884*0Sstevel@tonic-gate my ($dir, $uref, @dirlist) = @_; 885*0Sstevel@tonic-gate 886*0Sstevel@tonic-gate my $path_to_object = dir_name_to_path($dir); 887*0Sstevel@tonic-gate 888*0Sstevel@tonic-gate # 889*0Sstevel@tonic-gate # N.B. The word "try" here means for a binary (a shared library, 890*0Sstevel@tonic-gate # actually) that had unbound symbols, "try" to use OTHER 891*0Sstevel@tonic-gate # executables binding info to resolve those unbound symbols. 892*0Sstevel@tonic-gate # 893*0Sstevel@tonic-gate # At least one executable needs this library; we select the one 894*0Sstevel@tonic-gate # with minimal number of its own unbounds. 895*0Sstevel@tonic-gate # 896*0Sstevel@tonic-gate my (%sorting_list); 897*0Sstevel@tonic-gate my (@executables_to_try); 898*0Sstevel@tonic-gate my ($dir2, $cnt); 899*0Sstevel@tonic-gate foreach $dir2 (@dirlist) { 900*0Sstevel@tonic-gate next if (! defined($dir2)); 901*0Sstevel@tonic-gate next if ($dir2 eq $dir); 902*0Sstevel@tonic-gate if (exists($uref->{$dir2})) { 903*0Sstevel@tonic-gate $cnt = $uref->{$dir2}; 904*0Sstevel@tonic-gate } else { 905*0Sstevel@tonic-gate # 906*0Sstevel@tonic-gate # This binary is not on the unbounds list, so 907*0Sstevel@tonic-gate # give it the highest priority. 908*0Sstevel@tonic-gate # 909*0Sstevel@tonic-gate $cnt = 0; 910*0Sstevel@tonic-gate } 911*0Sstevel@tonic-gate $sorting_list{"$dir2 $cnt"} = $dir2; 912*0Sstevel@tonic-gate } 913*0Sstevel@tonic-gate 914*0Sstevel@tonic-gate foreach my $key (reverse(sort_on_count(keys %sorting_list))) { 915*0Sstevel@tonic-gate push(@executables_to_try, $sorting_list{$key}); 916*0Sstevel@tonic-gate } 917*0Sstevel@tonic-gate 918*0Sstevel@tonic-gate my ($my_new_count, $my_new_profile, %my_new_symbols); 919*0Sstevel@tonic-gate my ($object, $caller, $callee, $sym, $profile); 920*0Sstevel@tonic-gate my $reprofiled = 0; 921*0Sstevel@tonic-gate 922*0Sstevel@tonic-gate my ($line, $path2); 923*0Sstevel@tonic-gate 924*0Sstevel@tonic-gate foreach $dir2 (@executables_to_try) { 925*0Sstevel@tonic-gate $path2 = dir_name_to_path($dir2); 926*0Sstevel@tonic-gate emsg(gettext( 927*0Sstevel@tonic-gate "re-profiling: %s\n" . 928*0Sstevel@tonic-gate "using: %s\n"), $path_to_object, $path2); 929*0Sstevel@tonic-gate 930*0Sstevel@tonic-gate # read the other binary's profile 931*0Sstevel@tonic-gate 932*0Sstevel@tonic-gate $profile = "$dir2/profile.dynamic"; 933*0Sstevel@tonic-gate if (! -f $profile) { 934*0Sstevel@tonic-gate next; 935*0Sstevel@tonic-gate } 936*0Sstevel@tonic-gate 937*0Sstevel@tonic-gate my $prof_try_fh = do { local *FH; *FH }; 938*0Sstevel@tonic-gate open($prof_try_fh, "<$profile") || 939*0Sstevel@tonic-gate exiter(nofile($profile, $!)); 940*0Sstevel@tonic-gate 941*0Sstevel@tonic-gate # initialize for the next try: 942*0Sstevel@tonic-gate $my_new_profile = ''; 943*0Sstevel@tonic-gate $my_new_count = 0; 944*0Sstevel@tonic-gate %my_new_symbols = (); 945*0Sstevel@tonic-gate 946*0Sstevel@tonic-gate # try to find bindings that involve us ($dir) 947*0Sstevel@tonic-gate while (<$prof_try_fh>) { 948*0Sstevel@tonic-gate chop($line = $_); 949*0Sstevel@tonic-gate next if (/^\s*#/); 950*0Sstevel@tonic-gate next if (/^\s*$/); 951*0Sstevel@tonic-gate ($object, $caller, $callee, $sym) = 952*0Sstevel@tonic-gate split(/\|/, $line, 4); 953*0Sstevel@tonic-gate 954*0Sstevel@tonic-gate if ($caller eq '*REVERSE*') { 955*0Sstevel@tonic-gate next if ($callee =~ /^\*.*\*$/); 956*0Sstevel@tonic-gate if (! files_equal($callee, $path_to_object)) { 957*0Sstevel@tonic-gate next; 958*0Sstevel@tonic-gate } 959*0Sstevel@tonic-gate 960*0Sstevel@tonic-gate $my_new_profile .= 961*0Sstevel@tonic-gate "$callee|*DIRECT*|REVERSE_TO:" . 962*0Sstevel@tonic-gate "$object|$sym\n"; 963*0Sstevel@tonic-gate 964*0Sstevel@tonic-gate $my_new_symbols{$sym}++; 965*0Sstevel@tonic-gate $my_new_count++; 966*0Sstevel@tonic-gate 967*0Sstevel@tonic-gate } elsif (files_equal($caller, $path_to_object)) { 968*0Sstevel@tonic-gate $my_new_profile .= 969*0Sstevel@tonic-gate "$caller|*DIRECT*|$callee|$sym\n"; 970*0Sstevel@tonic-gate 971*0Sstevel@tonic-gate $my_new_symbols{$sym}++; 972*0Sstevel@tonic-gate $my_new_count++; 973*0Sstevel@tonic-gate } 974*0Sstevel@tonic-gate } 975*0Sstevel@tonic-gate close($prof_try_fh); 976*0Sstevel@tonic-gate 977*0Sstevel@tonic-gate next if (! $my_new_count); 978*0Sstevel@tonic-gate 979*0Sstevel@tonic-gate # modify our profile with the new information: 980*0Sstevel@tonic-gate $profile = "$dir/profile.dynamic"; 981*0Sstevel@tonic-gate if (! rename($profile, "$profile.0") || ! -f "$profile.0") { 982*0Sstevel@tonic-gate return 0; 983*0Sstevel@tonic-gate } 984*0Sstevel@tonic-gate my $prof_orig_fh = do { local *FH; *FH }; 985*0Sstevel@tonic-gate if (! open($prof_orig_fh, "<$profile.0")) { 986*0Sstevel@tonic-gate rename("$profile.0", $profile); 987*0Sstevel@tonic-gate return 0; 988*0Sstevel@tonic-gate } 989*0Sstevel@tonic-gate my $prof_fh = do { local *FH; *FH }; 990*0Sstevel@tonic-gate if (! open($prof_fh, ">$profile")) { 991*0Sstevel@tonic-gate rename("$profile.0", $profile); 992*0Sstevel@tonic-gate return 0; 993*0Sstevel@tonic-gate } 994*0Sstevel@tonic-gate my $resolved_from = dir_name_to_path($dir2); 995*0Sstevel@tonic-gate print $prof_fh "# REDUCING_UNBOUNDS_VIA_PROFILE_FROM: " . 996*0Sstevel@tonic-gate "$resolved_from\n"; 997*0Sstevel@tonic-gate 998*0Sstevel@tonic-gate while (<$prof_orig_fh>) { 999*0Sstevel@tonic-gate if (/^\s*#/) { 1000*0Sstevel@tonic-gate print $prof_fh $_; 1001*0Sstevel@tonic-gate next; 1002*0Sstevel@tonic-gate } 1003*0Sstevel@tonic-gate chop($line = $_); 1004*0Sstevel@tonic-gate ($object, $caller, $callee, $sym) = 1005*0Sstevel@tonic-gate split(/\|/, $line, 4); 1006*0Sstevel@tonic-gate if (! exists($my_new_symbols{$sym})) { 1007*0Sstevel@tonic-gate print $prof_fh $_; 1008*0Sstevel@tonic-gate next; 1009*0Sstevel@tonic-gate } 1010*0Sstevel@tonic-gate print $prof_fh "# RESOLVED_FROM=$resolved_from: $_"; 1011*0Sstevel@tonic-gate } 1012*0Sstevel@tonic-gate close($prof_orig_fh); 1013*0Sstevel@tonic-gate print $prof_fh "# NEW_PROFILE:\n" . $my_new_profile; 1014*0Sstevel@tonic-gate close($prof_fh); 1015*0Sstevel@tonic-gate 1016*0Sstevel@tonic-gate $reprofiled = 1; 1017*0Sstevel@tonic-gate last; 1018*0Sstevel@tonic-gate } 1019*0Sstevel@tonic-gate return $reprofiled; 1020*0Sstevel@tonic-gate} 1021*0Sstevel@tonic-gate 1022*0Sstevel@tonic-gate# 1023*0Sstevel@tonic-gate# This routine calls get_ldd_output on the object and parses the 1024*0Sstevel@tonic-gate# LD_DEBUG output. Returns a string containing the information in 1025*0Sstevel@tonic-gate# standard form. 1026*0Sstevel@tonic-gate# 1027*0Sstevel@tonic-gatesub get_dynamic_profile 1028*0Sstevel@tonic-gate{ 1029*0Sstevel@tonic-gate my ($object) = @_; 1030*0Sstevel@tonic-gate 1031*0Sstevel@tonic-gate # Check if the object is statically linked: 1032*0Sstevel@tonic-gate 1033*0Sstevel@tonic-gate my $str; 1034*0Sstevel@tonic-gate if (! is_elf($object)) { 1035*0Sstevel@tonic-gate return "DYNAMIC_PROFILE_SKIPPED_NOT_ELF"; 1036*0Sstevel@tonic-gate } elsif (is_statically_linked($object)) { 1037*0Sstevel@tonic-gate $str = cmd_output_file($object); 1038*0Sstevel@tonic-gate return "STATICALLY_LINKED: $str"; 1039*0Sstevel@tonic-gate } 1040*0Sstevel@tonic-gate 1041*0Sstevel@tonic-gate # Get the raw ldd output: 1042*0Sstevel@tonic-gate my $ldd_output = get_ldd_output($object); 1043*0Sstevel@tonic-gate 1044*0Sstevel@tonic-gate if ($ldd_output =~ /^ERROR:/) { 1045*0Sstevel@tonic-gate # some problem occurred, pass the error upward: 1046*0Sstevel@tonic-gate return $ldd_output; 1047*0Sstevel@tonic-gate } 1048*0Sstevel@tonic-gate 1049*0Sstevel@tonic-gate # variables for manipulating the output: 1050*0Sstevel@tonic-gate my ($line, $filters, $neededs, $rest); 1051*0Sstevel@tonic-gate my ($tmp, $tmp2, @bindings); 1052*0Sstevel@tonic-gate 1053*0Sstevel@tonic-gate # Now parse it: 1054*0Sstevel@tonic-gate 1055*0Sstevel@tonic-gate foreach $line (split(/\n/, $ldd_output)) { 1056*0Sstevel@tonic-gate 1057*0Sstevel@tonic-gate if ($line =~ /^\d+:\s*(.*)$/) { 1058*0Sstevel@tonic-gate # LD_DEBUG profile line, starts with "NNNNN:" 1059*0Sstevel@tonic-gate $tmp = $1; 1060*0Sstevel@tonic-gate next if ($tmp eq ''); 1061*0Sstevel@tonic-gate 1062*0Sstevel@tonic-gate if ($tmp =~ /^binding (.*)$/) { 1063*0Sstevel@tonic-gate # 1064*0Sstevel@tonic-gate # First look for: 1065*0Sstevel@tonic-gate # binding file=/bin/pagesize to \ 1066*0Sstevel@tonic-gate # file=/usr/lib/libc.so.1: symbol `exit' 1067*0Sstevel@tonic-gate # 1068*0Sstevel@tonic-gate $tmp = $1; 1069*0Sstevel@tonic-gate push(@bindings, ldd_binding_line($1, $object)); 1070*0Sstevel@tonic-gate 1071*0Sstevel@tonic-gate } elsif ($tmp =~ /^file=\S+\s+(.*)$/) { 1072*0Sstevel@tonic-gate # 1073*0Sstevel@tonic-gate # Next look for: 1074*0Sstevel@tonic-gate # file=/usr/platform/SUNW,Ultra-1/\ 1075*0Sstevel@tonic-gate # lib/libc_psr.so.1; filtered by /usr... 1076*0Sstevel@tonic-gate # file=libdl.so.1; needed by /usr/lib/libc.so.1 1077*0Sstevel@tonic-gate # 1078*0Sstevel@tonic-gate $rest = trim($1); 1079*0Sstevel@tonic-gate 1080*0Sstevel@tonic-gate if ($rest =~ /^filtered by /) { 1081*0Sstevel@tonic-gate $filters .= 1082*0Sstevel@tonic-gate ldd_filter_line($tmp); 1083*0Sstevel@tonic-gate } elsif ($rest =~ /^needed by /) { 1084*0Sstevel@tonic-gate $neededs .= 1085*0Sstevel@tonic-gate ldd_needed_line($tmp, $object); 1086*0Sstevel@tonic-gate } 1087*0Sstevel@tonic-gate 1088*0Sstevel@tonic-gate } 1089*0Sstevel@tonic-gate 1090*0Sstevel@tonic-gate } elsif ($line =~ /^stdout:(.*)$/) { 1091*0Sstevel@tonic-gate # LD_DEBUG stdout line: 1092*0Sstevel@tonic-gate 1093*0Sstevel@tonic-gate $tmp = trim($1); 1094*0Sstevel@tonic-gate next if ($tmp eq ''); 1095*0Sstevel@tonic-gate 1096*0Sstevel@tonic-gate if ($tmp =~ /\s+=>\s+/) { 1097*0Sstevel@tonic-gate # 1098*0Sstevel@tonic-gate # First look for standard dependency 1099*0Sstevel@tonic-gate # resolution lines: 1100*0Sstevel@tonic-gate # 1101*0Sstevel@tonic-gate # libsocket.so.1 => /usr/lib/libsocket.so.1 1102*0Sstevel@tonic-gate # 1103*0Sstevel@tonic-gate # Note that these are *all* of the 1104*0Sstevel@tonic-gate # needed shared objects, not just the 1105*0Sstevel@tonic-gate # directly needed ones. 1106*0Sstevel@tonic-gate # 1107*0Sstevel@tonic-gate $tmp =~ s/\s+/ /g; 1108*0Sstevel@tonic-gate $neededs .= "NEEDED_FOUND:$tmp" . "\n"; 1109*0Sstevel@tonic-gate 1110*0Sstevel@tonic-gate } elsif ($tmp =~ /symbol not found: (.*)$/) { 1111*0Sstevel@tonic-gate # 1112*0Sstevel@tonic-gate # Next look for unbound symbols: 1113*0Sstevel@tonic-gate # symbol not found: gethz (/usr/\ 1114*0Sstevel@tonic-gate # local/bin/gethz) 1115*0Sstevel@tonic-gate # 1116*0Sstevel@tonic-gate 1117*0Sstevel@tonic-gate $tmp = trim($1); 1118*0Sstevel@tonic-gate ($tmp, $tmp2) = split(/\s+/, $tmp, 2); 1119*0Sstevel@tonic-gate $tmp2 =~ s/[\(\)]//g; # trim off (). 1120*0Sstevel@tonic-gate 1121*0Sstevel@tonic-gate # $tmp is the symbol, $tmp2 is the 1122*0Sstevel@tonic-gate # calling object. 1123*0Sstevel@tonic-gate 1124*0Sstevel@tonic-gate push(@bindings, 1125*0Sstevel@tonic-gate "BINDING_UNBOUND:$tmp2|$tmp" . "\n" 1126*0Sstevel@tonic-gate ); 1127*0Sstevel@tonic-gate } 1128*0Sstevel@tonic-gate } 1129*0Sstevel@tonic-gate } 1130*0Sstevel@tonic-gate 1131*0Sstevel@tonic-gate # Return the output: 1132*0Sstevel@tonic-gate my $ret = ''; 1133*0Sstevel@tonic-gate $ret .= $filters if (defined($filters)); 1134*0Sstevel@tonic-gate $ret .= $neededs if (defined($neededs)); 1135*0Sstevel@tonic-gate $ret .= join('', @bindings); 1136*0Sstevel@tonic-gate 1137*0Sstevel@tonic-gate return $ret; 1138*0Sstevel@tonic-gate} 1139*0Sstevel@tonic-gate 1140*0Sstevel@tonic-gate# 1141*0Sstevel@tonic-gate# Routine used to parse a LD_DEBUG "binding" line. 1142*0Sstevel@tonic-gate# 1143*0Sstevel@tonic-gate# Returns "preprocessed format line" if line is ok, or 1144*0Sstevel@tonic-gate# null string otherwise. 1145*0Sstevel@tonic-gate# 1146*0Sstevel@tonic-gatesub ldd_binding_line 1147*0Sstevel@tonic-gate{ 1148*0Sstevel@tonic-gate my ($line, $object) = @_; 1149*0Sstevel@tonic-gate 1150*0Sstevel@tonic-gate my ($from, $to, $sym); 1151*0Sstevel@tonic-gate 1152*0Sstevel@tonic-gate my ($t1, $t2, $t3); # tmp vars for regex output 1153*0Sstevel@tonic-gate 1154*0Sstevel@tonic-gate # 1155*0Sstevel@tonic-gate # Working on a line like: 1156*0Sstevel@tonic-gate # 1157*0Sstevel@tonic-gate # binding file=/bin/pagesize to file=/usr/lib/libc.so.1: symbol `exit' 1158*0Sstevel@tonic-gate # 1159*0Sstevel@tonic-gate # (with the leading "binding " removed). 1160*0Sstevel@tonic-gate # 1161*0Sstevel@tonic-gate 1162*0Sstevel@tonic-gate if ($line =~ /^file=(\S+)\s+to file=(\S+)\s+symbol(.*)$/) { 1163*0Sstevel@tonic-gate # 1164*0Sstevel@tonic-gate # The following trim off spaces, ', `, ;, and :, from 1165*0Sstevel@tonic-gate # the edges so if the filename had those there could 1166*0Sstevel@tonic-gate # be a problem. 1167*0Sstevel@tonic-gate # 1168*0Sstevel@tonic-gate $from = $1; 1169*0Sstevel@tonic-gate $to = $2; 1170*0Sstevel@tonic-gate $sym = $3; 1171*0Sstevel@tonic-gate # 1172*0Sstevel@tonic-gate # guard against future changes to the LD_DEBUG output 1173*0Sstevel@tonic-gate # (i.e. information appended to the end) 1174*0Sstevel@tonic-gate # 1175*0Sstevel@tonic-gate $sym =~ s/'\s+.*$//; 1176*0Sstevel@tonic-gate 1177*0Sstevel@tonic-gate $to =~ s/:$//; 1178*0Sstevel@tonic-gate 1179*0Sstevel@tonic-gate $sym =~ s/[\s:;`']*$//; 1180*0Sstevel@tonic-gate $sym =~ s/^[\s:;`']*//; 1181*0Sstevel@tonic-gate 1182*0Sstevel@tonic-gate } elsif ($line =~ /^file=(.+) to file=(.+): symbol (.*)$/) { 1183*0Sstevel@tonic-gate # This will catch spaces, but is less robust. 1184*0Sstevel@tonic-gate $t1 = $1; 1185*0Sstevel@tonic-gate $t2 = $2; 1186*0Sstevel@tonic-gate $t3 = $3; 1187*0Sstevel@tonic-gate # 1188*0Sstevel@tonic-gate # guard against future changes to the LD_DEBUG output 1189*0Sstevel@tonic-gate # (i.e. information appended to the end) 1190*0Sstevel@tonic-gate # 1191*0Sstevel@tonic-gate $t3 =~ s/'\s+.*$//; 1192*0Sstevel@tonic-gate 1193*0Sstevel@tonic-gate $from = wclean($t1, 1); 1194*0Sstevel@tonic-gate $to = wclean($t2, 1); 1195*0Sstevel@tonic-gate $sym = wclean($t3); 1196*0Sstevel@tonic-gate 1197*0Sstevel@tonic-gate } else { 1198*0Sstevel@tonic-gate return ''; 1199*0Sstevel@tonic-gate } 1200*0Sstevel@tonic-gate 1201*0Sstevel@tonic-gate if ($from eq '' || $to eq '' || $sym eq '') { 1202*0Sstevel@tonic-gate return ''; 1203*0Sstevel@tonic-gate } 1204*0Sstevel@tonic-gate 1205*0Sstevel@tonic-gate # 1206*0Sstevel@tonic-gate # OK, we have 3 files: $from, $to, $object 1207*0Sstevel@tonic-gate # Which, if any, are the same file? 1208*0Sstevel@tonic-gate # 1209*0Sstevel@tonic-gate # Note that we have not yet done the Filter library 1210*0Sstevel@tonic-gate # substitutions yet. So one cannot be too trusting of the file 1211*0Sstevel@tonic-gate # comparisons done here. 1212*0Sstevel@tonic-gate # 1213*0Sstevel@tonic-gate 1214*0Sstevel@tonic-gate if (files_equal($from, $to, 0)) { 1215*0Sstevel@tonic-gate # 1216*0Sstevel@tonic-gate # We skip the "from" = "to" case 1217*0Sstevel@tonic-gate # (could call this: BINDING_SELF). 1218*0Sstevel@tonic-gate # 1219*0Sstevel@tonic-gate return ''; 1220*0Sstevel@tonic-gate } elsif (files_equal($object, $from, 0)) { 1221*0Sstevel@tonic-gate # DIRECT CASE (object calls library): 1222*0Sstevel@tonic-gate return "BINDING_DIRECT:$from|$to|$sym" . "\n"; 1223*0Sstevel@tonic-gate } elsif (files_equal($object, $to, 0)) { 1224*0Sstevel@tonic-gate # REVERSE CASE (library calls object): 1225*0Sstevel@tonic-gate return "BINDING_REVERSE:$from|$to|$sym" . "\n"; 1226*0Sstevel@tonic-gate } else { 1227*0Sstevel@tonic-gate # 1228*0Sstevel@tonic-gate # INDIRECT CASE (needed library calls library): 1229*0Sstevel@tonic-gate # (this will not be a library calling itself because 1230*0Sstevel@tonic-gate # we skip $from eq $to above). 1231*0Sstevel@tonic-gate # 1232*0Sstevel@tonic-gate return "BINDING_INDIRECT:$from|$to|$sym" . "\n"; 1233*0Sstevel@tonic-gate } 1234*0Sstevel@tonic-gate} 1235*0Sstevel@tonic-gate 1236*0Sstevel@tonic-gate# 1237*0Sstevel@tonic-gate# Routine used to parse a LD_DEBUG "filtered by" line. 1238*0Sstevel@tonic-gate# 1239*0Sstevel@tonic-gate# Returns "preprocessed format line" if line is ok, or null string 1240*0Sstevel@tonic-gate# otherwise. 1241*0Sstevel@tonic-gate# 1242*0Sstevel@tonic-gatesub ldd_filter_line 1243*0Sstevel@tonic-gate{ 1244*0Sstevel@tonic-gate my ($line) = @_; 1245*0Sstevel@tonic-gate 1246*0Sstevel@tonic-gate my ($filter, $filtee); 1247*0Sstevel@tonic-gate 1248*0Sstevel@tonic-gate # 1249*0Sstevel@tonic-gate # Working on a line like: 1250*0Sstevel@tonic-gate # 1251*0Sstevel@tonic-gate # file=/usr/platform/SUNW,Ultra-1/lib/libc_psr.so.1; \ 1252*0Sstevel@tonic-gate # filtered by /usr/lib/libc.so.1 1253*0Sstevel@tonic-gate # 1254*0Sstevel@tonic-gate 1255*0Sstevel@tonic-gate my ($t1, $t2); # tmp vars for regex output 1256*0Sstevel@tonic-gate 1257*0Sstevel@tonic-gate if ($line =~ /file=(\S+)\s+filtered by\s+(\S.*)$/) { 1258*0Sstevel@tonic-gate $t1 = $1; 1259*0Sstevel@tonic-gate $t2 = $2; 1260*0Sstevel@tonic-gate $filtee = wclean($t1); 1261*0Sstevel@tonic-gate $filter = wclean($t2); 1262*0Sstevel@tonic-gate } elsif ($line =~ /file=(.+); filtered by (.*)$/) { 1263*0Sstevel@tonic-gate $t1 = $1; 1264*0Sstevel@tonic-gate $t2 = $2; 1265*0Sstevel@tonic-gate $filtee = wclean($t1, 1); 1266*0Sstevel@tonic-gate $filter = wclean($t2, 1); 1267*0Sstevel@tonic-gate } else { 1268*0Sstevel@tonic-gate return ''; 1269*0Sstevel@tonic-gate } 1270*0Sstevel@tonic-gate 1271*0Sstevel@tonic-gate if ($filtee eq '' || $filter eq '') { 1272*0Sstevel@tonic-gate return ''; 1273*0Sstevel@tonic-gate } 1274*0Sstevel@tonic-gate # 1275*0Sstevel@tonic-gate # What kind of filter is $filter? 1276*0Sstevel@tonic-gate # STANDARD (contains no "real code", e.g. libxnet.so.1), or 1277*0Sstevel@tonic-gate # AUXILIARY (provides "code" if needed, but 1278*0Sstevel@tonic-gate # prefers to pass filtee's "code", e.g. libc.so.1) 1279*0Sstevel@tonic-gate # 1280*0Sstevel@tonic-gate # LD_DEBUG output does not indicate this, so dump -Lv is run on it 1281*0Sstevel@tonic-gate # in filter_lib_type: 1282*0Sstevel@tonic-gate # 1283*0Sstevel@tonic-gate 1284*0Sstevel@tonic-gate my $type = 'unknown'; 1285*0Sstevel@tonic-gate 1286*0Sstevel@tonic-gate $type = filter_lib_type($filter); 1287*0Sstevel@tonic-gate 1288*0Sstevel@tonic-gate if ($type eq 'STD') { 1289*0Sstevel@tonic-gate return "FILTER_STD:$filter|$filtee" . "\n"; 1290*0Sstevel@tonic-gate } elsif ($type eq 'AUX') { 1291*0Sstevel@tonic-gate return "FILTER_AUX:$filter|$filtee" . "\n"; 1292*0Sstevel@tonic-gate } else { 1293*0Sstevel@tonic-gate return ''; 1294*0Sstevel@tonic-gate } 1295*0Sstevel@tonic-gate} 1296*0Sstevel@tonic-gate 1297*0Sstevel@tonic-gate# 1298*0Sstevel@tonic-gate# Routine used to parse a LD_DEBUG "needed by" line. 1299*0Sstevel@tonic-gate# 1300*0Sstevel@tonic-gate# Returns "preprocessed format line" if line is ok, or the null string 1301*0Sstevel@tonic-gate# otherwise. 1302*0Sstevel@tonic-gate# 1303*0Sstevel@tonic-gatesub ldd_needed_line 1304*0Sstevel@tonic-gate{ 1305*0Sstevel@tonic-gate my ($line, $object) = @_; 1306*0Sstevel@tonic-gate 1307*0Sstevel@tonic-gate my ($thing_needed, $file); 1308*0Sstevel@tonic-gate 1309*0Sstevel@tonic-gate my ($t1, $t2); # tmp variables for regex output. 1310*0Sstevel@tonic-gate 1311*0Sstevel@tonic-gate # 1312*0Sstevel@tonic-gate # Working on a line like: 1313*0Sstevel@tonic-gate # 1314*0Sstevel@tonic-gate # file=libdl.so.1; needed by /usr/lib/libc.so.1 1315*0Sstevel@tonic-gate # 1316*0Sstevel@tonic-gate 1317*0Sstevel@tonic-gate if ($line =~ /file=(\S+)\s+needed by\s+(\S.*)$/) { 1318*0Sstevel@tonic-gate $t1 = $1; 1319*0Sstevel@tonic-gate $t2 = $2; 1320*0Sstevel@tonic-gate $thing_needed = wclean($t1); 1321*0Sstevel@tonic-gate $file = wclean($t2); 1322*0Sstevel@tonic-gate } elsif ($line =~ /file=(.+); needed by (.*)$/) { 1323*0Sstevel@tonic-gate $t1 = $1; 1324*0Sstevel@tonic-gate $t2 = $2; 1325*0Sstevel@tonic-gate $thing_needed = wclean($t1, 1); 1326*0Sstevel@tonic-gate $file = wclean($t2, 1); 1327*0Sstevel@tonic-gate } else { 1328*0Sstevel@tonic-gate return ''; 1329*0Sstevel@tonic-gate } 1330*0Sstevel@tonic-gate 1331*0Sstevel@tonic-gate if ($thing_needed eq '' || $file eq '') { 1332*0Sstevel@tonic-gate return ''; 1333*0Sstevel@tonic-gate } 1334*0Sstevel@tonic-gate 1335*0Sstevel@tonic-gate # 1336*0Sstevel@tonic-gate # Note that $thing_needed is not a path to a file, just the 1337*0Sstevel@tonic-gate # short name unresolved, e.g. "libc.so.1". The next line of the 1338*0Sstevel@tonic-gate # LD_DEBUG output would tell us where $thing_needed is resolved 1339*0Sstevel@tonic-gate # to. 1340*0Sstevel@tonic-gate # 1341*0Sstevel@tonic-gate 1342*0Sstevel@tonic-gate if (files_equal($object, $file)) { 1343*0Sstevel@tonic-gate return "NEEDED_DIRECT:$thing_needed|$file" . "\n"; 1344*0Sstevel@tonic-gate } else { 1345*0Sstevel@tonic-gate return "NEEDED_INDIRECT:$thing_needed|$file" . "\n"; 1346*0Sstevel@tonic-gate } 1347*0Sstevel@tonic-gate} 1348*0Sstevel@tonic-gate 1349*0Sstevel@tonic-gate# 1350*0Sstevel@tonic-gate# Routine to clean up a "word" string from ldd output. 1351*0Sstevel@tonic-gate# 1352*0Sstevel@tonic-gate# This is specialized for removing the stuff surrounding files and 1353*0Sstevel@tonic-gate# symbols in the LD_DEBUG output. It is usually a file name or symbol 1354*0Sstevel@tonic-gate# name. 1355*0Sstevel@tonic-gate# 1356*0Sstevel@tonic-gatesub wclean 1357*0Sstevel@tonic-gate{ 1358*0Sstevel@tonic-gate my ($w, $keep_space) = @_; 1359*0Sstevel@tonic-gate 1360*0Sstevel@tonic-gate if (! $keep_space) { 1361*0Sstevel@tonic-gate # make sure leading/trailing spaces are gone. 1362*0Sstevel@tonic-gate $w =~ s/[\s:;`']*$//; # get rid of : ; ' and ` 1363*0Sstevel@tonic-gate $w =~ s/^[\s:;`']*//; 1364*0Sstevel@tonic-gate } else { 1365*0Sstevel@tonic-gate $w =~ s/[:;`']*$//; # get rid of : ; ' and ` 1366*0Sstevel@tonic-gate $w =~ s/^[:;`']*//; 1367*0Sstevel@tonic-gate } 1368*0Sstevel@tonic-gate 1369*0Sstevel@tonic-gate return $w; 1370*0Sstevel@tonic-gate} 1371*0Sstevel@tonic-gate 1372*0Sstevel@tonic-gate# 1373*0Sstevel@tonic-gate# This routine runs ldd -r on the object file with LD_DEBUG flags turned 1374*0Sstevel@tonic-gate# on. It collects the stdout and the LD_DEBUG profile data for the 1375*0Sstevel@tonic-gate# object (it must skip the LD_DEBUG profile data for /usr/bin/ldd 1376*0Sstevel@tonic-gate# /bin/sh, or any other extraneous processes). 1377*0Sstevel@tonic-gate# 1378*0Sstevel@tonic-gate# It returns the profile data as a single string with \n separated 1379*0Sstevel@tonic-gate# records. Records starting with "stdout: " are the stdout lines, 1380*0Sstevel@tonic-gate# Records starting with "NNNNN: " are the LD_DEBUG lines. Our caller 1381*0Sstevel@tonic-gate# must split and parse those lines. 1382*0Sstevel@tonic-gate# 1383*0Sstevel@tonic-gate# If there is some non-fatal error, it returns a 1-line string like: 1384*0Sstevel@tonic-gate# ERROR: <error-message> 1385*0Sstevel@tonic-gate# 1386*0Sstevel@tonic-gatesub get_ldd_output 1387*0Sstevel@tonic-gate{ 1388*0Sstevel@tonic-gate 1389*0Sstevel@tonic-gate my ($object) = @_; 1390*0Sstevel@tonic-gate 1391*0Sstevel@tonic-gate my ($tmpdir, $outfile, $errfile); 1392*0Sstevel@tonic-gate 1393*0Sstevel@tonic-gate if (! -f $object) { 1394*0Sstevel@tonic-gate exiter(nopathexist($object)); 1395*0Sstevel@tonic-gate } 1396*0Sstevel@tonic-gate 1397*0Sstevel@tonic-gate # We use the tmp_dir for our work: 1398*0Sstevel@tonic-gate $tmpdir = $tmp_prof_dir; 1399*0Sstevel@tonic-gate 1400*0Sstevel@tonic-gate # Clean out the tmpdir. 1401*0Sstevel@tonic-gate if ($tmpdir !~ m,^/*$,) { 1402*0Sstevel@tonic-gate unlink(<$tmpdir/*>); 1403*0Sstevel@tonic-gate # 1404*0Sstevel@tonic-gate # The following puts xgettext(1) back on track. It is 1405*0Sstevel@tonic-gate # confused and believes it is inside a C-style /* comment */ 1406*0Sstevel@tonic-gate # 1407*0Sstevel@tonic-gate my $unused = "*/"; 1408*0Sstevel@tonic-gate } 1409*0Sstevel@tonic-gate 1410*0Sstevel@tonic-gate # Output files for collecting output of the ldd -r command: 1411*0Sstevel@tonic-gate $errfile = "$tmpdir/stderr"; 1412*0Sstevel@tonic-gate $outfile = "$tmpdir/stdout"; 1413*0Sstevel@tonic-gate 1414*0Sstevel@tonic-gate my ($rc, $msg, $child, $result); 1415*0Sstevel@tonic-gate 1416*0Sstevel@tonic-gate # 1417*0Sstevel@tonic-gate # This forking method should have 2 LD_DEBUG bind.<PID> files 1418*0Sstevel@tonic-gate # one for ldd and the other for $object. system() could have 1419*0Sstevel@tonic-gate # another from the shell. 1420*0Sstevel@tonic-gate # 1421*0Sstevel@tonic-gate 1422*0Sstevel@tonic-gate # Fork off a child: 1423*0Sstevel@tonic-gate $child = fork(); 1424*0Sstevel@tonic-gate 1425*0Sstevel@tonic-gate # 1426*0Sstevel@tonic-gate # Note: the file "/tmp/.../bind.$child" should be the "ldd" 1427*0Sstevel@tonic-gate # profile, but we do not want to depend upon that. 1428*0Sstevel@tonic-gate # 1429*0Sstevel@tonic-gate 1430*0Sstevel@tonic-gate if (! defined($child)) { 1431*0Sstevel@tonic-gate # Problem forking: 1432*0Sstevel@tonic-gate exiter(sprintf(gettext( 1433*0Sstevel@tonic-gate "cannot fork for command: ldd -r %s: %s\n"), $object, $!)); 1434*0Sstevel@tonic-gate 1435*0Sstevel@tonic-gate } elsif ($child == 0) { 1436*0Sstevel@tonic-gate 1437*0Sstevel@tonic-gate # Reopen std output to the desired output files: 1438*0Sstevel@tonic-gate open(STDOUT, ">$outfile") || 1439*0Sstevel@tonic-gate exiter(nofile($outfile, $!)); 1440*0Sstevel@tonic-gate 1441*0Sstevel@tonic-gate open(STDERR, ">$errfile") || 1442*0Sstevel@tonic-gate exiter(nofile($errfile, $!)); 1443*0Sstevel@tonic-gate 1444*0Sstevel@tonic-gate # 1445*0Sstevel@tonic-gate # Set the env to turn on debugging from the linker: 1446*0Sstevel@tonic-gate # 1447*0Sstevel@tonic-gate $ENV{'LD_DEBUG'} = "files,bindings"; 1448*0Sstevel@tonic-gate $ENV{'LD_DEBUG_OUTPUT'} = "$tmpdir/bind"; 1449*0Sstevel@tonic-gate 1450*0Sstevel@tonic-gate # 1451*0Sstevel@tonic-gate # Set LD_NOAUXFLTR to avoid auxiliary filters (e.g. libc_psr) 1452*0Sstevel@tonic-gate # since they are not of interest to the public/private 1453*0Sstevel@tonic-gate # symbol status and confuse things more than anything else. 1454*0Sstevel@tonic-gate # 1455*0Sstevel@tonic-gate $ENV{'LD_NOAUXFLTR'} = "1"; 1456*0Sstevel@tonic-gate 1457*0Sstevel@tonic-gate # Run ldd -r: 1458*0Sstevel@tonic-gate c_locale(1); 1459*0Sstevel@tonic-gate exec($cmd_ldd, '-r', $object); 1460*0Sstevel@tonic-gate exit 1; # only reached if exec fails. 1461*0Sstevel@tonic-gate } else { 1462*0Sstevel@tonic-gate wait; # Wait for children to finish. 1463*0Sstevel@tonic-gate $rc = $?; # Record exit status. 1464*0Sstevel@tonic-gate $msg = $!; 1465*0Sstevel@tonic-gate } 1466*0Sstevel@tonic-gate 1467*0Sstevel@tonic-gate # Check the exit status: 1468*0Sstevel@tonic-gate if ($rc != 0) { 1469*0Sstevel@tonic-gate if (-s $errfile) { 1470*0Sstevel@tonic-gate my $tmp; 1471*0Sstevel@tonic-gate my $errfile_fh = do { local *FH; *FH }; 1472*0Sstevel@tonic-gate if (open($errfile_fh, "<$errfile")) { 1473*0Sstevel@tonic-gate while (<$errfile_fh>) { 1474*0Sstevel@tonic-gate if (/ldd:/) { 1475*0Sstevel@tonic-gate $tmp = $_; 1476*0Sstevel@tonic-gate last; 1477*0Sstevel@tonic-gate } 1478*0Sstevel@tonic-gate } 1479*0Sstevel@tonic-gate close($errfile_fh); 1480*0Sstevel@tonic-gate } 1481*0Sstevel@tonic-gate if (defined($tmp)) { 1482*0Sstevel@tonic-gate chomp($tmp); 1483*0Sstevel@tonic-gate if ($tmp =~ /ldd:\s*(\S.*)$/) { 1484*0Sstevel@tonic-gate $tmp = $1; 1485*0Sstevel@tonic-gate } 1486*0Sstevel@tonic-gate if ($tmp =~ /^[^:]+:\s*(\S.*)$/) { 1487*0Sstevel@tonic-gate my $t = $1; 1488*0Sstevel@tonic-gate if ($t !~ /^\s*$/) { 1489*0Sstevel@tonic-gate $tmp = $t; 1490*0Sstevel@tonic-gate } 1491*0Sstevel@tonic-gate } 1492*0Sstevel@tonic-gate $msg = $tmp if ($tmp !~ /^\s*$/); 1493*0Sstevel@tonic-gate } 1494*0Sstevel@tonic-gate } 1495*0Sstevel@tonic-gate emsg("%s", norunprog("$cmd_ldd -r $object", "$msg\n")); 1496*0Sstevel@tonic-gate $msg =~ s/\n/ /g; 1497*0Sstevel@tonic-gate $msg =~ s/;/,/g; 1498*0Sstevel@tonic-gate $msg = sprintf("ERROR: " . gettext( 1499*0Sstevel@tonic-gate "Error running: ldd -r LD_DEBUG: %s"), $msg); 1500*0Sstevel@tonic-gate return $msg; 1501*0Sstevel@tonic-gate } 1502*0Sstevel@tonic-gate 1503*0Sstevel@tonic-gate # 1504*0Sstevel@tonic-gate # We now have all the output files created. We read them and 1505*0Sstevel@tonic-gate # merge them into one long string to return to whoever called 1506*0Sstevel@tonic-gate # us. The caller will parse it, not us. Our goal here is to 1507*0Sstevel@tonic-gate # just return the correct LD_DEBUG profile data. 1508*0Sstevel@tonic-gate # 1509*0Sstevel@tonic-gate 1510*0Sstevel@tonic-gate if (-f "$tmpdir/stdout") { 1511*0Sstevel@tonic-gate my $out_fh = do { local *FH; *FH }; 1512*0Sstevel@tonic-gate if (! open($out_fh, "<$tmpdir/stdout")) { 1513*0Sstevel@tonic-gate exiter(nofile("$tmpdir/stdout", $!)); 1514*0Sstevel@tonic-gate } 1515*0Sstevel@tonic-gate while (<$out_fh>) { 1516*0Sstevel@tonic-gate # Add the special prefix for STDOUT: 1517*0Sstevel@tonic-gate $result .= "stdout: $_"; 1518*0Sstevel@tonic-gate } 1519*0Sstevel@tonic-gate close($out_fh); 1520*0Sstevel@tonic-gate } 1521*0Sstevel@tonic-gate 1522*0Sstevel@tonic-gate my ($file, $count, $goodone, $ok, $aok, @file); 1523*0Sstevel@tonic-gate 1524*0Sstevel@tonic-gate $count = 0; 1525*0Sstevel@tonic-gate 1526*0Sstevel@tonic-gate my $prevline; 1527*0Sstevel@tonic-gate 1528*0Sstevel@tonic-gate # Loop over each "bind.NNNNN" file in the tmp directory: 1529*0Sstevel@tonic-gate foreach $file (<$tmpdir/bind.*>) { 1530*0Sstevel@tonic-gate 1531*0Sstevel@tonic-gate # Open it for reading: 1532*0Sstevel@tonic-gate my $ldd_file_fh = do { local *FH; *FH }; 1533*0Sstevel@tonic-gate if (! open($ldd_file_fh, "<$file")) { 1534*0Sstevel@tonic-gate exiter(nofile($file, $!)); 1535*0Sstevel@tonic-gate } 1536*0Sstevel@tonic-gate 1537*0Sstevel@tonic-gate # 1538*0Sstevel@tonic-gate # ok = 1 means this file we are reading the profile file 1539*0Sstevel@tonic-gate # corresponding to $object. We set ok = 0 as soon as we 1540*0Sstevel@tonic-gate # discover otherwise. 1541*0Sstevel@tonic-gate # 1542*0Sstevel@tonic-gate $ok = 1; 1543*0Sstevel@tonic-gate 1544*0Sstevel@tonic-gate # 1545*0Sstevel@tonic-gate # $aok = 1 means always OK. I.e. we are definitely in the 1546*0Sstevel@tonic-gate # correct profile. 1547*0Sstevel@tonic-gate # 1548*0Sstevel@tonic-gate $aok = 0; 1549*0Sstevel@tonic-gate 1550*0Sstevel@tonic-gate # 1551*0Sstevel@tonic-gate # this variable will hold the previous line so that we 1552*0Sstevel@tonic-gate # can skip adjacent duplicates. 1553*0Sstevel@tonic-gate # 1554*0Sstevel@tonic-gate $prevline = ''; 1555*0Sstevel@tonic-gate 1556*0Sstevel@tonic-gate my $idx; 1557*0Sstevel@tonic-gate 1558*0Sstevel@tonic-gate while (<$ldd_file_fh>) { 1559*0Sstevel@tonic-gate 1560*0Sstevel@tonic-gate # 1561*0Sstevel@tonic-gate # This check is done to perform a simple 1562*0Sstevel@tonic-gate # uniq'ing of the output. Non-PIC objects have 1563*0Sstevel@tonic-gate # lots of duplicates, many of them right after 1564*0Sstevel@tonic-gate # each other. 1565*0Sstevel@tonic-gate # 1566*0Sstevel@tonic-gate 1567*0Sstevel@tonic-gate next if ($_ eq $prevline); 1568*0Sstevel@tonic-gate $prevline = $_; 1569*0Sstevel@tonic-gate 1570*0Sstevel@tonic-gate # 1571*0Sstevel@tonic-gate # Check to see if this is the wrong profile 1572*0Sstevel@tonic-gate # file: The ones we know about are "ldd" and 1573*0Sstevel@tonic-gate # "sh". If the object under test is ever "ldd" 1574*0Sstevel@tonic-gate # or "sh" this will fail. 1575*0Sstevel@tonic-gate # 1576*0Sstevel@tonic-gate if ($aok) { 1577*0Sstevel@tonic-gate ; 1578*0Sstevel@tonic-gate } elsif ($ok) { 1579*0Sstevel@tonic-gate # 1580*0Sstevel@tonic-gate # checks line: 1581*0Sstevel@tonic-gate # file=ldd; analyzing [ RTLD_GLOBAL RTLD_LAZY ] 1582*0Sstevel@tonic-gate # 1583*0Sstevel@tonic-gate if (/\bfile=\S+\b(ldd|sh)\b/) { 1584*0Sstevel@tonic-gate $ok = 0; 1585*0Sstevel@tonic-gate } else { 1586*0Sstevel@tonic-gate $idx = 1587*0Sstevel@tonic-gate index($_, " file=$object; analyzing"); 1588*0Sstevel@tonic-gate $aok = 1 if ($idx != -1); 1589*0Sstevel@tonic-gate } 1590*0Sstevel@tonic-gate } 1591*0Sstevel@tonic-gate 1592*0Sstevel@tonic-gate # We can skip this file as soon as we see $ok = 0. 1593*0Sstevel@tonic-gate last unless ($ok); 1594*0Sstevel@tonic-gate 1595*0Sstevel@tonic-gate # Gather the profile output into a string: 1596*0Sstevel@tonic-gate $file[$count] .= $_; 1597*0Sstevel@tonic-gate } 1598*0Sstevel@tonic-gate 1599*0Sstevel@tonic-gate # 1600*0Sstevel@tonic-gate # Note that this one is the desired profile 1601*0Sstevel@tonic-gate # (i.e. if $ok is still true): 1602*0Sstevel@tonic-gate # 1603*0Sstevel@tonic-gate $goodone .= "$count," if ($ok); 1604*0Sstevel@tonic-gate 1605*0Sstevel@tonic-gate # On to the next $file: 1606*0Sstevel@tonic-gate close($ldd_file_fh); 1607*0Sstevel@tonic-gate $count++; 1608*0Sstevel@tonic-gate } 1609*0Sstevel@tonic-gate 1610*0Sstevel@tonic-gate if (defined($goodone)) { 1611*0Sstevel@tonic-gate $goodone =~ s/,$//; # Trim the last comma off. 1612*0Sstevel@tonic-gate } 1613*0Sstevel@tonic-gate 1614*0Sstevel@tonic-gate # If we have none or more than one "good one" we are in trouble: 1615*0Sstevel@tonic-gate if (! defined($goodone) || ($goodone !~ /^\d+$/) || ($goodone =~ /,/)) { 1616*0Sstevel@tonic-gate 1617*0Sstevel@tonic-gate # 1618*0Sstevel@tonic-gate # Note that this is the first point at which we would detect 1619*0Sstevel@tonic-gate # a problem with the checking of SUID/SGID objects, although 1620*0Sstevel@tonic-gate # in theory we could have skipped these objects earlier. 1621*0Sstevel@tonic-gate # We prefer to let the linker, ld.so.1, indicate this failure 1622*0Sstevel@tonic-gate # and then we catch it and diagnose it here. 1623*0Sstevel@tonic-gate # 1624*0Sstevel@tonic-gate my $suid = is_suid($object); 1625*0Sstevel@tonic-gate 1626*0Sstevel@tonic-gate if ($suid == 1) { 1627*0Sstevel@tonic-gate $result = "ERROR: " . gettext( 1628*0Sstevel@tonic-gate "SUID - ldd(1) LD_DEBUG profile failed"); 1629*0Sstevel@tonic-gate } elsif ($suid == 2) { 1630*0Sstevel@tonic-gate $result = "ERROR: " . gettext( 1631*0Sstevel@tonic-gate "SGID - ldd(1) LD_DEBUG profile failed"); 1632*0Sstevel@tonic-gate } else { 1633*0Sstevel@tonic-gate $result = "ERROR: " . gettext( 1634*0Sstevel@tonic-gate "could not get ldd(1) LD_DEBUG profile output"); 1635*0Sstevel@tonic-gate } 1636*0Sstevel@tonic-gate 1637*0Sstevel@tonic-gate } else { 1638*0Sstevel@tonic-gate # Append the correct profile to the result and return it: 1639*0Sstevel@tonic-gate $result .= $file[$goodone]; 1640*0Sstevel@tonic-gate } 1641*0Sstevel@tonic-gate 1642*0Sstevel@tonic-gate # Tidy up our mess by cleaning out the tmpdir. 1643*0Sstevel@tonic-gate unlink(<$tmpdir/*>) if ($tmpdir !~ m,^/*$,); 1644*0Sstevel@tonic-gate 1645*0Sstevel@tonic-gate return $result; 1646*0Sstevel@tonic-gate} 1647