1e1051a39Sopenharmony_ci#! /usr/bin/env perl 2e1051a39Sopenharmony_ci# Copyright 2009-2020 The OpenSSL Project Authors. All Rights Reserved. 3e1051a39Sopenharmony_ci# 4e1051a39Sopenharmony_ci# Licensed under the Apache License 2.0 (the "License"). You may not use 5e1051a39Sopenharmony_ci# this file except in compliance with the License. You can obtain a copy 6e1051a39Sopenharmony_ci# in the file LICENSE in the source distribution or at 7e1051a39Sopenharmony_ci# https://www.openssl.org/source/license.html 8e1051a39Sopenharmony_ci 9e1051a39Sopenharmony_ci 10e1051a39Sopenharmony_ci# ==================================================================== 11e1051a39Sopenharmony_ci# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL 12e1051a39Sopenharmony_ci# project. The module is, however, dual licensed under OpenSSL and 13e1051a39Sopenharmony_ci# CRYPTOGAMS licenses depending on where you obtain it. For further 14e1051a39Sopenharmony_ci# details see http://www.openssl.org/~appro/cryptogams/. 15e1051a39Sopenharmony_ci# ==================================================================== 16e1051a39Sopenharmony_ci 17e1051a39Sopenharmony_ci# RC4 for PA-RISC. 18e1051a39Sopenharmony_ci 19e1051a39Sopenharmony_ci# June 2009. 20e1051a39Sopenharmony_ci# 21e1051a39Sopenharmony_ci# Performance is 33% better than gcc 3.2 generated code on PA-7100LC. 22e1051a39Sopenharmony_ci# For reference, [4x] unrolled loop is >40% faster than folded one. 23e1051a39Sopenharmony_ci# It's possible to unroll loop 8 times on PA-RISC 2.0, but improvement 24e1051a39Sopenharmony_ci# is believed to be not sufficient to justify the effort... 25e1051a39Sopenharmony_ci# 26e1051a39Sopenharmony_ci# Special thanks to polarhome.com for providing HP-UX account. 27e1051a39Sopenharmony_ci 28e1051a39Sopenharmony_ci$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; 29e1051a39Sopenharmony_ci 30e1051a39Sopenharmony_ci# $output is the last argument if it looks like a file (it has an extension) 31e1051a39Sopenharmony_ci# $flavour is the first argument if it doesn't look like a file 32e1051a39Sopenharmony_ci$output = $#ARGV >= 0 && $ARGV[$#ARGV] =~ m|\.\w+$| ? pop : undef; 33e1051a39Sopenharmony_ci$flavour = $#ARGV >= 0 && $ARGV[0] !~ m|\.| ? shift : undef; 34e1051a39Sopenharmony_ci 35e1051a39Sopenharmony_ci$output and open STDOUT,">$output"; 36e1051a39Sopenharmony_ci 37e1051a39Sopenharmony_ciif ($flavour =~ /64/) { 38e1051a39Sopenharmony_ci $LEVEL ="2.0W"; 39e1051a39Sopenharmony_ci $SIZE_T =8; 40e1051a39Sopenharmony_ci $FRAME_MARKER =80; 41e1051a39Sopenharmony_ci $SAVED_RP =16; 42e1051a39Sopenharmony_ci $PUSH ="std"; 43e1051a39Sopenharmony_ci $PUSHMA ="std,ma"; 44e1051a39Sopenharmony_ci $POP ="ldd"; 45e1051a39Sopenharmony_ci $POPMB ="ldd,mb"; 46e1051a39Sopenharmony_ci} else { 47e1051a39Sopenharmony_ci $LEVEL ="1.0"; 48e1051a39Sopenharmony_ci $SIZE_T =4; 49e1051a39Sopenharmony_ci $FRAME_MARKER =48; 50e1051a39Sopenharmony_ci $SAVED_RP =20; 51e1051a39Sopenharmony_ci $PUSH ="stw"; 52e1051a39Sopenharmony_ci $PUSHMA ="stwm"; 53e1051a39Sopenharmony_ci $POP ="ldw"; 54e1051a39Sopenharmony_ci $POPMB ="ldwm"; 55e1051a39Sopenharmony_ci} 56e1051a39Sopenharmony_ci 57e1051a39Sopenharmony_ci$FRAME=4*$SIZE_T+$FRAME_MARKER; # 4 saved regs + frame marker 58e1051a39Sopenharmony_ci # [+ argument transfer] 59e1051a39Sopenharmony_ci$SZ=1; # defaults to RC4_CHAR 60e1051a39Sopenharmony_ciif (open CONF,"<${dir}../../opensslconf.h") { 61e1051a39Sopenharmony_ci while(<CONF>) { 62e1051a39Sopenharmony_ci if (m/#\s*define\s+RC4_INT\s+(.*)/) { 63e1051a39Sopenharmony_ci $SZ = ($1=~/char$/) ? 1 : 4; 64e1051a39Sopenharmony_ci last; 65e1051a39Sopenharmony_ci } 66e1051a39Sopenharmony_ci } 67e1051a39Sopenharmony_ci close CONF; 68e1051a39Sopenharmony_ci} 69e1051a39Sopenharmony_ci 70e1051a39Sopenharmony_ciif ($SZ==1) { # RC4_CHAR 71e1051a39Sopenharmony_ci $LD="ldb"; 72e1051a39Sopenharmony_ci $LDX="ldbx"; 73e1051a39Sopenharmony_ci $MKX="addl"; 74e1051a39Sopenharmony_ci $ST="stb"; 75e1051a39Sopenharmony_ci} else { # RC4_INT (~5% faster than RC4_CHAR on PA-7100LC) 76e1051a39Sopenharmony_ci $LD="ldw"; 77e1051a39Sopenharmony_ci $LDX="ldwx,s"; 78e1051a39Sopenharmony_ci $MKX="sh2addl"; 79e1051a39Sopenharmony_ci $ST="stw"; 80e1051a39Sopenharmony_ci} 81e1051a39Sopenharmony_ci 82e1051a39Sopenharmony_ci$key="%r26"; 83e1051a39Sopenharmony_ci$len="%r25"; 84e1051a39Sopenharmony_ci$inp="%r24"; 85e1051a39Sopenharmony_ci$out="%r23"; 86e1051a39Sopenharmony_ci 87e1051a39Sopenharmony_ci@XX=("%r19","%r20"); 88e1051a39Sopenharmony_ci@TX=("%r21","%r22"); 89e1051a39Sopenharmony_ci$YY="%r28"; 90e1051a39Sopenharmony_ci$TY="%r29"; 91e1051a39Sopenharmony_ci 92e1051a39Sopenharmony_ci$acc="%r1"; 93e1051a39Sopenharmony_ci$ix="%r2"; 94e1051a39Sopenharmony_ci$iy="%r3"; 95e1051a39Sopenharmony_ci$dat0="%r4"; 96e1051a39Sopenharmony_ci$dat1="%r5"; 97e1051a39Sopenharmony_ci$rem="%r6"; 98e1051a39Sopenharmony_ci$mask="%r31"; 99e1051a39Sopenharmony_ci 100e1051a39Sopenharmony_cisub unrolledloopbody { 101e1051a39Sopenharmony_cifor ($i=0;$i<4;$i++) { 102e1051a39Sopenharmony_ci$code.=<<___; 103e1051a39Sopenharmony_ci ldo 1($XX[0]),$XX[1] 104e1051a39Sopenharmony_ci `sprintf("$LDX %$TY(%$key),%$dat1") if ($i>0)` 105e1051a39Sopenharmony_ci and $mask,$XX[1],$XX[1] 106e1051a39Sopenharmony_ci $LDX $YY($key),$TY 107e1051a39Sopenharmony_ci $MKX $YY,$key,$ix 108e1051a39Sopenharmony_ci $LDX $XX[1]($key),$TX[1] 109e1051a39Sopenharmony_ci $MKX $XX[0],$key,$iy 110e1051a39Sopenharmony_ci $ST $TX[0],0($ix) 111e1051a39Sopenharmony_ci comclr,<> $XX[1],$YY,%r0 ; conditional 112e1051a39Sopenharmony_ci copy $TX[0],$TX[1] ; move 113e1051a39Sopenharmony_ci `sprintf("%sdep %$dat1,%d,8,%$acc",$i==1?"z":"",8*($i-1)+7) if ($i>0)` 114e1051a39Sopenharmony_ci $ST $TY,0($iy) 115e1051a39Sopenharmony_ci addl $TX[0],$TY,$TY 116e1051a39Sopenharmony_ci addl $TX[1],$YY,$YY 117e1051a39Sopenharmony_ci and $mask,$TY,$TY 118e1051a39Sopenharmony_ci and $mask,$YY,$YY 119e1051a39Sopenharmony_ci___ 120e1051a39Sopenharmony_cipush(@TX,shift(@TX)); push(@XX,shift(@XX)); # "rotate" registers 121e1051a39Sopenharmony_ci} } 122e1051a39Sopenharmony_ci 123e1051a39Sopenharmony_cisub foldedloop { 124e1051a39Sopenharmony_cimy ($label,$count)=@_; 125e1051a39Sopenharmony_ci$code.=<<___; 126e1051a39Sopenharmony_ci$label 127e1051a39Sopenharmony_ci $MKX $YY,$key,$iy 128e1051a39Sopenharmony_ci $LDX $YY($key),$TY 129e1051a39Sopenharmony_ci $MKX $XX[0],$key,$ix 130e1051a39Sopenharmony_ci $ST $TX[0],0($iy) 131e1051a39Sopenharmony_ci ldo 1($XX[0]),$XX[0] 132e1051a39Sopenharmony_ci $ST $TY,0($ix) 133e1051a39Sopenharmony_ci addl $TX[0],$TY,$TY 134e1051a39Sopenharmony_ci ldbx $inp($out),$dat1 135e1051a39Sopenharmony_ci and $mask,$TY,$TY 136e1051a39Sopenharmony_ci and $mask,$XX[0],$XX[0] 137e1051a39Sopenharmony_ci $LDX $TY($key),$acc 138e1051a39Sopenharmony_ci $LDX $XX[0]($key),$TX[0] 139e1051a39Sopenharmony_ci ldo 1($out),$out 140e1051a39Sopenharmony_ci xor $dat1,$acc,$acc 141e1051a39Sopenharmony_ci addl $TX[0],$YY,$YY 142e1051a39Sopenharmony_ci stb $acc,-1($out) 143e1051a39Sopenharmony_ci addib,<> -1,$count,$label ; $count is always small 144e1051a39Sopenharmony_ci and $mask,$YY,$YY 145e1051a39Sopenharmony_ci___ 146e1051a39Sopenharmony_ci} 147e1051a39Sopenharmony_ci 148e1051a39Sopenharmony_ci$code=<<___; 149e1051a39Sopenharmony_ci .LEVEL $LEVEL 150e1051a39Sopenharmony_ci .SPACE \$TEXT\$ 151e1051a39Sopenharmony_ci .SUBSPA \$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY 152e1051a39Sopenharmony_ci 153e1051a39Sopenharmony_ci .EXPORT RC4,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR,ARGW3=GR 154e1051a39Sopenharmony_ciRC4 155e1051a39Sopenharmony_ci .PROC 156e1051a39Sopenharmony_ci .CALLINFO FRAME=`$FRAME-4*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=6 157e1051a39Sopenharmony_ci .ENTRY 158e1051a39Sopenharmony_ci $PUSH %r2,-$SAVED_RP(%sp) ; standard prologue 159e1051a39Sopenharmony_ci $PUSHMA %r3,$FRAME(%sp) 160e1051a39Sopenharmony_ci $PUSH %r4,`-$FRAME+1*$SIZE_T`(%sp) 161e1051a39Sopenharmony_ci $PUSH %r5,`-$FRAME+2*$SIZE_T`(%sp) 162e1051a39Sopenharmony_ci $PUSH %r6,`-$FRAME+3*$SIZE_T`(%sp) 163e1051a39Sopenharmony_ci 164e1051a39Sopenharmony_ci cmpib,*= 0,$len,L\$abort 165e1051a39Sopenharmony_ci sub $inp,$out,$inp ; distance between $inp and $out 166e1051a39Sopenharmony_ci 167e1051a39Sopenharmony_ci $LD `0*$SZ`($key),$XX[0] 168e1051a39Sopenharmony_ci $LD `1*$SZ`($key),$YY 169e1051a39Sopenharmony_ci ldo `2*$SZ`($key),$key 170e1051a39Sopenharmony_ci 171e1051a39Sopenharmony_ci ldi 0xff,$mask 172e1051a39Sopenharmony_ci ldi 3,$dat0 173e1051a39Sopenharmony_ci 174e1051a39Sopenharmony_ci ldo 1($XX[0]),$XX[0] ; warm up loop 175e1051a39Sopenharmony_ci and $mask,$XX[0],$XX[0] 176e1051a39Sopenharmony_ci $LDX $XX[0]($key),$TX[0] 177e1051a39Sopenharmony_ci addl $TX[0],$YY,$YY 178e1051a39Sopenharmony_ci cmpib,*>>= 6,$len,L\$oop1 ; is $len large enough to bother? 179e1051a39Sopenharmony_ci and $mask,$YY,$YY 180e1051a39Sopenharmony_ci 181e1051a39Sopenharmony_ci and,<> $out,$dat0,$rem ; is $out aligned? 182e1051a39Sopenharmony_ci b L\$alignedout 183e1051a39Sopenharmony_ci subi 4,$rem,$rem 184e1051a39Sopenharmony_ci sub $len,$rem,$len 185e1051a39Sopenharmony_ci___ 186e1051a39Sopenharmony_ci&foldedloop("L\$alignout",$rem); # process till $out is aligned 187e1051a39Sopenharmony_ci 188e1051a39Sopenharmony_ci$code.=<<___; 189e1051a39Sopenharmony_ciL\$alignedout ; $len is at least 4 here 190e1051a39Sopenharmony_ci and,<> $inp,$dat0,$acc ; is $inp aligned? 191e1051a39Sopenharmony_ci b L\$oop4 192e1051a39Sopenharmony_ci sub $inp,$acc,$rem ; align $inp 193e1051a39Sopenharmony_ci 194e1051a39Sopenharmony_ci sh3addl $acc,%r0,$acc 195e1051a39Sopenharmony_ci subi 32,$acc,$acc 196e1051a39Sopenharmony_ci mtctl $acc,%cr11 ; load %sar with vshd align factor 197e1051a39Sopenharmony_ci ldwx $rem($out),$dat0 198e1051a39Sopenharmony_ci ldo 4($rem),$rem 199e1051a39Sopenharmony_ciL\$oop4misalignedinp 200e1051a39Sopenharmony_ci___ 201e1051a39Sopenharmony_ci&unrolledloopbody(); 202e1051a39Sopenharmony_ci$code.=<<___; 203e1051a39Sopenharmony_ci $LDX $TY($key),$ix 204e1051a39Sopenharmony_ci ldwx $rem($out),$dat1 205e1051a39Sopenharmony_ci ldo -4($len),$len 206e1051a39Sopenharmony_ci or $ix,$acc,$acc ; last piece, no need to dep 207e1051a39Sopenharmony_ci vshd $dat0,$dat1,$iy ; align data 208e1051a39Sopenharmony_ci copy $dat1,$dat0 209e1051a39Sopenharmony_ci xor $iy,$acc,$acc 210e1051a39Sopenharmony_ci stw $acc,0($out) 211e1051a39Sopenharmony_ci cmpib,*<< 3,$len,L\$oop4misalignedinp 212e1051a39Sopenharmony_ci ldo 4($out),$out 213e1051a39Sopenharmony_ci cmpib,*= 0,$len,L\$done 214e1051a39Sopenharmony_ci nop 215e1051a39Sopenharmony_ci b L\$oop1 216e1051a39Sopenharmony_ci nop 217e1051a39Sopenharmony_ci 218e1051a39Sopenharmony_ci .ALIGN 8 219e1051a39Sopenharmony_ciL\$oop4 220e1051a39Sopenharmony_ci___ 221e1051a39Sopenharmony_ci&unrolledloopbody(); 222e1051a39Sopenharmony_ci$code.=<<___; 223e1051a39Sopenharmony_ci $LDX $TY($key),$ix 224e1051a39Sopenharmony_ci ldwx $inp($out),$dat0 225e1051a39Sopenharmony_ci ldo -4($len),$len 226e1051a39Sopenharmony_ci or $ix,$acc,$acc ; last piece, no need to dep 227e1051a39Sopenharmony_ci xor $dat0,$acc,$acc 228e1051a39Sopenharmony_ci stw $acc,0($out) 229e1051a39Sopenharmony_ci cmpib,*<< 3,$len,L\$oop4 230e1051a39Sopenharmony_ci ldo 4($out),$out 231e1051a39Sopenharmony_ci cmpib,*= 0,$len,L\$done 232e1051a39Sopenharmony_ci nop 233e1051a39Sopenharmony_ci___ 234e1051a39Sopenharmony_ci&foldedloop("L\$oop1",$len); 235e1051a39Sopenharmony_ci$code.=<<___; 236e1051a39Sopenharmony_ciL\$done 237e1051a39Sopenharmony_ci $POP `-$FRAME-$SAVED_RP`(%sp),%r2 238e1051a39Sopenharmony_ci ldo -1($XX[0]),$XX[0] ; chill out loop 239e1051a39Sopenharmony_ci sub $YY,$TX[0],$YY 240e1051a39Sopenharmony_ci and $mask,$XX[0],$XX[0] 241e1051a39Sopenharmony_ci and $mask,$YY,$YY 242e1051a39Sopenharmony_ci $ST $XX[0],`-2*$SZ`($key) 243e1051a39Sopenharmony_ci $ST $YY,`-1*$SZ`($key) 244e1051a39Sopenharmony_ci $POP `-$FRAME+1*$SIZE_T`(%sp),%r4 245e1051a39Sopenharmony_ci $POP `-$FRAME+2*$SIZE_T`(%sp),%r5 246e1051a39Sopenharmony_ci $POP `-$FRAME+3*$SIZE_T`(%sp),%r6 247e1051a39Sopenharmony_ciL\$abort 248e1051a39Sopenharmony_ci bv (%r2) 249e1051a39Sopenharmony_ci .EXIT 250e1051a39Sopenharmony_ci $POPMB -$FRAME(%sp),%r3 251e1051a39Sopenharmony_ci .PROCEND 252e1051a39Sopenharmony_ci___ 253e1051a39Sopenharmony_ci 254e1051a39Sopenharmony_ci$code.=<<___; 255e1051a39Sopenharmony_ci 256e1051a39Sopenharmony_ci .EXPORT RC4_set_key,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR 257e1051a39Sopenharmony_ci .ALIGN 8 258e1051a39Sopenharmony_ciRC4_set_key 259e1051a39Sopenharmony_ci .PROC 260e1051a39Sopenharmony_ci .CALLINFO NO_CALLS 261e1051a39Sopenharmony_ci .ENTRY 262e1051a39Sopenharmony_ci $ST %r0,`0*$SZ`($key) 263e1051a39Sopenharmony_ci $ST %r0,`1*$SZ`($key) 264e1051a39Sopenharmony_ci ldo `2*$SZ`($key),$key 265e1051a39Sopenharmony_ci copy %r0,@XX[0] 266e1051a39Sopenharmony_ciL\$1st 267e1051a39Sopenharmony_ci $ST @XX[0],0($key) 268e1051a39Sopenharmony_ci ldo 1(@XX[0]),@XX[0] 269e1051a39Sopenharmony_ci bb,>= @XX[0],`31-8`,L\$1st ; @XX[0]<256 270e1051a39Sopenharmony_ci ldo $SZ($key),$key 271e1051a39Sopenharmony_ci 272e1051a39Sopenharmony_ci ldo `-256*$SZ`($key),$key ; rewind $key 273e1051a39Sopenharmony_ci addl $len,$inp,$inp ; $inp to point at the end 274e1051a39Sopenharmony_ci sub %r0,$len,%r23 ; inverse index 275e1051a39Sopenharmony_ci copy %r0,@XX[0] 276e1051a39Sopenharmony_ci copy %r0,@XX[1] 277e1051a39Sopenharmony_ci ldi 0xff,$mask 278e1051a39Sopenharmony_ci 279e1051a39Sopenharmony_ciL\$2nd 280e1051a39Sopenharmony_ci $LDX @XX[0]($key),@TX[0] 281e1051a39Sopenharmony_ci ldbx %r23($inp),@TX[1] 282e1051a39Sopenharmony_ci addi,nuv 1,%r23,%r23 ; increment and conditional 283e1051a39Sopenharmony_ci sub %r0,$len,%r23 ; inverse index 284e1051a39Sopenharmony_ci addl @TX[0],@XX[1],@XX[1] 285e1051a39Sopenharmony_ci addl @TX[1],@XX[1],@XX[1] 286e1051a39Sopenharmony_ci and $mask,@XX[1],@XX[1] 287e1051a39Sopenharmony_ci $MKX @XX[0],$key,$TY 288e1051a39Sopenharmony_ci $LDX @XX[1]($key),@TX[1] 289e1051a39Sopenharmony_ci $MKX @XX[1],$key,$YY 290e1051a39Sopenharmony_ci ldo 1(@XX[0]),@XX[0] 291e1051a39Sopenharmony_ci $ST @TX[0],0($YY) 292e1051a39Sopenharmony_ci bb,>= @XX[0],`31-8`,L\$2nd ; @XX[0]<256 293e1051a39Sopenharmony_ci $ST @TX[1],0($TY) 294e1051a39Sopenharmony_ci 295e1051a39Sopenharmony_ci bv,n (%r2) 296e1051a39Sopenharmony_ci .EXIT 297e1051a39Sopenharmony_ci nop 298e1051a39Sopenharmony_ci .PROCEND 299e1051a39Sopenharmony_ci 300e1051a39Sopenharmony_ci .EXPORT RC4_options,ENTRY 301e1051a39Sopenharmony_ci .ALIGN 8 302e1051a39Sopenharmony_ciRC4_options 303e1051a39Sopenharmony_ci .PROC 304e1051a39Sopenharmony_ci .CALLINFO NO_CALLS 305e1051a39Sopenharmony_ci .ENTRY 306e1051a39Sopenharmony_ci blr %r0,%r28 307e1051a39Sopenharmony_ci ldi 3,%r1 308e1051a39Sopenharmony_ciL\$pic 309e1051a39Sopenharmony_ci andcm %r28,%r1,%r28 310e1051a39Sopenharmony_ci bv (%r2) 311e1051a39Sopenharmony_ci .EXIT 312e1051a39Sopenharmony_ci ldo L\$opts-L\$pic(%r28),%r28 313e1051a39Sopenharmony_ci .PROCEND 314e1051a39Sopenharmony_ci .ALIGN 8 315e1051a39Sopenharmony_ciL\$opts 316e1051a39Sopenharmony_ci .STRINGZ "rc4(4x,`$SZ==1?"char":"int"`)" 317e1051a39Sopenharmony_ci .STRINGZ "RC4 for PA-RISC, CRYPTOGAMS by <appro\@openssl.org>" 318e1051a39Sopenharmony_ci___ 319e1051a39Sopenharmony_ci 320e1051a39Sopenharmony_ciif (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1` 321e1051a39Sopenharmony_ci =~ /GNU assembler/) { 322e1051a39Sopenharmony_ci $gnuas = 1; 323e1051a39Sopenharmony_ci} 324e1051a39Sopenharmony_ci 325e1051a39Sopenharmony_ciforeach(split("\n",$code)) { 326e1051a39Sopenharmony_ci s/\`([^\`]*)\`/eval $1/ge; 327e1051a39Sopenharmony_ci 328e1051a39Sopenharmony_ci s/(\.LEVEL\s+2\.0)W/$1w/ if ($gnuas && $SIZE_T==8); 329e1051a39Sopenharmony_ci s/\.SPACE\s+\$TEXT\$/.text/ if ($gnuas && $SIZE_T==8); 330e1051a39Sopenharmony_ci s/\.SUBSPA.*// if ($gnuas && $SIZE_T==8); 331e1051a39Sopenharmony_ci s/cmpib,\*/comib,/ if ($SIZE_T==4); 332e1051a39Sopenharmony_ci s/\bbv\b/bve/ if ($SIZE_T==8); 333e1051a39Sopenharmony_ci 334e1051a39Sopenharmony_ci print $_,"\n"; 335e1051a39Sopenharmony_ci} 336e1051a39Sopenharmony_ciclose STDOUT or die "error closing STDOUT: $!"; 337