summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/speclib
blob: 5f73fed75b30a9bf8dbfe3c63a498e50a69ede01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/perl
use Getopt::Long;
use File::Temp qw'tempdir';
use File::Basename;
use strict;

sub dllname($;$);

my $verbose;
my $static;
my $exclude;

GetOptions('static!'=>\$static, 'v|exclude!'=>\$exclude);

my $lib = shift;
my $nm = shift;
my $ar = shift;
my $libdll = shift;

open my $nm_fd, '-|', $nm, '-Ap', '--defined-only', @ARGV, $libdll or
  die "$0: execution of $nm for object files failed - $!\n";

my %match_syms = ();
my $symfiles = ();
my $lastfn;
my @headtail = ();
my %extract = ();
my $libdllname;
while (<$nm_fd>) {
    study;
    m%^\Q$libdll\E:([^:]*):\d+ i \.idata\$([56])% and do {
	if ($2 eq 5) {
	    push @headtail, $1;
	} else {
	    pop @headtail;
	}
	next;
    };
    m%^\Q$libdll\E:[^:]*:\d+ I (__head_.*)$% and do {
	$libdllname = $1;
	next;
    };
    next unless m%^([^:]*):([^:]*(?=:))?.* [DTI] (.*)%o;
    if ($1 ne $libdll) {
	 $match_syms{$3} = 1;
     } elsif ($match_syms{$3} ? !$exclude : $exclude) {
	 $extract{$2} = 1;
     }
}
close $nm_fd;

%extract or die "$0: couldn't find symbols for $lib\n";
defined($libdllname) or die "$0: couldn't determine __head_<NAME> - malformed import archive?\n";
for (@headtail) {
    $extract{$_} = 1;
}

my $dir = tempdir(CLEANUP => 1);

chdir $dir;
# print join(' ', '+', $ar, 'x', sort keys %extract), "\n";
my $res = system $ar, 'x', $libdll, sort keys %extract;
die "$0: $ar extraction exited with non-zero status\n" if $res;
unlink $lib;
$res = system $ar, 'crus', $lib, sort keys %extract;
die "$0: $ar creation exited with non-zero status\n" if $res;

open my $lib_fd, '<', $lib or die "$0: couldn't open $lib for input - $!\n";
binmode $lib_fd;

my $libname = dllname($lib, 'lib');
my $pad = length($libdllname) - length($libname);
die "$0: library name too long (" . length($libname) . ")\n" if $pad < 0;
$libname .= "\0" x $pad;

$res = sysread($lib_fd, $_, -s $lib);
close $lib_fd;

die "$0: couldn't read $lib - $!\n" if $res != -s _;
0 while s/$libdllname/$libname/sog;

open $lib_fd, '>', $lib or die "$0: couldn't open $lib for output - $!\n";
syswrite($lib_fd, $_) == length($_) or die "$0: write to $lib failed - $!\n";
close $lib_fd;
exit 0;

sub dllname($;$) {
    my $x = basename($_[0], '.a');
    $x =~ s/^lib//o;
    return '__head_' . $_[1] . $x;
}