1# $OpenBSD: Library.pm,v 1.8 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;
19use feature qw(say);
20
21use LT::LaFile;
22
23package LT::LaFile;
24sub link(@p)
25{
26	return LT::Linker::LaFile->new->link(@p);
27}
28
29package LT::Linker::LaFile;
30our @ISA = qw(LT::Linker);
31
32use LT::Util;
33use LT::Trace;
34use File::Basename;
35
36sub link
37{
38	my ($linker, $self, $ltprog, $ltconfig, $la, $fname, $odir, $shared,
39	    $objs, $dirs, $libs, $deplibs, $libdirs, $parser, $gp) = @_;
40
41	tsay {"creating link command for library (linked ",
42		($shared) ? "dynamically" : "statically", ")"};
43
44	my $RPdirs = $self->{RPdirs};
45
46	my @libflags;
47	my @cmd;
48	my $dst = ($odir eq '.') ? "$ltdir/$fname" : "$odir/$ltdir/$fname";
49	if ($la =~ m/\.a$/) {
50		# probably just a convenience library
51		$dst = ($odir eq '.') ? "$fname" : "$odir/$fname";
52	}
53	my $symlinkdir = $ltdir;
54	if ($odir ne '.') {
55		$symlinkdir = "$odir/$ltdir";
56	}
57	mkdir $symlinkdir if ! -d $symlinkdir;
58
59	my ($staticlibs, $finalorderedlibs, $args) =
60	    $linker->common1($parser, $gp, $deplibs, $libdirs, $dirs, $libs);
61
62	# static linking
63	if (!$shared) {
64		@cmd = ('ar', 'cru', $dst);
65		foreach my $a (@$staticlibs) {
66			if ($a =~ m/\.a$/ && $a !~ m/_pic\.a/) {
67				# extract objects from archive
68				my $libfile = basename($a);
69				my $xdir = "$odir/$ltdir/${la}x/$libfile";
70				LT::Archive->extract($xdir, $a);
71				my @kobjs = LT::Archive->get_objlist($a);
72				map { $_ = "$xdir/$_"; } @kobjs;
73				push @libflags, @kobjs;
74			}
75		}
76		foreach my $k (@$finalorderedlibs) {
77			my $l = $libs->{$k};
78			# XXX improve test
79			# this has to be done probably only with
80			# convenience libraries
81			next if !defined $l->{lafile};
82			my $lainfo = LT::LaFile->parse($l->{lafile});
83			next if ($lainfo->stringize('dlname') ne '');
84			$l->resolve_library($dirs, 0, 0, ref($self));
85			my $a = $l->{fullpath};
86			if ($a =~ m/\.a$/ && $a !~ m/_pic\.a/) {
87				# extract objects from archive
88				my $libfile = basename $a;
89				my $xdir = "$odir/$ltdir/${la}x/$libfile";
90				LT::Archive->extract($xdir, $a);
91				my @kobjs = LT::Archive->get_objlist($a);
92				map { $_ = "$xdir/$_"; } @kobjs;
93				push @libflags, @kobjs;
94			}
95		}
96		push @cmd, @libflags if @libflags;
97		push @cmd, @$objs if @$objs;
98		my ($fh, $file);
99
100		if (@cmd > 512) {
101			use OpenBSD::MkTemp qw(mkstemp);
102			my @extra = splice(@cmd, 512);
103			($fh, $file) = mkstemp("/tmp/arargs.XXXXXXXXXXXX");
104			print $fh map {"$_\n"} @extra;
105			close $fh;
106			push @cmd, "\@$file";
107		}
108		LT::Exec->link(@cmd);
109		unlink($file) if defined $file;
110
111		LT::Exec->link('ranlib', $dst);
112		return;
113	}
114
115	my $tmp = [];
116	while (my $k = shift @$finalorderedlibs) {
117		my $l = $libs->{$k};
118		$l->resolve_library($dirs, 1, $gp->static, ref($self));
119		if ($l->{dropped}) {
120			# remove library if dependency on it has been dropped
121			delete $libs->{$k};
122		} else {
123			push(@$tmp, $k);
124		}
125	}
126	$finalorderedlibs = $tmp;
127
128	my @libobjects = values %$libs;
129	tsay {"libs:\n", join("\n", (keys %$libs))};
130	tsay {"libfiles:\n",
131	    join("\n", map { $_->{fullpath}//'UNDEF' } @libobjects) };
132
133	$linker->create_symlinks($symlinkdir, $libs);
134	my $prev_was_archive = 0;
135	my $libcounter = 0;
136	foreach my $k (@$finalorderedlibs) {
137		my $a = $libs->{$k}->{fullpath} || die "Link error: $k not found in \$libs\n";
138		if ($a =~ m/\.a$/) {
139			# don't make a -lfoo out of a static library
140			push @libflags, '-Wl,-whole-archive' unless $prev_was_archive;
141			push @libflags, $a;
142			if ($libcounter == @$finalorderedlibs - 1) {
143				push @libflags, '-Wl,-no-whole-archive';
144			}
145			$prev_was_archive = 1;
146		} else {
147			push @libflags, '-Wl,-no-whole-archive' if $prev_was_archive;
148			$prev_was_archive = 0;
149			push @libflags, $linker->infer_libparameter($a, $k);
150		}
151		$libcounter++;
152	}
153
154	# add libdirs to rpath if they are not in standard lib path
155	for my $l (@$libdirs) {
156		if (!LT::OSConfig->is_search_dir($l)) {
157			push @$RPdirs, $l;
158		}
159	}
160
161	my @linkeropts = ();
162	if (!$ltconfig->noshared) {
163		push(@linkeropts, '-soname', $fname);
164		for my $d (@$RPdirs) {
165			push(@linkeropts, '-rpath', $d);
166		}
167	}
168
169	@cmd = @$ltprog;
170	push @cmd, $ltconfig->sharedflag, @{$ltconfig->picflags};
171	push @cmd, '-o', $dst;
172	push @cmd, '-pthread' if $parser->{pthread};
173	push @cmd, @$args if $args;
174	push @cmd, @$objs if @$objs;
175	push @cmd, '-Wl,-whole-archive', @$staticlibs, '-Wl,-no-whole-archive'
176	    if @$staticlibs;
177	push @cmd, "-L$symlinkdir", @libflags if @libflags;
178
179	my @e = $linker->export_symbols($ltconfig,
180	    "$odir/$ltdir/$la", $gp, @$objs, @$staticlibs);
181	push(@cmd, join(',', "-Wl", @e)) if @e;
182	push @cmd, join(',', "-Wl", @linkeropts) if @linkeropts;
183	LT::Exec->link(@cmd);
184}
185
1861;
187