| #! /usr/bin/env perl | 
 | # Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved. | 
 | # | 
 | # Licensed under the OpenSSL license (the "License").  You may not use | 
 | # this file except in compliance with the License.  You can obtain a copy | 
 | # in the file LICENSE in the source distribution or at | 
 | # https://www.openssl.org/source/license.html | 
 |  | 
 |  | 
 | package x86gas; | 
 |  | 
 | *out=\@::out; | 
 |  | 
 | $::lbdecor=$::aout?"L":".L";		# local label decoration | 
 | $nmdecor=($::aout or $::coff)?"_":"";	# external name decoration | 
 |  | 
 | $initseg=""; | 
 |  | 
 | $align=16; | 
 | $align=log($align)/log(2) if ($::aout); | 
 | $com_start="#" if ($::aout or $::coff); | 
 |  | 
 | sub opsize() | 
 | { my $reg=shift; | 
 |     if    ($reg =~ m/^%e/o)		{ "l"; } | 
 |     elsif ($reg =~ m/^%[a-d][hl]$/o)	{ "b"; } | 
 |     elsif ($reg =~ m/^%[yxm]/o)		{ undef; } | 
 |     else				{ "w"; } | 
 | } | 
 |  | 
 | # swap arguments; | 
 | # expand opcode with size suffix; | 
 | # prefix numeric constants with $; | 
 | sub ::generic | 
 | { my($opcode,@arg)=@_; | 
 |   my($suffix,$dst,$src); | 
 |  | 
 |     @arg=reverse(@arg); | 
 |  | 
 |     for (@arg) | 
 |     {	s/^(\*?)(e?[a-dsixphl]{2})$/$1%$2/o;	# gp registers | 
 | 	s/^([xy]?mm[0-7])$/%$1/o;		# xmm/mmx registers | 
 | 	s/^(\-?[0-9]+)$/\$$1/o;			# constants | 
 | 	s/^(\-?0x[0-9a-f]+)$/\$$1/o;		# constants | 
 |     } | 
 |  | 
 |     $dst = $arg[$#arg]		if ($#arg>=0); | 
 |     $src = $arg[$#arg-1]	if ($#arg>=1); | 
 |     if    ($dst =~ m/^%/o)	{ $suffix=&opsize($dst); } | 
 |     elsif ($src =~ m/^%/o)	{ $suffix=&opsize($src); } | 
 |     else			{ $suffix="l";           } | 
 |     undef $suffix if ($dst =~ m/^%[xm]/o || $src =~ m/^%[xm]/o); | 
 |  | 
 |     if ($#_==0)				{ &::emit($opcode);		} | 
 |     elsif ($#_==1 && $opcode =~ m/^(call|clflush|j|loop|set)/o) | 
 | 					{ &::emit($opcode,@arg);	} | 
 |     else				{ &::emit($opcode.$suffix,@arg);} | 
 |  | 
 |   1; | 
 | } | 
 | # | 
 | # opcodes not covered by ::generic above, mostly inconsistent namings... | 
 | # | 
 | sub ::movzx	{ &::movzb(@_);			} | 
 | sub ::pushfd	{ &::pushfl;			} | 
 | sub ::popfd	{ &::popfl;			} | 
 | sub ::cpuid	{ &::emit(".byte\t0x0f,0xa2");	} | 
 | sub ::rdtsc	{ &::emit(".byte\t0x0f,0x31");	} | 
 |  | 
 | sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); } | 
 | sub ::call_ptr	{ &::generic("call","*$_[0]");	} | 
 | sub ::jmp_ptr	{ &::generic("jmp","*$_[0]");	} | 
 |  | 
 | *::bswap = sub	{ &::emit("bswap","%$_[0]");	} if (!$::i386); | 
 |  | 
 | sub ::DWP | 
 | { my($addr,$reg1,$reg2,$idx)=@_; | 
 |   my $ret=""; | 
 |  | 
 |     if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; } | 
 |  | 
 |     $addr =~ s/^\s+//; | 
 |     # prepend global references with optional underscore | 
 |     $addr =~ s/^([^\+\-0-9][^\+\-]*)/&::islabel($1) or "$nmdecor$1"/ige; | 
 |  | 
 |     $reg1 = "%$reg1" if ($reg1); | 
 |     $reg2 = "%$reg2" if ($reg2); | 
 |  | 
 |     $ret .= $addr if (($addr ne "") && ($addr ne 0)); | 
 |  | 
 |     if ($reg2) | 
 |     {	$idx!= 0 or $idx=1; | 
 | 	$ret .= "($reg1,$reg2,$idx)"; | 
 |     } | 
 |     elsif ($reg1) | 
 |     {	$ret .= "($reg1)";	} | 
 |  | 
 |   $ret; | 
 | } | 
 | sub ::QWP	{ &::DWP(@_);	} | 
 | sub ::BP	{ &::DWP(@_);	} | 
 | sub ::WP	{ &::DWP(@_);	} | 
 | sub ::BC	{ @_;		} | 
 | sub ::DWC	{ @_;		} | 
 |  | 
 | sub ::file | 
 | {   push(@out,".text\n");	} | 
 |  | 
 | sub ::function_begin_B | 
 | { my $func=shift; | 
 |   my $global=($func !~ /^_/); | 
 |   my $begin="${::lbdecor}_${func}_begin"; | 
 |  | 
 |     &::LABEL($func,$global?"$begin":"$nmdecor$func"); | 
 |     $func=$nmdecor.$func; | 
 |  | 
 |     push(@out,".globl\t$func\n")	if ($global); | 
 |     if ($::macosx) { | 
 |       push(@out,".private_extern\t$func\n"); | 
 |     } else { | 
 |       push(@out,".hidden\t$func\n"); | 
 |     } | 
 |     if ($::coff) | 
 |     {	push(@out,".def\t$func;\t.scl\t".(3-$global).";\t.type\t32;\t.endef\n"); } | 
 |     elsif (($::aout and !$::pic) or $::macosx) | 
 |     { } | 
 |     else | 
 |     {	push(@out,".type	$func,\@function\n"); } | 
 |     push(@out,".align\t$align\n"); | 
 |     push(@out,"$func:\n"); | 
 |     push(@out,"$begin:\n")		if ($global); | 
 |     $::stack=4; | 
 | } | 
 |  | 
 | sub ::function_end_B | 
 | { my $func=shift; | 
 |     push(@out,".size\t$nmdecor$func,.-".&::LABEL($func)."\n") if ($::elf); | 
 |     $::stack=0; | 
 |     &::wipe_labels(); | 
 | } | 
 |  | 
 | sub ::comment | 
 | 	{ | 
 | 	if (!defined($com_start) or $::elf) | 
 | 		{	# Regarding $::elf above... | 
 | 			# GNU and SVR4 as'es use different comment delimiters, | 
 | 		push(@out,"\n");	# so we just skip ELF comments... | 
 | 		return; | 
 | 		} | 
 | 	foreach (@_) | 
 | 		{ | 
 | 		if (/^\s*$/) | 
 | 			{ push(@out,"\n"); } | 
 | 		else | 
 | 			{ push(@out,"\t$com_start $_ $com_end\n"); } | 
 | 		} | 
 | 	} | 
 |  | 
 | sub ::external_label | 
 | {   foreach(@_) { &::LABEL($_,$nmdecor.$_); }   } | 
 |  | 
 | sub ::public_label | 
 | {   push(@out,".globl\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");   } | 
 |  | 
 | sub ::file_end | 
 | {   if ($::macosx) | 
 |     {	if (%non_lazy_ptr) | 
 |     	{   push(@out,".section __IMPORT,__pointers,non_lazy_symbol_pointers\n"); | 
 | 	    foreach $i (keys %non_lazy_ptr) | 
 | 	    {	push(@out,"$non_lazy_ptr{$i}:\n.indirect_symbol\t$i\n.long\t0\n");   } | 
 | 	} | 
 |     } | 
 |     if (0 && grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out) { | 
 | 	my $tmp=".comm\t${nmdecor}OPENSSL_ia32cap_P,16"; | 
 | 	if ($::macosx)	{ push (@out,"$tmp,2\n"); } | 
 | 	elsif ($::elf)	{ push (@out,"$tmp,4\n"); } | 
 | 	else		{ push (@out,"$tmp\n"); } | 
 |     } | 
 |     push(@out,$initseg) if ($initseg); | 
 | } | 
 |  | 
 | sub ::data_byte	{   push(@out,".byte\t".join(',',@_)."\n");   } | 
 | sub ::data_short{   push(@out,".value\t".join(',',@_)."\n");  } | 
 | sub ::data_word {   push(@out,".long\t".join(',',@_)."\n");   } | 
 |  | 
 | sub ::align | 
 | { my $val=$_[0]; | 
 |     if ($::aout) | 
 |     {	$val=int(log($val)/log(2)); | 
 | 	$val.=",0x90"; | 
 |     } | 
 |     push(@out,".align\t$val\n"); | 
 | } | 
 |  | 
 | sub ::picmeup | 
 | { my($dst,$sym,$base,$reflabel)=@_; | 
 |  | 
 |     if (($::pic && ($::elf || $::aout)) || $::macosx) | 
 |     {	if (!defined($base)) | 
 | 	{   &::call(&::label("PIC_me_up")); | 
 | 	    &::set_label("PIC_me_up"); | 
 | 	    &::blindpop($dst); | 
 | 	    $base=$dst; | 
 | 	    $reflabel=&::label("PIC_me_up"); | 
 | 	} | 
 | 	if ($::macosx) | 
 | 	{   my $indirect=&::static_label("$nmdecor$sym\$non_lazy_ptr"); | 
 | 	    &::mov($dst,&::DWP("$indirect-$reflabel",$base)); | 
 | 	    $non_lazy_ptr{"$nmdecor$sym"}=$indirect; | 
 | 	} | 
 | 	elsif ($sym eq "OPENSSL_ia32cap_P" && $::elf>0) | 
 | 	{   &::lea($dst,&::DWP("$sym-$reflabel",$base));   } | 
 | 	else | 
 | 	{   &::lea($dst,&::DWP("_GLOBAL_OFFSET_TABLE_+[.-$reflabel]", | 
 | 			    $base)); | 
 | 	    &::mov($dst,&::DWP("$sym\@GOT",$dst)); | 
 | 	} | 
 |     } | 
 |     else | 
 |     {	&::lea($dst,&::DWP($sym));	} | 
 | } | 
 |  | 
 | sub ::initseg | 
 | { my $f=$nmdecor.shift; | 
 |  | 
 |     if ($::android) | 
 |     {	$initseg.=<<___; | 
 | .section	.init_array | 
 | .align	4 | 
 | .long	$f | 
 | ___ | 
 |     } | 
 |     elsif ($::elf) | 
 |     {	$initseg.=<<___; | 
 | .section	.init | 
 | 	call	$f | 
 | ___ | 
 |     } | 
 |     elsif ($::coff) | 
 |     {   $initseg.=<<___;	# applies to both Cygwin and Mingw | 
 | .section	.ctors | 
 | .long	$f | 
 | ___ | 
 |     } | 
 |     elsif ($::macosx) | 
 |     {	$initseg.=<<___; | 
 | .mod_init_func | 
 | .align 2 | 
 | .long   $f | 
 | ___ | 
 |     } | 
 |     elsif ($::aout) | 
 |     {	my $ctor="${nmdecor}_GLOBAL_\$I\$$f"; | 
 | 	$initseg.=".text\n"; | 
 | 	$initseg.=".type	$ctor,\@function\n" if ($::pic); | 
 | 	$initseg.=<<___;	# OpenBSD way... | 
 | .globl	$ctor | 
 | .align	2 | 
 | $ctor: | 
 | 	jmp	$f | 
 | ___ | 
 |     } | 
 | } | 
 |  | 
 | sub ::dataseg | 
 | {   push(@out,".data\n");   } | 
 |  | 
 | sub ::preprocessor_ifdef | 
 | { my($define)=@_; | 
 |     push(@out,"#ifdef ${define}\n"); | 
 | } | 
 |  | 
 | sub ::preprocessor_endif | 
 | { push(@out,"#endif\n");    } | 
 |  | 
 | *::hidden = sub { push(@out,".hidden\t$nmdecor$_[0]\n"); } if ($::elf); | 
 |  | 
 | 1; |