ring/crypto/perlasm/x86asm.pl
David Benjamin a905bbb52a Consistently include BTI markers in every assembly file
Trying to migrate Chromium to the "link all the asm files together"
strategy broke the aarch64 Android build because some of the ifdef'd out
assembly files were missing the .note.gnu.property section for BTI. If
we add support for IBT, that'll be another one.

To fix this, introduce <openssl/asm_base.h>, which must be included at
the start of every assembly file (before the target ifdefs). This does a
couple things:

- It emits BTI and noexecstack markers into every assembly file, even
  those that ifdef themselves out.

- It resolves the MSan -> OPENSSL_NO_ASM logic, so we only need to do it
  once.

- It defines the same OPENSSL_X86_64, etc., defines we set elsewhere, so
  we can ensure they're consistent.

This required carving files up a bit. <openssl/base.h> has a lot of
things, such that trying to guard everything in it on __ASSEMBLER__
would be tedious. Instead, I moved the target defines to a new
<openssl/target.h>. Then <openssl/asm_base.h> is the new header that
pulls in all those things.

Bug: 542
Change-Id: I1682b4d929adea72908655fa1bb15765a6b3473b
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60765
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
2023-06-22 23:36:55 +00:00

364 lines
8.6 KiB
Perl

#! /usr/bin/env perl
# Copyright 1995-2018 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
# require 'x86asm.pl';
# &asm_init(<flavor>[,$i386only]);
# &function_begin("foo");
# ...
# &function_end("foo");
# &asm_finish
$out=();
$i386=0;
# AUTOLOAD is this context has quite unpleasant side effect, namely
# that typos in function calls effectively go to assembler output,
# but on the pros side we don't have to implement one subroutine per
# each opcode...
sub ::AUTOLOAD
{ my $opcode = $AUTOLOAD;
die "more than 4 arguments passed to $opcode" if ($#_>3);
$opcode =~ s/.*:://;
if ($opcode =~ /^push/) { $stack+=4; }
elsif ($opcode =~ /^pop/) { $stack-=4; }
&generic($opcode,@_) or die "undefined subroutine \&$AUTOLOAD";
}
# record_function_hit(int) writes a byte with value one to the given offset of
# |BORINGSSL_function_hit|, but only if BORINGSSL_DISPATCH_TEST is defined.
# This is used in impl_dispatch_test.cc to test whether the expected assembly
# functions are triggered by high-level API calls.
sub ::record_function_hit
{ my($index)=@_;
&preprocessor_ifdef("BORINGSSL_DISPATCH_TEST");
&push("ebx");
&push("edx");
&call(&label("pic"));
&set_label("pic");
&blindpop("ebx");
&lea("ebx",&DWP("BORINGSSL_function_hit+$index"."-".&label("pic"),"ebx"));
&mov("edx", 1);
&movb(&BP(0, "ebx"), "dl");
&pop("edx");
&pop("ebx");
&preprocessor_endif();
}
sub ::emit
{ my $opcode=shift;
if ($#_==-1) { push(@out,"\t$opcode\n"); }
else { push(@out,"\t$opcode\t".join(',',@_)."\n"); }
}
sub ::LB
{ $_[0] =~ m/^e?([a-d])x$/o or die "$_[0] does not have a 'low byte'";
$1."l";
}
sub ::HB
{ $_[0] =~ m/^e?([a-d])x$/o or die "$_[0] does not have a 'high byte'";
$1."h";
}
sub ::stack_push{ my $num=$_[0]*4; $stack+=$num; &sub("esp",$num); }
sub ::stack_pop { my $num=$_[0]*4; $stack-=$num; &add("esp",$num); }
sub ::blindpop { &pop($_[0]); $stack+=4; }
sub ::wparam { &DWP($stack+4*$_[0],"esp"); }
sub ::swtmp { &DWP(4*$_[0],"esp"); }
sub ::bswap
{ if ($i386) # emulate bswap for i386
{ &comment("bswap @_");
&xchg(&HB(@_),&LB(@_));
&ror (@_,16);
&xchg(&HB(@_),&LB(@_));
}
else
{ &generic("bswap",@_); }
}
# These are made-up opcodes introduced over the years essentially
# by ignorance, just alias them to real ones...
sub ::movb { &mov(@_); }
sub ::xorb { &xor(@_); }
sub ::rotl { &rol(@_); }
sub ::rotr { &ror(@_); }
sub ::exch { &xchg(@_); }
sub ::halt { &hlt; }
sub ::movz { &movzx(@_); }
sub ::pushf { &pushfd; }
sub ::popf { &popfd; }
# 3 argument instructions
sub ::movq
{ my($p1,$p2,$optimize)=@_;
if ($optimize && $p1=~/^mm[0-7]$/ && $p2=~/^mm[0-7]$/)
# movq between mmx registers can sink Intel CPUs
{ &::pshufw($p1,$p2,0xe4); }
else
{ &::generic("movq",@_); }
}
# SSE>2 instructions
my %regrm = ( "eax"=>0, "ecx"=>1, "edx"=>2, "ebx"=>3,
"esp"=>4, "ebp"=>5, "esi"=>6, "edi"=>7 );
sub ::pextrd
{ my($dst,$src,$imm)=@_;
if ("$dst:$src" =~ /(e[a-dsd][ixp]):xmm([0-7])/)
{ &::data_byte(0x66,0x0f,0x3a,0x16,0xc0|($2<<3)|$regrm{$1},$imm); }
else
{ &::generic("pextrd",@_); }
}
sub ::pinsrd
{ my($dst,$src,$imm)=@_;
if ("$dst:$src" =~ /xmm([0-7]):(e[a-dsd][ixp])/)
{ &::data_byte(0x66,0x0f,0x3a,0x22,0xc0|($1<<3)|$regrm{$2},$imm); }
else
{ &::generic("pinsrd",@_); }
}
sub ::pshufb
{ my($dst,$src)=@_;
if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
{ &data_byte(0x66,0x0f,0x38,0x00,0xc0|($1<<3)|$2); }
else
{ &::generic("pshufb",@_); }
}
sub ::palignr
{ my($dst,$src,$imm)=@_;
if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
{ &::data_byte(0x66,0x0f,0x3a,0x0f,0xc0|($1<<3)|$2,$imm); }
else
{ &::generic("palignr",@_); }
}
sub ::pclmulqdq
{ my($dst,$src,$imm)=@_;
if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
{ &::data_byte(0x66,0x0f,0x3a,0x44,0xc0|($1<<3)|$2,$imm); }
else
{ &::generic("pclmulqdq",@_); }
}
sub ::rdrand
{ my ($dst)=@_;
if ($dst =~ /(e[a-dsd][ixp])/)
{ &::data_byte(0x0f,0xc7,0xf0|$regrm{$dst}); }
else
{ &::generic("rdrand",@_); }
}
sub ::rdseed
{ my ($dst)=@_;
if ($dst =~ /(e[a-dsd][ixp])/)
{ &::data_byte(0x0f,0xc7,0xf8|$regrm{$dst}); }
else
{ &::generic("rdrand",@_); }
}
sub rxb {
local *opcode=shift;
my ($dst,$src1,$src2,$rxb)=@_;
$rxb|=0x7<<5;
$rxb&=~(0x04<<5) if($dst>=8);
$rxb&=~(0x01<<5) if($src1>=8);
$rxb&=~(0x02<<5) if($src2>=8);
push @opcode,$rxb;
}
sub ::vprotd
{ my $args=join(',',@_);
if ($args =~ /xmm([0-7]),xmm([0-7]),([x0-9a-f]+)/)
{ my @opcode=(0x8f);
rxb(\@opcode,$1,$2,-1,0x08);
push @opcode,0x78,0xc2;
push @opcode,0xc0|($2&7)|(($1&7)<<3); # ModR/M
my $c=$3;
push @opcode,$c=~/^0/?oct($c):$c;
&::data_byte(@opcode);
}
else
{ &::generic("vprotd",@_); }
}
sub ::endbranch
{
&::data_byte(0xf3,0x0f,0x1e,0xfb);
}
# label management
$lbdecor="L"; # local label decoration, set by package
$label="000";
sub ::islabel # see is argument is a known label
{ my $i;
foreach $i (values %label) { return $i if ($i eq $_[0]); }
$label{$_[0]}; # can be undef
}
sub ::label # instantiate a function-scope label
{ if (!defined($label{$_[0]}))
{ $label{$_[0]}="${lbdecor}${label}${_[0]}"; $label++; }
$label{$_[0]};
}
sub ::LABEL # instantiate a file-scope label
{ $label{$_[0]}=$_[1] if (!defined($label{$_[0]}));
$label{$_[0]};
}
sub ::static_label { &::LABEL($_[0],$lbdecor.$_[0]); }
sub ::set_label_B { push(@out,"@_:\n"); }
sub ::set_label
{ my $label=&::label($_[0]);
&::align($_[1]) if ($_[1]>1);
&::set_label_B($label);
$label;
}
sub ::wipe_labels # wipes function-scope labels
{ foreach $i (keys %label)
{ delete $label{$i} if ($label{$i} =~ /^\Q${lbdecor}\E[0-9]{3}/); }
}
# subroutine management
sub ::function_begin
{ &function_begin_B(@_);
$stack=4;
&push("ebp");
&push("ebx");
&push("esi");
&push("edi");
}
sub ::function_end
{ &pop("edi");
&pop("esi");
&pop("ebx");
&pop("ebp");
&ret();
&function_end_B(@_);
$stack=0;
&wipe_labels();
}
sub ::function_end_A
{ &pop("edi");
&pop("esi");
&pop("ebx");
&pop("ebp");
&ret();
$stack+=16; # readjust esp as if we didn't pop anything
}
sub ::asciz
{ my @str=unpack("C*",shift);
push @str,0;
while ($#str>15) {
&data_byte(@str[0..15]);
foreach (0..15) { shift @str; }
}
&data_byte(@str) if (@str);
}
sub ::asm_finish
{ &file_end();
my $comment = "//";
$comment = ";" if ($win32);
print <<___;
$comment This file is generated from a similarly-named Perl script in the BoringSSL
$comment source tree. Do not edit by hand.
___
if ($win32) {
print <<___ unless $masm;
\%ifdef BORINGSSL_PREFIX
\%include "boringssl_prefix_symbols_nasm.inc"
\%endif
\%ifidn __OUTPUT_FORMAT__, win32
___
print @out;
print <<___ unless $masm;
\%else
; Work around https://bugzilla.nasm.us/show_bug.cgi?id=3392738
ret
\%endif
___
} else {
my $target;
if ($elf) {
$target = "defined(__ELF__)";
} elsif ($macosx) {
$target = "defined(__APPLE__)";
} else {
die "unknown target";
}
print <<___;
#include <openssl/asm_base.h>
#if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86) && $target
___
print @out;
print <<___;
#endif // !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86) && $target
___
}
}
sub ::asm_init
{ my ($type,$cpu)=@_;
$i386=$cpu;
$elf=$cpp=$coff=$aout=$macosx=$win32=$mwerks=$android=0;
if (($type eq "elf"))
{ $elf=1; require "x86gas.pl"; }
elsif (($type eq "elf-1"))
{ $elf=-1; require "x86gas.pl"; }
elsif (($type eq "a\.out"))
{ $aout=1; require "x86gas.pl"; }
elsif (($type eq "coff" or $type eq "gaswin"))
{ $coff=1; require "x86gas.pl"; }
elsif (($type eq "win32n"))
{ $win32=1; require "x86nasm.pl"; }
elsif (($type eq "win32"))
{ $win32=1; $masm=1; require "x86masm.pl"; }
elsif (($type eq "macosx"))
{ $aout=1; $macosx=1; require "x86gas.pl"; }
elsif (($type eq "android"))
{ $elf=1; $android=1; require "x86gas.pl"; }
else
{ print STDERR <<"EOF";
Pick one target type from
elf - Linux, FreeBSD, Solaris x86, etc.
a.out - DJGPP, elder OpenBSD, etc.
coff - GAS/COFF such as Win32 targets
win32n - Windows 95/Windows NT NASM format
macosx - Mac OS X
EOF
exit(1);
}
$pic=0;
for (@ARGV) { $pic=1 if (/\-[fK]PIC/i); }
&file();
}
sub ::hidden {}
1;