#!/usr/bin/perl -w
BEGIN {
chdir
't'
if
-d
't'
;
@INC
=
'../lib'
;
require
"./test.pl"
;
}
$^V =~ /^v\d+\.\d*[13579]\./
or skip_all
"on maint"
;
if
(
$Config
{cc} =~ /g\+\+/) {
skip_all
"on g++"
;
}
if
(
$Config
{ccname} eq
"gcc"
&&
$Config
{ccflags} =~ /-flto\b/) {
skip_all
"LTO libperl.a flags don't match the final linker sections"
;
}
my
$libperl_a
;
for
my
$f
(
qw(../libperl.a libperl.a)
) {
if
(-f
$f
) {
$libperl_a
=
$f
;
last
;
}
}
unless
(
defined
$libperl_a
) {
skip_all
"no libperl.a"
;
}
print
"# \$^O = $^O\n"
;
print
"# \$Config{archname} = $Config{archname}\n"
;
print
"# \$Config{cc} = $Config{cc}\n"
;
print
"# libperl = $libperl_a\n"
;
my
$nm
;
my
$nm_opt
=
''
;
my
$nm_style
;
my
$nm_fh
;
my
$nm_err_tmp
=
"libperl$$"
;
END {
unlink
$nm_err_tmp
if
$nm_err_tmp
;
}
my
$fake_input
;
my
$fake_style
;
if
(
@ARGV
== 1) {
$fake_input
=
shift
@ARGV
;
print
"# Faking nm output from $fake_input\n"
;
if
(
$fake_input
=~ s/\@(.+)$//) {
$fake_style
= $1;
print
"# Faking nm style from $fake_style\n"
;
if
(
$fake_style
eq
'gnu'
||
$fake_style
eq
'linux'
||
$fake_style
eq
'freebsd'
) {
$nm_style
=
'gnu'
}
elsif
(
$fake_style
eq
'darwin'
||
$fake_style
eq
'osx'
) {
$nm_style
=
'darwin'
}
else
{
die
"$0: Unknown explicit nm style '$fake_style'\n"
;
}
}
}
unless
(
defined
$nm_style
) {
if
($^O eq
'linux'
) {
$nm_style
=
'gnu'
;
}
elsif
($^O eq
'freebsd'
) {
$nm_style
=
'gnu'
;
}
elsif
($^O eq
'darwin'
) {
$nm_style
=
'darwin'
;
}
}
if
(
defined
$nm_style
) {
if
(
$nm_style
eq
'gnu'
) {
$nm
=
'/usr/bin/nm'
;
}
elsif
(
$nm_style
eq
'darwin'
) {
$nm
=
'/usr/bin/nm'
;
$nm_opt
=
'-m'
;
}
else
{
die
"$0: Unexpected nm style '$nm_style'\n"
;
}
}
if
($^O eq
'linux'
&&
$Config
{archname} !~ /^(?:x|i6)86/) {
skip_all
"linux but archname $Config{archname} not x86*"
;
}
unless
(
defined
$nm
) {
skip_all
"no nm"
;
}
unless
(
defined
$nm_style
) {
skip_all
"no nm style"
;
}
print
"# nm = $nm\n"
;
print
"# nm_style = $nm_style\n"
;
print
"# nm_opt = $nm_opt\n"
;
unless
(-x
$nm
) {
skip_all
"no executable nm $nm"
;
}
if
(
$nm_style
eq
'gnu'
&& !
defined
$fake_style
) {
open
(
my
$gnu_verify
,
"$nm --version|"
) or
skip_all
"nm failed: $!"
;
my
$gnu_verified
;
while
(<
$gnu_verify
>) {
if
(/^GNU nm/) {
$gnu_verified
= 1;
last
;
}
}
unless
(
$gnu_verified
) {
skip_all
"no GNU nm"
;
}
}
if
(
defined
$fake_input
) {
if
(
$fake_input
eq
'-'
) {
open
(
$nm_fh
,
"<&STDIN"
) or
skip_all
"Duping STDIN failed: $!"
;
}
else
{
open
(
$nm_fh
,
"<"
,
$fake_input
) or
skip_all
"Opening '$fake_input' failed: $!"
;
}
undef
$nm_err_tmp
;
}
else
{
print
qq{# command: "$nm $nm_opt $libperl_a 2>$nm_err_tmp |"\n}
;
open
(
$nm_fh
,
"$nm $nm_opt $libperl_a 2>$nm_err_tmp |"
) or
skip_all
"$nm $nm_opt $libperl_a failed: $!"
;
}
sub
is_perlish_symbol {
$_
[0] =~ /^(?:PL_|Perl|PerlIO)/;
}
sub
nm_parse_gnu {
my
$symbols
=
shift
;
my
$line
=
$_
;
if
(m{^(\w+\.o):$}) {
$symbols
->{obj}{$1}++;
$symbols
->{o} = $1;
return
;
}
else
{
die
"$0: undefined current object: $line"
unless
defined
$symbols
->{o};
if
(s/^[0-9a-f]{8}(?:[0-9a-f]{8})? //) {
if
(/^[Rr] (\w+)$/) {
$symbols
->{data}{const}{$1}{
$symbols
->{o}}++;
}
elsif
(/^r .+$/) {
}
elsif
(/^([Tti]) (\w+)(\..+)?$/) {
$symbols
->{text}{$2}{
$symbols
->{o}}{$1}++;
}
elsif
(/^C (\w+)$/) {
$symbols
->{data}{common}{$1}{
$symbols
->{o}}++;
}
elsif
(/^[BbSs] (\w+)(\.\d+)?$/) {
$symbols
->{data}{bss}{$1}{
$symbols
->{o}}++;
}
elsif
(/^D _LIB_VERSION$/) {
}
elsif
(/^[DdGg] (\w+)$/) {
$symbols
->{data}{data}{$1}{
$symbols
->{o}}++;
}
elsif
(/^. \.?(\w+)$/) {
print
"# Unknown type: $line ($symbols->{o})\n"
;
}
return
;
}
elsif
(/^ {8}(?: {8})? U _?(\w+)$/) {
my
(
$symbol
) = $1;
return
if
is_perlish_symbol(
$symbol
);
$symbols
->{
undef
}{
$symbol
}{
$symbols
->{o}}++;
return
;
}
}
print
"# Unexpected nm output '$line' ($symbols->{o})\n"
;
}
sub
nm_parse_darwin {
my
$symbols
=
shift
;
my
$line
=
$_
;
if
(m{^(?:.+)?libperl\.a\((\w+\.o)\):$} ||
m{^(\w+\.o):$}) {
$symbols
->{obj}{$1}++;
$symbols
->{o} = $1;
return
;
}
else
{
die
"$0: undefined current object: $line"
unless
defined
$symbols
->{o};
if
(s/^[0-9a-f]{8}(?:[0-9a-f]{8})? //) {
if
(/^\(__TEXT,__(?:const|(?:asan_)?cstring|literal\d+)\) (?:non-)?external _?(\w+)(\.\w+){0,2}$/) {
my
(
$symbol
,
$suffix
) = ($1, $2);
return
if
defined
$suffix
&& /__TEXT,__const/;
return
if
$symbol
=~ /^L\.str\d+$/;
$symbols
->{data}{const}{
$symbol
}{
$symbols
->{o}}++;
}
elsif
(/^\(__TEXT,__text\) ((?:non-|private )?external) \[cold func\] _(\w+\.cold\.[1-9][0-9]*)$/) {
}
elsif
(/^\(__TEXT,__text\) ((?:non-|private )?external) _(\w+)$/) {
my
(
$exp
,
$sym
) = ($1, $2);
$symbols
->{text}{
$sym
}{
$symbols
->{o}}{
$exp
=~ /^non/ ?
't'
:
'T'
}++;
}
elsif
(/^\(__DATA,__\w*?(const|data|bss|common)\w*\) (?:non-)?external _?(\w+)(\.\w+){0,3}$/) {
my
(
$dtype
,
$symbol
,
$suffix
) = ($1, $2, $3);
return
if
defined
$suffix
;
$symbols
->{data}{
$dtype
}{
$symbol
}{
$symbols
->{o}}++;
}
elsif
(/^\(__DATA,__const\) non-external _\.memset_pattern\d*$/) {
}
elsif
(/^\(__TEXT,__eh_frame/) {
return
;
}
elsif
(/^\(__\w+,__\w+\) /) {
print
"# Unknown type: $line ($symbols->{o})\n"
;
}
return
;
}
elsif
(/^ {8}(?: {8})? \(undefined(?: \[lazy bound\])?\) external _?(.+)/) {
my
(
$symbol
) = $1 =~ s/\
$UNIX2003
\z//r;
return
if
is_perlish_symbol(
$symbol
);
$symbols
->{
undef
}{
$symbol
}{
$symbols
->{o}}++;
return
;
}
}
print
"# Unexpected nm output '$line' ($symbols->{o})\n"
;
}
my
$nm_parse
;
if
(
$nm_style
eq
'gnu'
) {
$nm_parse
= \
&nm_parse_gnu
;
}
elsif
(
$nm_style
eq
'darwin'
) {
$nm_parse
= \
&nm_parse_darwin
;
}
unless
(
defined
$nm_parse
) {
skip_all
"no nm parser ($nm_style $nm_style, \$^O $^O)"
;
}
my
%symbols
;
while
(<
$nm_fh
>) {
next
if
/^$/;
chomp
;
$nm_parse
->(\
%symbols
);
}
unless
(
keys
%symbols
) {
skip_all
"no symbols\n"
;
}
unless
(
exists
$symbols
{text}) {
skip_all
"no text symbols\n"
;
}
ok(
$symbols
{obj}{
'util.o'
},
"has object util.o"
);
ok(
$symbols
{text}{
'Perl_croak'
}{
'util.o'
},
"has text Perl_croak in util.o"
);
ok(
exists
$symbols
{data}{const},
"has data const symbols"
);
ok(
$symbols
{data}{const}{PL_no_modify}{
'globals.o'
},
"has PL_no_modify"
);
my
$nocommon
=
$Config
{ccflags} =~ /-fno-common/ ? 1 : 0;
print
"# nocommon = $nocommon\n"
;
my
%data_symbols
;
for
my
$dtype
(
sort
keys
%{
$symbols
{data}}) {
for
my
$symbol
(
sort
keys
%{
$symbols
{data}{
$dtype
}}) {
$data_symbols
{
$symbol
}++;
}
}
if
( !
$symbols
{data}{common} ) {
$symbols
{data}{common} =
$symbols
{data}{bss};
}
ok(
$symbols
{data}{common}{PL_hash_seed_w}{
'globals.o'
},
"has PL_hash_seed_w"
);
ok(
$symbols
{data}{data}{PL_ppaddr}{
'globals.o'
},
"has PL_ppaddr"
);
ok(
keys
%{
$symbols
{
undef
}},
"has undefined symbols"
);
my
%expected
= (
chmod
=>
undef
,
socket
=>
'd_socket'
,
getenv
=>
undef
,
sigaction
=>
'd_sigaction'
,
time
=>
'd_time'
,
);
if
(
$Config
{uselongdouble} &&
$Config
{longdblsize} >
$Config
{doublesize}) {
$expected
{expl} =
undef
;
}
elsif
(
$Config
{usequadmath}) {
$expected
{expq} =
undef
;
}
else
{
$expected
{
exp
} =
undef
;
}
if
(
$Config
{usedl} ) {
$expected
{dlopen} =
'd_dlopen'
;
}
for
my
$symbol
(
sort
keys
%expected
) {
if
(
defined
$expected
{
$symbol
} && !
$Config
{
$expected
{
$symbol
}}) {
SKIP: {
skip(
"no $symbol"
);
}
next
;
}
my
@o
=
exists
$symbols
{
undef
}{
$symbol
} ?
sort
keys
%{
$symbols
{
undef
}{
$symbol
} } : ();
ok(
@o
,
"uses $symbol (@o)"
);
}
my
%unexpected
;
for
my
$str
(
qw(system)
) {
$unexpected
{
$str
} =
"d_$str"
;
}
for
my
$stdio
(
qw(gets fgets tmpfile sprintf vsprintf)
) {
$unexpected
{
$stdio
} =
undef
;
}
for
my
$str
(
qw(strcat strcpy strncat strncpy)
) {
$unexpected
{
$str
} =
undef
;
}
$unexpected
{atoi} =
undef
;
$unexpected
{atol} =
undef
;
for
my
$str
(
qw(atoll strtol strtoul strtoq)
) {
$unexpected
{
$str
} =
"d_$str"
;
}
for
my
$symbol
(
sort
keys
%unexpected
) {
if
(
defined
$unexpected
{
$symbol
} && !
$Config
{
$unexpected
{
$symbol
}}) {
SKIP: {
skip(
"no $symbol"
);
}
next
;
}
my
@o
=
exists
$symbols
{
undef
}{
$symbol
} ?
sort
keys
%{
$symbols
{
undef
}{
$symbol
} } : ();
if
(
$symbol
eq
'sprintf'
&&
$Config
{d_Gconvert} =~ /^
sprintf
/ &&
@o
== 1 &&
$o
[0] eq
'sv.o'
) {
SKIP: {
skip(
"uses sprintf for Gconvert in sv.o"
);
}
}
else
{
is(
@o
, 0,
"uses no $symbol (@o)"
);
}
}
my
$export_S_prefix
= 0;
for
my
$t
(
sort
grep
{ /^S_/ }
keys
%{
$symbols
{text}}) {
for
my
$o
(
sort
keys
%{
$symbols
{text}{
$t
}}) {
if
(
exists
$symbols
{text}{
$t
}{
$o
}{T}) {
fail(
$t
,
"$t exported from $o"
);
$export_S_prefix
++;
}
}
}
is(
$export_S_prefix
, 0,
"no S_ exports"
);
if
(
defined
$nm_err_tmp
) {
if
(
open
(
my
$nm_err_fh
,
$nm_err_tmp
)) {
my
$error
;
while
(<
$nm_err_fh
>) {
if
( $^O eq
'darwin'
) {
if
(/nm:
no
name list/ || /^(.*: )?
no
symbols$/ ) {
print
"# $^O ignoring $nm output: $_"
;
next
;
}
}
warn
"$0: Unexpected $nm error: $_"
;
$error
++;
}
die
"$0: Unexpected $nm errors\n"
if
$error
;
}
else
{
warn
"Failed to open '$nm_err_tmp': $!\n"
;
}
}
done_testing();