1# $OpenBSD: Library.pm,v 1.14 2023/07/08 08:15:32 espie Exp $ 2 3# Copyright (c) 2007-2010 Steven Mestdagh <steven@openbsd.org> 4# Copyright (c) 2012 Marc Espie <espie@openbsd.org> 5# 6# Permission to use, copy, modify, and distribute this software for any 7# purpose with or without fee is hereby granted, provided that the above 8# copyright notice and this permission notice appear in all copies. 9# 10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 18use v5.36; 19 20package LT::Library::Stash; 21 22sub new($class) 23{ 24 bless {}, $class; 25} 26 27sub create($self, $key) 28{ 29 if (!exists $self->{$key}) { 30 $self->{$key} = LT::Library->new($key); 31 } 32 return $self->{$key}; 33} 34 35package LT::Library; 36 37use LT::Util; 38use LT::Trace; 39 40# find actual library filename 41# XXX pick the right one if multiple are found! 42sub resolve_library($self, $dirs, $shared, $staticflag, $linkmode, $gp = undef) 43{ 44 45 my $libtofind = $self->{key}; 46 my $libfile = 0; 47 my @globbedlib; 48 49 my $pic = ''; # used when finding static libraries 50 if ($linkmode eq 'LT::LaFile') { 51 $pic = '_pic'; 52 } 53 54 if (defined $self->{lafile}) { 55 require LT::LaFile; 56 # if there is a .la file, use the info from there 57 tsay {"found .la file $self->{lafile} for library key: ", 58 $self->{key}}; 59 my $lainfo = LT::LaFile->parse($self->{lafile}); 60 my $dlname = $lainfo->{dlname}; 61 my $oldlib = $lainfo->{old_library}; 62 my $libdir = $lainfo->{libdir}; 63 my $installed = $lainfo->{installed}; 64 my $d = abs_dir($self->{lafile}); 65 # get the name we need (this may include a -release) 66 if (!$dlname && !$oldlib) { 67 die "Link error: neither static nor shared library found in $self->{lafile}\n"; 68 } 69 if ($d !~ m/\Q$ltdir\E$/ && $installed eq 'no') { 70 $d .= "/$ltdir"; 71 } 72 if ($shared) { 73 if ($dlname) { 74 $libfile = "$d/$dlname"; 75 } else { 76 # fall back to static 77 $libfile = "$d/$oldlib"; 78 } 79 # if -static has been passed, don't link dynamically 80 # against not-installed libraries 81 if ($staticflag && $installed eq 'no') { 82 $libfile = "$d/$oldlib"; 83 } 84 } else { 85 $libfile = "$d/$oldlib"; 86 } 87 if (! -f $libfile) { 88 tsay {".la file ", $self->{lafile}, 89 "points to nonexistent file ", $libfile, " !"}; 90 } 91 } else { 92 # search in .libs when priority is high 93 push @$dirs, $gp->libsearchdirs if defined $gp; 94 tsay {"searching for $libtofind"}; 95 tsay {"search path= ", join(':', @$dirs)}; 96 tsay {"search type= ", $shared ? 'shared' : 'static'}; 97 foreach my $sd (@$dirs) { 98 if ($shared) { 99 # select correct library by sorting by version number only 100 my $bestlib = $self->findbest($sd, $libtofind); 101 if ($bestlib) { 102 tsay {"found $libtofind in $sd"}; 103 $libfile = $bestlib; 104 last; 105 } else { 106 # XXX find static library instead? 107 my $spath = "$sd/lib$libtofind$pic.a"; 108 if (-f $spath) { 109 tsay {"found static $libtofind in $sd"}; 110 $libfile = $spath; 111 last; 112 } 113 } 114 } else { 115 # look for a static library 116 my $spath = "$sd/lib$libtofind.a"; 117 if (-f $spath) { 118 tsay {"found static $libtofind in $sd"}; 119 $libfile = $spath; 120 last; 121 } 122 } 123 } 124 } 125 if (!$libfile) { 126 delete $self->{fullpath}; 127 if ($linkmode eq 'LT::LaFile') { 128 say "warning: dependency on $libtofind dropped"; 129 $self->{dropped} = 1; 130 } elsif ($linkmode eq 'LT::Program') { 131 die "Link error: $libtofind not found!\n"; 132 } 133 } else { 134 $self->{fullpath} = $libfile; 135 tsay {"\$libs->{$self->{key}}->{fullpath} = ", 136 $self->{fullpath}}; 137 } 138} 139 140sub findbest($self, $sd, $name) 141{ 142 my $best = undef; 143 if (opendir(my $dir, $sd)) { 144 my ($major, $minor) = (-1, -1); 145 while (my $e = readdir($dir)) { 146 next unless $e =~ m/^lib\Q$name\E\.so\.(\d+)\.(\d+)$/; 147 if ($1 > $major || ($1 == $major && $2 > $minor)) { 148 ($major, $minor) = ($1, $2); 149 $best = "$sd/$e"; 150 } 151 } 152 closedir($dir); 153 } 154 if (!defined $best) { 155 my $cand = "$sd/lib$name.so"; 156 if (-e $cand) { 157 $best = $cand; 158 } 159 } 160 return $best; 161} 162 163# give a list of library dependencies found in the actual shared library 164sub inspect($self) 165{ 166 my $filename = $self->{fullpath}; 167 my @deps; 168 169 if (!defined($filename)){ 170 say "warning: library was specified that could not be found: $self->{key}"; 171 return; 172 } 173 tsay {"inspecting $filename for library dependencies..."}; 174 open(my $fh, '-|', "objdump", "-p", "--", $filename); 175 while (<$fh>) { 176 if (m/\s+NEEDED\s+(\S+)\s*$/) { 177 push @deps, $1; 178 } 179 } 180 tsay {"found ", (@deps == 0) ? 'no ' : '', 181 "deps for $filename\n@deps"}; 182 return @deps; 183} 184 185# give the list of RPATH/RUNPATH directories 186sub findrpaths($self) 187{ 188 my $filename = $self->{fullpath}; 189 my @dirs; 190 191 if (!defined($filename)){ 192 say "warning: library was specified that could not be found: $self->{key}"; 193 return; 194 } 195 tsay {"inspecting $filename for non standard RPATH/RUNPATH..."}; 196 open(my $fh, '-|', "objdump", "-p", "--", $filename); 197 while (<$fh>) { 198 if (m/R(?:UN)?PATH\s+(.*)$/) { 199 @dirs = split(":", $1); 200 last; 201 } 202 } 203 tsay {"found ", (@dirs == 0) ? 'none ' : '', 204 "RPATH/RUNPATH for $filename\n@dirs"}; 205 return @dirs; 206} 207 208sub new($class, $key) 209{ 210 bless { key => $key }, $class; 211} 212 2131; 214