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>
364 lines
8.6 KiB
Perl
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;
|