| ########################################################################### |
| # Module for ACC tool to create a model of calling conventions |
| # |
| # Copyright (C) 2009-2011 Institute for System Programming, RAS |
| # Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) |
| # Copyright (C) 2011-2012 ROSA Laboratory |
| # Copyright (C) 2012-2015 Andrey Ponomarenko's ABI Laboratory |
| # |
| # Written by Andrey Ponomarenko |
| # |
| # PLATFORMS |
| # ========= |
| # Linux, FreeBSD and Mac OS X |
| # x86 - System V ABI Intel386 Architecture Processor Supplement |
| # x86_64 - System V ABI AMD64 Architecture Processor Supplement |
| # |
| # MS Windows |
| # x86 - MSDN Argument Passing and Naming Conventions |
| # x86_64 - MSDN x64 Software Conventions |
| # |
| # This program is free software: you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License or the GNU Lesser |
| # General Public License as published by the Free Software Foundation. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # and the GNU Lesser General Public License along with this program. |
| # If not, see <http://www.gnu.org/licenses/>. |
| ########################################################################### |
| use strict; |
| |
| my $BYTE = 8; |
| |
| my %UsedReg = (); |
| my %UsedStack = (); |
| |
| my %IntAlgn = ( |
| "x86"=>{ |
| "double"=>4, |
| "long double"=>4 |
| } |
| ); |
| |
| sub classifyType($$$$$) |
| { |
| my ($Tid, $TInfo, $Arch, $System, $Word) = @_; |
| my %Type = get_PureType($Tid, $TInfo); |
| my %Classes = (); |
| if($Type{"Name"} eq "void") |
| { |
| $Classes{0}{"Class"} = "VOID"; |
| return %Classes; |
| } |
| if($System=~/\A(unix|linux|macos|freebsd)\Z/) |
| { # GCC |
| if($Arch eq "x86") |
| { |
| if(isFloat($Type{"Name"})) { |
| $Classes{0}{"Class"} = "FLOAT"; |
| } |
| elsif($Type{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) { |
| $Classes{0}{"Class"} = "INTEGRAL"; |
| } |
| else { # Struct, Class, Union |
| $Classes{0}{"Class"} = "MEMORY"; |
| } |
| } |
| elsif($Arch eq "x86_64") |
| { |
| if($Type{"Type"}=~/Enum|Pointer|Ptr/ |
| or isScalar($Type{"Name"}) |
| or $Type{"Name"}=~/\A(_Bool|bool)\Z/) { |
| $Classes{0}{"Class"} = "INTEGER"; |
| } |
| elsif($Type{"Name"} eq "__int128" |
| or $Type{"Name"} eq "unsigned __int128") |
| { |
| $Classes{0}{"Class"} = "INTEGER"; |
| $Classes{1}{"Class"} = "INTEGER"; |
| } |
| elsif($Type{"Name"}=~/\A(float|double|_Decimal32|_Decimal64|__m64)\Z/) { |
| $Classes{0}{"Class"} = "SSE"; |
| } |
| elsif($Type{"Name"}=~/\A(__float128|_Decimal128|__m128)\Z/) |
| { |
| $Classes{0}{"Class"} = "SSE"; |
| $Classes{8}{"Class"} = "SSEUP"; |
| } |
| elsif($Type{"Name"} eq "__m256") |
| { |
| $Classes{0}{"Class"} = "SSE"; |
| $Classes{24}{"Class"} = "SSEUP"; |
| } |
| elsif($Type{"Name"} eq "long double") |
| { |
| $Classes{0}{"Class"} = "X87"; |
| $Classes{8}{"Class"} = "X87UP"; |
| } |
| elsif($Type{"Name"}=~/\Acomplex (float|double)\Z/) { |
| $Classes{0}{"Class"} = "MEMORY"; |
| } |
| elsif($Type{"Name"} eq "complex long double") { |
| $Classes{0}{"Class"} = "COMPLEX_X87"; |
| } |
| elsif($Type{"Type"}=~/Struct|Class|Union|Array/) |
| { |
| if($Type{"Size"}>4*8) { |
| $Classes{0}{"Class"} = "MEMORY"; |
| } |
| else { |
| %Classes = classifyAggregate($Tid, $TInfo, $Arch, $System, $Word); |
| } |
| } |
| else { |
| $Classes{0}{"Class"} = "MEMORY"; |
| } |
| } |
| elsif($Arch eq "arm") |
| { |
| } |
| } |
| elsif($System eq "windows") |
| { # MS C++ Compiler |
| if($Arch eq "x86") |
| { |
| if(isFloat($Type{"Name"})) { |
| $Classes{0}{"Class"} = "FLOAT"; |
| } |
| elsif($Type{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) { |
| $Classes{0}{"Class"} = "INTEGRAL"; |
| } |
| elsif($Type{"Type"}=~/\A(Struct|Union)\Z/ and $Type{"Size"}<=8) { |
| $Classes{0}{"Class"} = "POD"; |
| } |
| else { # Struct, Class, Union |
| $Classes{0}{"Class"} = "MEMORY"; |
| } |
| } |
| elsif($Arch eq "x86_64") |
| { |
| if($Type{"Name"}=~/\A(float|double|long double)\Z/) { |
| $Classes{0}{"Class"} = "FLOAT"; |
| } |
| elsif($Type{"Name"}=~/\A__m128(|i|d)\Z/) { |
| $Classes{0}{"Class"} = "M128"; |
| } |
| elsif(isScalar($Type{"Name"}) |
| or $Type{"Type"}=~/Enum|Pointer|Ptr/ |
| or $Type{"Name"}=~/\A(_Bool|bool)\Z/ |
| or ($Type{"Type"}=~/\A(Struct|Union)\Z/ and $Type{"Size"}<=8) |
| or $Type{"Name"} eq "__m64") { |
| $Classes{0}{"Class"} = "INTEGRAL"; |
| } |
| else { |
| $Classes{0}{"Class"} = "MEMORY"; |
| } |
| } |
| } |
| return %Classes; |
| } |
| |
| sub classifyAggregate($$$$$) |
| { |
| my ($Tid, $TInfo, $Arch, $System, $Word) = @_; |
| my %Type = get_PureType($Tid, $TInfo); |
| my %Group = (); |
| my $GroupID = 0; |
| my %Classes = (); |
| my %Offsets = (); |
| if($Type{"Type"} eq "Array") |
| { |
| my %Base = get_OneStep_BaseType($Tid, $TInfo); |
| my %BaseType = get_PureType($Base{"Tid"}, $TInfo); |
| my $Pos = 0; |
| my $Max = 0; |
| if(my $BSize = $BaseType{"Size"}) { |
| $Max = ($Type{"Size"}/$BSize) - 1; |
| } |
| foreach my $Pos (0 .. $Max) |
| { |
| # if($TInfo->{1}{"Name"} eq "void") |
| # { # DWARF ABI Dump |
| # $Type{"Memb"}{$Pos}{"offset"} = $Type{"Size"}/($Max+1); |
| # } |
| $Type{"Memb"}{$Pos}{"algn"} = getAlignment_Model($BaseType{"Tid"}, $TInfo, $Arch); |
| $Type{"Memb"}{$Pos}{"type"} = $BaseType{"Tid"}; |
| $Type{"Memb"}{$Pos}{"name"} = "[$Pos]"; |
| } |
| } |
| if($Type{"Type"} eq "Union") |
| { |
| foreach my $Pos (keys(%{$Type{"Memb"}})) |
| { |
| $Offsets{$Pos} = $Pos; |
| $Group{0}{$Pos} = 1; |
| } |
| } |
| else |
| { # Struct, Class |
| foreach my $Pos (keys(%{$Type{"Memb"}})) |
| { |
| my $Offset = getOffset($Pos, \%Type, $TInfo, $Arch, $Word)/$BYTE; |
| $Offsets{$Pos} = $Offset; |
| my $GroupOffset = int($Offset/$Word)*$Word; |
| $Group{$GroupOffset}{$Pos} = 1; |
| } |
| } |
| foreach my $GroupOffset (sort {int($a)<=>int($b)} (keys(%Group))) |
| { |
| my %GroupClasses = (); |
| foreach my $Pos (sort {int($a)<=>int($b)} (keys(%{$Group{$GroupOffset}}))) |
| { # split the field into the classes |
| my $MTid = $Type{"Memb"}{$Pos}{"type"}; |
| my $MName = $Type{"Memb"}{$Pos}{"name"}; |
| my %SubClasses = classifyType($MTid, $TInfo, $Arch, $System, $Word); |
| foreach my $Offset (sort {int($a)<=>int($b)} keys(%SubClasses)) |
| { |
| if(defined $SubClasses{$Offset}{"Elems"}) |
| { |
| foreach (keys(%{$SubClasses{$Offset}{"Elems"}})) { |
| $SubClasses{$Offset}{"Elems"}{$_} = joinFields($MName, $SubClasses{$Offset}{"Elems"}{$_}); |
| } |
| } |
| else { |
| $SubClasses{$Offset}{"Elems"}{0} = $MName; |
| } |
| } |
| |
| # add to the group |
| foreach my $Offset (sort {int($a)<=>int($b)} keys(%SubClasses)) { |
| $GroupClasses{$Offsets{$Pos}+$Offset} = $SubClasses{$Offset}; |
| } |
| } |
| |
| # merge classes in the group |
| my %MergeGroup = (); |
| |
| foreach my $Offset (sort {int($a)<=>int($b)} keys(%GroupClasses)) { |
| $MergeGroup{int($Offset/$Word)}{$Offset} = $GroupClasses{$Offset}; |
| } |
| |
| foreach my $Offset (sort {int($a)<=>int($b)} keys(%MergeGroup)) { |
| while(postMerger($Arch, $System, $MergeGroup{$Offset})) { }; |
| } |
| |
| %GroupClasses = (); |
| foreach my $M_Offset (sort {int($a)<=>int($b)} keys(%MergeGroup)) |
| { |
| foreach my $Offset (sort {int($a)<=>int($b)} keys(%{$MergeGroup{$M_Offset}})) |
| { |
| $GroupClasses{$Offset} = $MergeGroup{$M_Offset}{$Offset}; |
| } |
| } |
| |
| # add to the result list of classes |
| foreach my $Offset (sort {int($a)<=>int($b)} keys(%GroupClasses)) |
| { |
| if($Type{"Type"} eq "Union") |
| { |
| foreach my $P (keys(%{$GroupClasses{$Offset}{"Elems"}})) |
| { |
| if($P!=0) { |
| delete($GroupClasses{$Offset}{"Elems"}{$P}); |
| } |
| } |
| } |
| $Classes{$Offset} = $GroupClasses{$Offset}; |
| } |
| } |
| |
| return %Classes; |
| } |
| |
| sub postMerger($$$) |
| { |
| my ($Arch, $System, $PreClasses) = @_; |
| my @Offsets = sort {int($a)<=>int($b)} keys(%{$PreClasses}); |
| if($#Offsets==0) { |
| return 0; |
| } |
| my %PostClasses = (); |
| my $Num = 0; |
| my $Merged = 0; |
| while($Num<=$#Offsets-1) |
| { |
| my $Offset1 = $Offsets[$Num]; |
| my $Offset2 = $Offsets[$Num+1]; |
| my $Class1 = $PreClasses->{$Offset1}{"Class"}; |
| my $Class2 = $PreClasses->{$Offset2}{"Class"}; |
| my $ResClass = ""; |
| if($System=~/\A(unix|linux|macos|freebsd)\Z/) |
| { # GCC |
| if($Arch eq "x86_64") |
| { |
| if($Class1 eq $Class2) { |
| $ResClass = $Class1; |
| } |
| elsif($Class1 eq "MEMORY" |
| or $Class2 eq "MEMORY") { |
| $ResClass = "MEMORY"; |
| } |
| elsif($Class1 eq "INTEGER" |
| or $Class2 eq "INTEGER") { |
| $ResClass = "INTEGER"; |
| } |
| elsif($Class1=~/X87/ |
| or $Class2=~/X87/) { |
| $ResClass = "MEMORY"; |
| } |
| else { |
| $ResClass = "SSE"; |
| } |
| } |
| } |
| if($ResClass) |
| { # combine |
| $PostClasses{$Offset1}{"Class"} = $ResClass; |
| foreach (keys(%{$PreClasses->{$Offset1}{"Elems"}})) { |
| $PostClasses{$Offset1}{"Elems"}{$Offset1+$_} = $PreClasses->{$Offset1}{"Elems"}{$_}; |
| } |
| foreach (keys(%{$PreClasses->{$Offset2}{"Elems"}})) { |
| $PostClasses{$Offset1}{"Elems"}{$Offset2+$_} = $PreClasses->{$Offset2}{"Elems"}{$_}; |
| } |
| $Merged = 1; |
| } |
| else |
| { # save unchanged |
| $PostClasses{$Offset1} = $PreClasses->{$Offset1}; |
| $PostClasses{$Offset2} = $PreClasses->{$Offset2}; |
| } |
| $Num += 2; |
| } |
| if($Num==$#Offsets) { |
| $PostClasses{$Offsets[$Num]} = $PreClasses->{$Offsets[$Num]}; |
| } |
| %{$PreClasses} = %PostClasses; |
| return $Merged; |
| } |
| |
| sub callingConvention_R_Model($$$$$$) { |
| return callingConvention_R_I_Model(@_, 1); |
| } |
| |
| sub joinFields($$) |
| { |
| my ($F1, $F2) = @_; |
| if(substr($F2, 0, 1) eq "[") |
| { # array elements |
| return $F1.$F2; |
| } |
| else { # fields |
| return $F1.".".$F2; |
| } |
| } |
| |
| sub callingConvention_R_I_Model($$$$$$) |
| { |
| my ($SInfo, $TInfo, $Arch, $System, $Word, $Target) = @_; |
| my %Conv = (); |
| my $RTid = $SInfo->{"Return"}; |
| my %Type = get_PureType($RTid, $TInfo); |
| |
| if($Target) { |
| %UsedReg = (); |
| } |
| |
| my %UsedReg_Copy = %UsedReg; |
| |
| my %Classes = classifyType($RTid, $TInfo, $Arch, $System, $Word); |
| |
| foreach my $Offset (sort {int($a)<=>int($b)} keys(%Classes)) |
| { |
| my $Elems = undef; |
| if(defined $Classes{$Offset}{"Elems"}) |
| { |
| foreach (keys(%{$Classes{$Offset}{"Elems"}})) { |
| $Classes{$Offset}{"Elems"}{$_} = joinFields(".result", $Classes{$Offset}{"Elems"}{$_}); |
| } |
| $Elems = $Classes{$Offset}{"Elems"}; |
| } |
| else { |
| $Elems = { 0 => ".result" }; |
| } |
| |
| my $CName = $Classes{$Offset}{"Class"}; |
| |
| if($CName eq "VOID") { |
| next; |
| } |
| |
| if($System=~/\A(unix|linux|macos|freebsd)\Z/) |
| { # GCC |
| if($Arch eq "x86") |
| { |
| if($CName eq "FLOAT") |
| { # x87 register |
| useRegister("st0", "f", $Elems, $SInfo); |
| } |
| elsif($CName eq "INTEGRAL") |
| { |
| useRegister("eax", "f", $Elems, $SInfo); |
| } |
| elsif($CName eq "MEMORY") { |
| pushStack_R($SInfo, $Word); |
| } |
| } |
| elsif($Arch eq "x86_64") |
| { |
| my @INT = ("rax", "rdx"); |
| my @SSE = ("xmm0", "xmm1"); |
| if($CName eq "INTEGER") |
| { |
| if(my $R = getLastAvailable($SInfo, "f", @INT)) |
| { |
| useRegister($R, "f", $Elems, $SInfo); |
| } |
| else |
| { # revert registers |
| # pass as MEMORY |
| %UsedReg = %UsedReg_Copy; |
| useHidden($SInfo, $Arch, $System, $Word); |
| $Conv{"Hidden"} = 1; |
| last; |
| } |
| } |
| elsif($CName eq "SSE") |
| { |
| if(my $R = getLastAvailable($SInfo, "8l", @SSE)) |
| { |
| useRegister($R, "8l", $Elems, $SInfo); |
| } |
| else |
| { |
| %UsedReg = %UsedReg_Copy; |
| useHidden($SInfo, $Arch, $System, $Word); |
| $Conv{"Hidden"} = 1; |
| last; |
| } |
| } |
| elsif($CName eq "SSEUP") |
| { |
| if(my $R = getLastUsed($SInfo, "xmm0", "xmm1")) |
| { |
| useRegister($R, "8h", $Elems, $SInfo); |
| } |
| else |
| { |
| %UsedReg = %UsedReg_Copy; |
| useHidden($SInfo, $Arch, $System, $Word); |
| $Conv{"Hidden"} = 1; |
| last; |
| } |
| } |
| elsif($CName eq "X87") |
| { |
| useRegister("st0", "8l", $Elems, $SInfo); |
| } |
| elsif($CName eq "X87UP") |
| { |
| useRegister("st0", "8h", $Elems, $SInfo); |
| } |
| elsif($CName eq "COMPLEX_X87") |
| { |
| useRegister("st0", "f", $Elems, $SInfo); |
| useRegister("st1", "f", $Elems, $SInfo); |
| } |
| elsif($CName eq "MEMORY") |
| { |
| useHidden($SInfo, $Arch, $System, $Word); |
| $Conv{"Hidden"} = 1; |
| last; |
| } |
| } |
| elsif($Arch eq "arm") |
| { # TODO |
| } |
| } |
| elsif($System eq "windows") |
| { # MS C++ Compiler |
| if($Arch eq "x86") |
| { |
| if($CName eq "FLOAT") |
| { |
| useRegister("fp0", "f", $Elems, $SInfo); |
| } |
| elsif($CName eq "INTEGRAL") |
| { |
| useRegister("eax", "f", $Elems, $SInfo); |
| } |
| elsif($CName eq "POD") |
| { |
| useRegister("eax", "f", $Elems, $SInfo); |
| useRegister("edx", "f", $Elems, $SInfo); |
| } |
| elsif($CName eq "MEMORY" or $CName eq "M128") |
| { |
| useHidden($SInfo, $Arch, $System, $Word); |
| $Conv{"Hidden"} = 1; |
| } |
| } |
| elsif($Arch eq "x86_64") |
| { |
| if($CName eq "FLOAT" or $CName eq "M128") |
| { |
| useRegister("xmm0", "f", $Elems, $SInfo); |
| } |
| elsif($CName eq "INTEGRAL") |
| { |
| useRegister("eax", "f", $Elems, $SInfo); |
| } |
| elsif($CName eq "MEMORY") |
| { |
| useHidden($SInfo, $Arch, $System, $Word); |
| $Conv{"Hidden"} = 1; |
| } |
| } |
| } |
| } |
| |
| |
| if(my %Regs = usedBy(".result", $SInfo)) |
| { |
| $Conv{"Method"} = "reg"; |
| $Conv{"Registers"} = join(", ", sort(keys(%Regs))); |
| } |
| elsif(my %Regs = usedBy(".result_ptr", $SInfo)) |
| { |
| $Conv{"Method"} = "reg"; |
| $Conv{"Registers"} = join(", ", sort(keys(%Regs))); |
| } |
| |
| if(not $Conv{"Method"}) |
| { # unknown |
| if($Type{"Name"} ne "void") |
| { |
| $Conv{"Method"} = "stack"; |
| $Conv{"Hidden"} = 1; |
| } |
| } |
| |
| return %Conv; |
| } |
| |
| sub usedBy($$) |
| { |
| my ($Name, $SInfo) = @_; |
| my %Regs = (); |
| foreach my $Reg (sort keys(%{$UsedReg{$SInfo}})) |
| { |
| foreach my $Size (sort keys(%{$UsedReg{$SInfo}{$Reg}})) |
| { |
| foreach my $Offset (sort keys(%{$UsedReg{$SInfo}{$Reg}{$Size}})) |
| { |
| if($UsedReg{$SInfo}{$Reg}{$Size}{$Offset}=~/\A\Q$Name\E(\.|\Z)/) { |
| $Regs{$Reg} = 1; |
| } |
| } |
| } |
| } |
| return %Regs; |
| } |
| |
| sub useHidden($$$$) |
| { |
| my ($SInfo, $Arch, $System, $Word) = @_; |
| if($System=~/\A(unix|linux|macos|freebsd)\Z/) |
| { # GCC |
| if($Arch eq "x86") { |
| pushStack_R($SInfo, $Word); |
| } |
| elsif($Arch eq "x86_64") |
| { |
| my $Elems = { 0 => ".result_ptr" }; |
| useRegister("rdi", "f", $Elems, $SInfo); |
| } |
| } |
| elsif($System eq "windows") |
| { # MS C++ Compiler |
| if($Arch eq "x86") { |
| pushStack_R($SInfo, $Word); |
| } |
| elsif($Arch eq "x86_64") |
| { |
| my $Elems = { 0 => ".result_ptr" }; |
| useRegister("rcx", "f", $Elems, $SInfo); |
| } |
| } |
| } |
| |
| sub pushStack_P($$$$) |
| { |
| my ($SInfo, $Pos, $TInfo, $StackAlgn) = @_; |
| my $PTid = $SInfo->{"Param"}{$Pos}{"type"}; |
| my $PName = $SInfo->{"Param"}{$Pos}{"name"}; |
| |
| if(my $Offset = $SInfo->{"Param"}{$Pos}{"offset"}) |
| { # DWARF ABI Dump |
| return pushStack_Offset($SInfo, $Offset, $TInfo->{$PTid}{"Size"}, { 0 => $PName }); |
| } |
| else |
| { |
| my $Alignment = $SInfo->{"Param"}{$Pos}{"algn"}; |
| if($Alignment<$StackAlgn) { |
| $Alignment = $StackAlgn; |
| } |
| return pushStack($SInfo, $Alignment, $TInfo->{$PTid}{"Size"}, { 0 => $PName }); |
| } |
| } |
| |
| sub pushStack_R($$) |
| { |
| my ($SInfo, $Word) = @_; |
| return pushStack($SInfo, $Word, $Word, { 0 => ".result_ptr" }); |
| } |
| |
| sub pushStack_C($$$) |
| { |
| my ($SInfo, $Class, $TInfo) = @_; |
| return pushStack($SInfo, $Class->{"Algn"}, $Class->{"Size"}, $Class->{"Elems"}); |
| } |
| |
| sub pushStack($$$$) |
| { |
| my ($SInfo, $Algn, $Size, $Elem) = @_; |
| my $Offset = 0; |
| if(my @Offsets = sort {int($a)<=>int($b)} keys(%{$UsedStack{$SInfo}})) |
| { |
| $Offset = $Offsets[$#Offsets]; |
| $Offset += $UsedStack{$SInfo}{$Offset}{"Size"}; |
| $Offset += getPadding($Offset, $Algn); |
| } |
| return pushStack_Offset($SInfo, $Offset, $Size, $Elem); |
| } |
| |
| sub pushStack_Offset($$$$) |
| { |
| my ($SInfo, $Offset, $Size, $Elem) = @_; |
| my %Info = ( |
| "Size" => $Size, |
| "Elem" => $Elem |
| ); |
| $UsedStack{$SInfo}{$Offset} = \%Info; |
| return $Offset; |
| } |
| |
| sub useRegister($$$$) |
| { |
| my ($R, $Offset, $Elems, $SInfo) = @_; |
| if(defined $UsedReg{$SInfo}{$R}) |
| { |
| if(defined $UsedReg{$SInfo}{$R}{$Offset}) |
| { # busy |
| return 0; |
| } |
| } |
| $UsedReg{$SInfo}{$R}{$Offset}=$Elems; |
| return $R; |
| } |
| |
| sub getLastAvailable(@) |
| { |
| my $SInfo = shift(@_); |
| my $Offset = shift(@_); |
| my $Pos = 0; |
| foreach (@_) |
| { |
| if(not defined $UsedReg{$SInfo}{$_}) { |
| return $_; |
| } |
| elsif(not defined $UsedReg{$SInfo}{$_}{$Offset}) { |
| return $_; |
| } |
| } |
| return undef; |
| } |
| |
| sub getLastUsed(@) |
| { |
| my $SInfo = shift(@_); |
| my $Pos = 0; |
| foreach (@_) |
| { |
| if(not defined $UsedReg{$SInfo}{$_}) |
| { |
| if($Pos>0) { |
| return @_[$Pos-1]; |
| } |
| else { |
| return @_[0]; |
| } |
| } |
| $Pos+=1; |
| } |
| return undef; |
| } |
| |
| sub callingConvention_P_Model($$$$$$) { |
| return callingConvention_P_I_Model(@_, 1); |
| } |
| |
| sub callingConvention_P_I_Model($$$$$$$) |
| { # calling conventions for different compilers and operating systems |
| my ($SInfo, $Pos, $TInfo, $Arch, $System, $Word, $Target) = @_; |
| my %Conv = (); |
| my $ParamTypeId = $SInfo->{"Param"}{$Pos}{"type"}; |
| my $PName = $SInfo->{"Param"}{$Pos}{"name"}; |
| my %Type = get_PureType($ParamTypeId, $TInfo); |
| |
| if($Target) |
| { |
| %UsedReg = (); |
| |
| # distribute return value |
| if(my $RTid = $SInfo->{"Return"}) { |
| callingConvention_R_I_Model($SInfo, $TInfo, $Arch, $System, $Word, 0); |
| } |
| # distribute other parameters |
| if($Pos>0) |
| { |
| my %PConv = (); |
| my $PPos = 0; |
| while($PConv{"Next"} ne $Pos) |
| { |
| %PConv = callingConvention_P_I_Model($SInfo, $PPos++, $TInfo, $Arch, $System, $Word, 0); |
| if(not $PConv{"Next"}) { |
| last; |
| } |
| } |
| } |
| } |
| |
| my %UsedReg_Copy = %UsedReg; |
| |
| my %Classes = classifyType($ParamTypeId, $TInfo, $Arch, $System, $Word); |
| |
| my $Error = 0; |
| foreach my $Offset (sort {int($a)<=>int($b)} keys(%Classes)) |
| { |
| my $Elems = undef; |
| if(defined $Classes{$Offset}{"Elems"}) |
| { |
| foreach (keys(%{$Classes{$Offset}{"Elems"}})) { |
| $Classes{$Offset}{"Elems"}{$_} = joinFields($PName, $Classes{$Offset}{"Elems"}{$_}); |
| } |
| $Elems = $Classes{$Offset}{"Elems"}; |
| } |
| else { |
| $Elems = { 0 => $PName }; |
| } |
| |
| my $CName = $Classes{$Offset}{"Class"}; |
| |
| if($CName eq "VOID") { |
| next; |
| } |
| |
| if($System=~/\A(unix|linux|macos|freebsd)\Z/) |
| { # GCC |
| if($Arch eq "x86") |
| { |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| elsif($Arch eq "x86_64") |
| { |
| my @INT = ("rdi", "rsi", "rdx", "rcx", "r8", "r9"); |
| my @SSE = ("xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"); |
| |
| if($CName eq "INTEGER") |
| { |
| if(my $R = getLastAvailable($SInfo, "f", @INT)) { |
| useRegister($R, "f", $Elems, $SInfo); |
| } |
| else |
| { # revert registers and |
| # push the argument on the stack |
| %UsedReg = %UsedReg_Copy; |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| elsif($CName eq "SSE") |
| { |
| if(my $R = getLastAvailable($SInfo, "8l", @SSE)) { |
| useRegister($R, "8l", $Elems, $SInfo); |
| } |
| else |
| { |
| %UsedReg = %UsedReg_Copy; |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| elsif($CName eq "SSEUP") |
| { |
| if(my $R = getLastUsed($SInfo, @SSE)) { |
| useRegister($R, "8h", $Elems, $SInfo); |
| } |
| else |
| { |
| %UsedReg = %UsedReg_Copy; |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| elsif($CName=~/X87|MEMORY/) |
| { # MEMORY, X87, X87UP, COMPLEX_X87 |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| else |
| { |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| elsif($Arch eq "arm") |
| { # Procedure Call Standard for the ARM Architecture |
| # TODO |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| else |
| { # TODO |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| elsif($System eq "windows") |
| { # MS C++ Compiler |
| if($Arch eq "x86") |
| { |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| elsif($Arch eq "x86_64") |
| { |
| if($Pos<=3) |
| { |
| if($CName eq "FLOAT") |
| { |
| useRegister("xmm".$Pos, "8l", $Elems, $SInfo); |
| } |
| elsif($CName eq "INTEGRAL") |
| { |
| if($Pos==0) { |
| useRegister("rcx", "f", $Elems, $SInfo); |
| } |
| elsif($Pos==1) { |
| useRegister("rdx", "f", $Elems, $SInfo); |
| } |
| elsif($Pos==2) { |
| useRegister("r8", "f", $Elems, $SInfo); |
| } |
| elsif($Pos==3) { |
| useRegister("r9", "f", $Elems, $SInfo); |
| } |
| else |
| { |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| else |
| { |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| else |
| { |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| } |
| else |
| { # TODO |
| pushStack_P($SInfo, $Pos, $TInfo, $Word); |
| last; |
| } |
| } |
| |
| if(my %Regs = usedBy($PName, $SInfo)) |
| { |
| $Conv{"Method"} = "reg"; |
| $Conv{"Registers"} = join(", ", sort(keys(%Regs))); |
| } |
| else |
| { |
| if($Type{"Name"} ne "void") { |
| $Conv{"Method"} = "stack"; |
| } |
| } |
| |
| if(defined $SInfo->{"Param"}{$Pos+1}) |
| { # TODO |
| $Conv{"Next"} = $Pos+1; |
| } |
| |
| return %Conv; |
| } |
| |
| sub getAlignment_Model($$$) |
| { |
| my ($Tid, $TInfo, $Arch) = @_; |
| |
| if(not $Tid) |
| { # incomplete ABI dump |
| return 0; |
| } |
| |
| if(defined $TInfo->{$Tid}{"Algn"}) { |
| return $TInfo->{$Tid}{"Algn"}; |
| } |
| else |
| { |
| if($TInfo->{$Tid}{"Type"}=~/Struct|Class|Union|MethodPtr/) |
| { |
| if(defined $TInfo->{$Tid}{"Memb"}) |
| { |
| my $Max = 0; |
| foreach my $Pos (keys(%{$TInfo->{$Tid}{"Memb"}})) |
| { |
| my $Algn = $TInfo->{$Tid}{"Memb"}{$Pos}{"algn"}; |
| if(not $Algn) { |
| $Algn = getAlignment_Model($TInfo->{$Tid}{"Memb"}{$Pos}{"type"}, $TInfo, $Arch); |
| } |
| if($Algn>$Max) { |
| $Max = $Algn; |
| } |
| } |
| return $Max; |
| } |
| return 0; |
| } |
| elsif($TInfo->{$Tid}{"Type"} eq "Array") |
| { |
| my %Base = get_OneStep_BaseType($Tid, $TInfo); |
| |
| if($Base{"Tid"} eq $Tid) |
| { # emergency exit |
| return 0; |
| } |
| |
| return getAlignment_Model($Base{"Tid"}, $TInfo, $Arch); |
| } |
| elsif($TInfo->{$Tid}{"Type"}=~/Intrinsic|Enum|Pointer|FuncPtr/) |
| { # model |
| return getInt_Algn($Tid, $TInfo, $Arch); |
| } |
| else |
| { |
| my %PureType = get_PureType($Tid, $TInfo); |
| |
| if($PureType{"Tid"} eq $Tid) |
| { # emergency exit |
| return 0; |
| } |
| |
| return getAlignment_Model($PureType{"Tid"}, $TInfo, $Arch); |
| } |
| } |
| } |
| |
| sub getInt_Algn($$$) |
| { |
| my ($Tid, $TInfo, $Arch) = @_; |
| my $Name = $TInfo->{$Tid}{"Name"}; |
| if(my $Algn = $IntAlgn{$Arch}{$Name}) { |
| return $Algn; |
| } |
| else |
| { |
| my $Size = $TInfo->{$Tid}{"Size"}; |
| if($Arch eq "x86_64") |
| { # x86_64: sizeof==alignment |
| return $Size; |
| } |
| elsif($Arch eq "arm") |
| { |
| if($Size>8) |
| { # 128-bit vector (16) |
| return 8; |
| } |
| return $Size; |
| } |
| elsif($Arch eq "x86") |
| { |
| if($Size>4) |
| { # "double" (8) and "long double" (12) |
| return 4; |
| } |
| return $Size; |
| } |
| return $Size; |
| } |
| } |
| |
| sub getAlignment($$$$$) |
| { |
| my ($Pos, $TypePtr, $TInfo, $Arch, $Word) = @_; |
| my $Tid = $TypePtr->{"Memb"}{$Pos}{"type"}; |
| my %Type = get_PureType($Tid, $TInfo); |
| my $Computed = $TypePtr->{"Memb"}{$Pos}{"algn"}; |
| my $Alignment = 0; |
| |
| if(my $BSize = $TypePtr->{"Memb"}{$Pos}{"bitfield"}) |
| { # bitfields |
| if($Computed) |
| { # real in bits |
| $Alignment = $Computed; |
| } |
| else |
| { # model |
| if($BSize eq $Type{"Size"}*$BYTE) |
| { |
| $Alignment = $BSize; |
| } |
| else { |
| $Alignment = 1; |
| } |
| } |
| return ($Alignment, $BSize); |
| } |
| else |
| { # other fields |
| if($Computed) |
| { # real in bytes |
| $Alignment = $Computed*$BYTE; |
| } |
| else |
| { # model |
| $Alignment = getAlignment_Model($Tid, $TInfo, $Arch)*$BYTE; |
| } |
| return ($Alignment, $Type{"Size"}*$BYTE); |
| } |
| } |
| |
| sub getOffset($$$$$) |
| { # offset of the field including padding |
| my ($FieldPos, $TypePtr, $TInfo, $Arch, $Word) = @_; |
| |
| if($TypePtr->{"Type"} eq "Union") { |
| return 0; |
| } |
| |
| # if((my $Off = $TypePtr->{"Memb"}{$FieldPos}{"offset"}) ne "") |
| # { # DWARF ABI Dump (generated by the ABI Dumper tool) |
| # return $Off*$BYTE; |
| # } |
| |
| my $Offset = 0; |
| my $Buffer=0; |
| |
| foreach my $Pos (0 .. keys(%{$TypePtr->{"Memb"}})-1) |
| { |
| my ($Alignment, $MSize) = getAlignment($Pos, $TypePtr, $TInfo, $Arch, $Word); |
| |
| if(not $Alignment) |
| { # support for old ABI dumps |
| if($MSize=~/\A(8|16|32|64)\Z/) |
| { |
| if($Buffer+$MSize<$Word*$BYTE) |
| { |
| $Alignment = 1; |
| $Buffer += $MSize; |
| } |
| else |
| { |
| $Alignment = $MSize; |
| $Buffer = 0; |
| } |
| } |
| else |
| { |
| $Alignment = 1; |
| $Buffer += $MSize; |
| } |
| } |
| |
| # padding |
| $Offset += getPadding($Offset, $Alignment); |
| if($Pos==$FieldPos) |
| { # after the padding |
| # before the field |
| return $Offset; |
| } |
| $Offset += $MSize; |
| } |
| return $FieldPos; # if something is going wrong |
| } |
| |
| sub getPadding($$) |
| { |
| my ($Offset, $Alignment) = @_; |
| my $Padding = 0; |
| if($Offset % $Alignment!=0) |
| { # not aligned, add padding |
| $Padding = $Alignment - $Offset % $Alignment; |
| } |
| return $Padding; |
| } |
| |
| sub isMemPadded($$$$$$) |
| { # check if the target field can be added/removed/changed |
| # without shifting other fields because of padding bits |
| my ($FieldPos, $Size, $TypePtr, $Skip, $TInfo, $Arch, $Word) = @_; |
| return 0 if($FieldPos==0); |
| delete($TypePtr->{"Memb"}{""}); |
| my $Offset = 0; |
| my (%Alignment, %MSize) = (); |
| my $MaxAlgn = 0; |
| my $End = keys(%{$TypePtr->{"Memb"}})-1; |
| my $NextField = $FieldPos+1; |
| foreach my $Pos (0 .. $End) |
| { |
| if($Skip and $Skip->{$Pos}) |
| { # skip removed/added fields |
| if($Pos > $FieldPos) |
| { # after the target |
| $NextField += 1; |
| next; |
| } |
| } |
| ($Alignment{$Pos}, $MSize{$Pos}) = getAlignment($Pos, $TypePtr, $TInfo, $Arch, $Word); |
| |
| if(not $Alignment{$Pos}) |
| { # emergency exit |
| return 0; |
| } |
| |
| if($Alignment{$Pos}>$MaxAlgn) { |
| $MaxAlgn = $Alignment{$Pos}; |
| } |
| if($Pos==$FieldPos) |
| { |
| if($Size==-1) |
| { # added/removed fields |
| if($Pos!=$End) |
| { # skip target field and see |
| # if enough padding will be |
| # created on the next step |
| # to include this field |
| next; |
| } |
| } |
| } |
| # padding |
| my $Padding = 0; |
| if($Offset % $Alignment{$Pos}!=0) |
| { # not aligned, add padding |
| $Padding = $Alignment{$Pos} - $Offset % $Alignment{$Pos}; |
| } |
| if($Pos==$NextField) |
| { # try to place target field in the padding |
| if($Size==-1) |
| { # added/removed fields |
| my $TPadding = 0; |
| if($Offset % $Alignment{$FieldPos}!=0) |
| {# padding of the target field |
| $TPadding = $Alignment{$FieldPos} - $Offset % $Alignment{$FieldPos}; |
| } |
| if($TPadding+$MSize{$FieldPos}<=$Padding) |
| { # enough padding to place target field |
| return 1; |
| } |
| else { |
| return 0; |
| } |
| } |
| else |
| { # changed fields |
| my $Delta = $Size-$MSize{$FieldPos}; |
| if($Delta>=0) |
| { # increased |
| if($Size-$MSize{$FieldPos}<=$Padding) |
| { # enough padding to change target field |
| return 1; |
| } |
| else { |
| return 0; |
| } |
| } |
| else |
| { # decreased |
| $Delta = abs($Delta); |
| if($Delta+$Padding>=$MSize{$Pos}) |
| { # try to place the next field |
| if(($Offset-$Delta) % $Alignment{$Pos} != 0) |
| { # padding of the next field in new place |
| my $NPadding = $Alignment{$Pos} - ($Offset-$Delta) % $Alignment{$Pos}; |
| if($NPadding+$MSize{$Pos}<=$Delta+$Padding) |
| { # enough delta+padding to store next field |
| return 0; |
| } |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| return 1; |
| } |
| } |
| } |
| elsif($Pos==$End) |
| { # target field is the last field |
| if($Size==-1) |
| { # added/removed fields |
| if($Offset % $MaxAlgn!=0) |
| { # tail padding |
| my $TailPadding = $MaxAlgn - $Offset % $MaxAlgn; |
| if($Padding+$MSize{$Pos}<=$TailPadding) |
| { # enough tail padding to place the last field |
| return 1; |
| } |
| } |
| return 0; |
| } |
| else |
| { # changed fields |
| # scenario #1 |
| my $Offset1 = $Offset+$Padding+$MSize{$Pos}; |
| if($Offset1 % $MaxAlgn != 0) |
| { # tail padding |
| $Offset1 += $MaxAlgn - $Offset1 % $MaxAlgn; |
| } |
| # scenario #2 |
| my $Offset2 = $Offset+$Padding+$Size; |
| if($Offset2 % $MaxAlgn != 0) |
| { # tail padding |
| $Offset2 += $MaxAlgn - $Offset2 % $MaxAlgn; |
| } |
| if($Offset1!=$Offset2) |
| { # different sizes of structure |
| return 0; |
| } |
| return 1; |
| } |
| } |
| $Offset += $Padding+$MSize{$Pos}; |
| } |
| return 0; |
| } |
| |
| sub isScalar($) { |
| return ($_[0]=~/\A(unsigned |)(char|short|int|long|long long)\Z/); |
| } |
| |
| sub isFloat($) { |
| return ($_[0]=~/\A(float|double|long double)\Z/); |
| } |
| |
| sub callingConvention_R_Real($) |
| { |
| my $SInfo = $_[0]; |
| my %Conv = (); |
| my %Regs = (); |
| my $Hidden = 0; |
| foreach my $Elem (keys(%{$SInfo->{"Reg"}})) |
| { |
| my $Reg = $SInfo->{"Reg"}{$Elem}; |
| if($Elem eq ".result_ptr") |
| { |
| $Hidden = 1; |
| $Regs{$Reg} = 1; |
| } |
| elsif(index($Elem, ".result")==0) { |
| $Regs{$Reg} = 1; |
| } |
| } |
| if(my @R = sort keys(%Regs)) |
| { |
| $Conv{"Method"} = "reg"; |
| $Conv{"Registers"} = join(", ", @R); |
| if($Hidden) { |
| $Conv{"Hidden"} = 1; |
| } |
| } |
| else |
| { |
| $Conv{"Method"} = "stack"; |
| $Conv{"Hidden"} = 1; |
| } |
| return %Conv; |
| } |
| |
| sub callingConvention_P_Real($$) |
| { |
| my ($SInfo, $Pos) = @_; |
| my %Conv = (); |
| my %Regs = (); |
| foreach my $Elem (keys(%{$SInfo->{"Reg"}})) |
| { |
| my $Reg = $SInfo->{"Reg"}{$Elem}; |
| if($Elem=~/\A$Pos([\.\+]|\Z)/) { |
| $Regs{$Reg} = 1; |
| } |
| } |
| if(my @R = sort keys(%Regs)) |
| { |
| $Conv{"Method"} = "reg"; |
| $Conv{"Registers"} = join(", ", @R); |
| } |
| else |
| { |
| $Conv{"Method"} = "stack"; |
| |
| if(defined $SInfo->{"Param"} |
| and defined $SInfo->{"Param"}{0}) |
| { |
| if(not defined $SInfo->{"Param"}{0}{"offset"}) |
| { |
| $Conv{"Method"} = "unknown"; |
| } |
| } |
| } |
| |
| return %Conv; |
| } |
| |
| return 1; |