1 ########################################################################### 2 # Module for ACC tool to create a model of calling conventions 3 # 4 # Copyright (C) 2009-2011 Institute for System Programming, RAS 5 # Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) 6 # Copyright (C) 2011-2012 ROSA Laboratory 7 # Copyright (C) 2012-2015 Andrey Ponomarenko's ABI Laboratory 8 # 9 # Written by Andrey Ponomarenko 10 # 11 # PLATFORMS 12 # ========= 13 # Linux, FreeBSD and Mac OS X 14 # x86 - System V ABI Intel386 Architecture Processor Supplement 15 # x86_64 - System V ABI AMD64 Architecture Processor Supplement 16 # 17 # MS Windows 18 # x86 - MSDN Argument Passing and Naming Conventions 19 # x86_64 - MSDN x64 Software Conventions 20 # 21 # This program is free software: you can redistribute it and/or modify 22 # it under the terms of the GNU General Public License or the GNU Lesser 23 # General Public License as published by the Free Software Foundation. 24 # 25 # This program is distributed in the hope that it will be useful, 26 # but WITHOUT ANY WARRANTY; without even the implied warranty of 27 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 # GNU General Public License for more details. 29 # 30 # You should have received a copy of the GNU General Public License 31 # and the GNU Lesser General Public License along with this program. 32 # If not, see <http://www.gnu.org/licenses/>. 33 ########################################################################### 34 use strict; 35 36 my $BYTE = 8; 37 38 my %UsedReg = (); 39 my %UsedStack = (); 40 41 my %IntAlgn = ( 42 "x86"=>{ 43 "double"=>4, 44 "long double"=>4 45 } 46 ); 47 48 sub classifyType($$$$$) 49 { 50 my ($Tid, $TInfo, $Arch, $System, $Word) = @_; 51 my %Type = get_PureType($Tid, $TInfo); 52 my %Classes = (); 53 if($Type{"Name"} eq "void") 54 { 55 $Classes{0}{"Class"} = "VOID"; 56 return %Classes; 57 } 58 if($System=~/\A(unix|linux|macos|freebsd)\Z/) 59 { # GCC 60 if($Arch eq "x86") 61 { 62 if(isFloat($Type{"Name"})) { 63 $Classes{0}{"Class"} = "FLOAT"; 64 } 65 elsif($Type{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) { 66 $Classes{0}{"Class"} = "INTEGRAL"; 67 } 68 else { # Struct, Class, Union 69 $Classes{0}{"Class"} = "MEMORY"; 70 } 71 } 72 elsif($Arch eq "x86_64") 73 { 74 if($Type{"Type"}=~/Enum|Pointer|Ptr/ 75 or isScalar($Type{"Name"}) 76 or $Type{"Name"}=~/\A(_Bool|bool)\Z/) { 77 $Classes{0}{"Class"} = "INTEGER"; 78 } 79 elsif($Type{"Name"} eq "__int128" 80 or $Type{"Name"} eq "unsigned __int128") 81 { 82 $Classes{0}{"Class"} = "INTEGER"; 83 $Classes{1}{"Class"} = "INTEGER"; 84 } 85 elsif($Type{"Name"}=~/\A(float|double|_Decimal32|_Decimal64|__m64)\Z/) { 86 $Classes{0}{"Class"} = "SSE"; 87 } 88 elsif($Type{"Name"}=~/\A(__float128|_Decimal128|__m128)\Z/) 89 { 90 $Classes{0}{"Class"} = "SSE"; 91 $Classes{8}{"Class"} = "SSEUP"; 92 } 93 elsif($Type{"Name"} eq "__m256") 94 { 95 $Classes{0}{"Class"} = "SSE"; 96 $Classes{24}{"Class"} = "SSEUP"; 97 } 98 elsif($Type{"Name"} eq "long double") 99 { 100 $Classes{0}{"Class"} = "X87"; 101 $Classes{8}{"Class"} = "X87UP"; 102 } 103 elsif($Type{"Name"}=~/\Acomplex (float|double)\Z/) { 104 $Classes{0}{"Class"} = "MEMORY"; 105 } 106 elsif($Type{"Name"} eq "complex long double") { 107 $Classes{0}{"Class"} = "COMPLEX_X87"; 108 } 109 elsif($Type{"Type"}=~/Struct|Class|Union|Array/) 110 { 111 if($Type{"Size"}>4*8) { 112 $Classes{0}{"Class"} = "MEMORY"; 113 } 114 else { 115 %Classes = classifyAggregate($Tid, $TInfo, $Arch, $System, $Word); 116 } 117 } 118 else { 119 $Classes{0}{"Class"} = "MEMORY"; 120 } 121 } 122 elsif($Arch eq "arm") 123 { 124 } 125 } 126 elsif($System eq "windows") 127 { # MS C++ Compiler 128 if($Arch eq "x86") 129 { 130 if(isFloat($Type{"Name"})) { 131 $Classes{0}{"Class"} = "FLOAT"; 132 } 133 elsif($Type{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) { 134 $Classes{0}{"Class"} = "INTEGRAL"; 135 } 136 elsif($Type{"Type"}=~/\A(Struct|Union)\Z/ and $Type{"Size"}<=8) { 137 $Classes{0}{"Class"} = "POD"; 138 } 139 else { # Struct, Class, Union 140 $Classes{0}{"Class"} = "MEMORY"; 141 } 142 } 143 elsif($Arch eq "x86_64") 144 { 145 if($Type{"Name"}=~/\A(float|double|long double)\Z/) { 146 $Classes{0}{"Class"} = "FLOAT"; 147 } 148 elsif($Type{"Name"}=~/\A__m128(|i|d)\Z/) { 149 $Classes{0}{"Class"} = "M128"; 150 } 151 elsif(isScalar($Type{"Name"}) 152 or $Type{"Type"}=~/Enum|Pointer|Ptr/ 153 or $Type{"Name"}=~/\A(_Bool|bool)\Z/ 154 or ($Type{"Type"}=~/\A(Struct|Union)\Z/ and $Type{"Size"}<=8) 155 or $Type{"Name"} eq "__m64") { 156 $Classes{0}{"Class"} = "INTEGRAL"; 157 } 158 else { 159 $Classes{0}{"Class"} = "MEMORY"; 160 } 161 } 162 } 163 return %Classes; 164 } 165 166 sub classifyAggregate($$$$$) 167 { 168 my ($Tid, $TInfo, $Arch, $System, $Word) = @_; 169 my %Type = get_PureType($Tid, $TInfo); 170 my %Group = (); 171 my $GroupID = 0; 172 my %Classes = (); 173 my %Offsets = (); 174 if($Type{"Type"} eq "Array") 175 { 176 my %Base = get_OneStep_BaseType($Tid, $TInfo); 177 my %BaseType = get_PureType($Base{"Tid"}, $TInfo); 178 my $Pos = 0; 179 my $Max = 0; 180 if(my $BSize = $BaseType{"Size"}) { 181 $Max = ($Type{"Size"}/$BSize) - 1; 182 } 183 foreach my $Pos (0 .. $Max) 184 { 185 # if($TInfo->{1}{"Name"} eq "void") 186 # { # DWARF ABI Dump 187 # $Type{"Memb"}{$Pos}{"offset"} = $Type{"Size"}/($Max+1); 188 # } 189 $Type{"Memb"}{$Pos}{"algn"} = getAlignment_Model($BaseType{"Tid"}, $TInfo, $Arch); 190 $Type{"Memb"}{$Pos}{"type"} = $BaseType{"Tid"}; 191 $Type{"Memb"}{$Pos}{"name"} = "[$Pos]"; 192 } 193 } 194 if($Type{"Type"} eq "Union") 195 { 196 foreach my $Pos (keys(%{$Type{"Memb"}})) 197 { 198 $Offsets{$Pos} = $Pos; 199 $Group{0}{$Pos} = 1; 200 } 201 } 202 else 203 { # Struct, Class 204 foreach my $Pos (keys(%{$Type{"Memb"}})) 205 { 206 my $Offset = getOffset($Pos, \%Type, $TInfo, $Arch, $Word)/$BYTE; 207 $Offsets{$Pos} = $Offset; 208 my $GroupOffset = int($Offset/$Word)*$Word; 209 $Group{$GroupOffset}{$Pos} = 1; 210 } 211 } 212 foreach my $GroupOffset (sort {int($a)<=>int($b)} (keys(%Group))) 213 { 214 my %GroupClasses = (); 215 foreach my $Pos (sort {int($a)<=>int($b)} (keys(%{$Group{$GroupOffset}}))) 216 { # split the field into the classes 217 my $MTid = $Type{"Memb"}{$Pos}{"type"}; 218 my $MName = $Type{"Memb"}{$Pos}{"name"}; 219 my %SubClasses = classifyType($MTid, $TInfo, $Arch, $System, $Word); 220 foreach my $Offset (sort {int($a)<=>int($b)} keys(%SubClasses)) 221 { 222 if(defined $SubClasses{$Offset}{"Elems"}) 223 { 224 foreach (keys(%{$SubClasses{$Offset}{"Elems"}})) { 225 $SubClasses{$Offset}{"Elems"}{$_} = joinFields($MName, $SubClasses{$Offset}{"Elems"}{$_}); 226 } 227 } 228 else { 229 $SubClasses{$Offset}{"Elems"}{0} = $MName; 230 } 231 } 232 233 # add to the group 234 foreach my $Offset (sort {int($a)<=>int($b)} keys(%SubClasses)) { 235 $GroupClasses{$Offsets{$Pos}+$Offset} = $SubClasses{$Offset}; 236 } 237 } 238 239 # merge classes in the group 240 my %MergeGroup = (); 241 242 foreach my $Offset (sort {int($a)<=>int($b)} keys(%GroupClasses)) { 243 $MergeGroup{int($Offset/$Word)}{$Offset} = $GroupClasses{$Offset}; 244 } 245 246 foreach my $Offset (sort {int($a)<=>int($b)} keys(%MergeGroup)) { 247 while(postMerger($Arch, $System, $MergeGroup{$Offset})) { }; 248 } 249 250 %GroupClasses = (); 251 foreach my $M_Offset (sort {int($a)<=>int($b)} keys(%MergeGroup)) 252 { 253 foreach my $Offset (sort {int($a)<=>int($b)} keys(%{$MergeGroup{$M_Offset}})) 254 { 255 $GroupClasses{$Offset} = $MergeGroup{$M_Offset}{$Offset}; 256 } 257 } 258 259 # add to the result list of classes 260 foreach my $Offset (sort {int($a)<=>int($b)} keys(%GroupClasses)) 261 { 262 if($Type{"Type"} eq "Union") 263 { 264 foreach my $P (keys(%{$GroupClasses{$Offset}{"Elems"}})) 265 { 266 if($P!=0) { 267 delete($GroupClasses{$Offset}{"Elems"}{$P}); 268 } 269 } 270 } 271 $Classes{$Offset} = $GroupClasses{$Offset}; 272 } 273 } 274 275 return %Classes; 276 } 277 278 sub postMerger($$$) 279 { 280 my ($Arch, $System, $PreClasses) = @_; 281 my @Offsets = sort {int($a)<=>int($b)} keys(%{$PreClasses}); 282 if($#Offsets==0) { 283 return 0; 284 } 285 my %PostClasses = (); 286 my $Num = 0; 287 my $Merged = 0; 288 while($Num<=$#Offsets-1) 289 { 290 my $Offset1 = $Offsets[$Num]; 291 my $Offset2 = $Offsets[$Num+1]; 292 my $Class1 = $PreClasses->{$Offset1}{"Class"}; 293 my $Class2 = $PreClasses->{$Offset2}{"Class"}; 294 my $ResClass = ""; 295 if($System=~/\A(unix|linux|macos|freebsd)\Z/) 296 { # GCC 297 if($Arch eq "x86_64") 298 { 299 if($Class1 eq $Class2) { 300 $ResClass = $Class1; 301 } 302 elsif($Class1 eq "MEMORY" 303 or $Class2 eq "MEMORY") { 304 $ResClass = "MEMORY"; 305 } 306 elsif($Class1 eq "INTEGER" 307 or $Class2 eq "INTEGER") { 308 $ResClass = "INTEGER"; 309 } 310 elsif($Class1=~/X87/ 311 or $Class2=~/X87/) { 312 $ResClass = "MEMORY"; 313 } 314 else { 315 $ResClass = "SSE"; 316 } 317 } 318 } 319 if($ResClass) 320 { # combine 321 $PostClasses{$Offset1}{"Class"} = $ResClass; 322 foreach (keys(%{$PreClasses->{$Offset1}{"Elems"}})) { 323 $PostClasses{$Offset1}{"Elems"}{$Offset1+$_} = $PreClasses->{$Offset1}{"Elems"}{$_}; 324 } 325 foreach (keys(%{$PreClasses->{$Offset2}{"Elems"}})) { 326 $PostClasses{$Offset1}{"Elems"}{$Offset2+$_} = $PreClasses->{$Offset2}{"Elems"}{$_}; 327 } 328 $Merged = 1; 329 } 330 else 331 { # save unchanged 332 $PostClasses{$Offset1} = $PreClasses->{$Offset1}; 333 $PostClasses{$Offset2} = $PreClasses->{$Offset2}; 334 } 335 $Num += 2; 336 } 337 if($Num==$#Offsets) { 338 $PostClasses{$Offsets[$Num]} = $PreClasses->{$Offsets[$Num]}; 339 } 340 %{$PreClasses} = %PostClasses; 341 return $Merged; 342 } 343 344 sub callingConvention_R_Model($$$$$$) { 345 return callingConvention_R_I_Model(@_, 1); 346 } 347 348 sub joinFields($$) 349 { 350 my ($F1, $F2) = @_; 351 if(substr($F2, 0, 1) eq "[") 352 { # array elements 353 return $F1.$F2; 354 } 355 else { # fields 356 return $F1.".".$F2; 357 } 358 } 359 360 sub callingConvention_R_I_Model($$$$$$) 361 { 362 my ($SInfo, $TInfo, $Arch, $System, $Word, $Target) = @_; 363 my %Conv = (); 364 my $RTid = $SInfo->{"Return"}; 365 my %Type = get_PureType($RTid, $TInfo); 366 367 if($Target) { 368 %UsedReg = (); 369 } 370 371 my %UsedReg_Copy = %UsedReg; 372 373 my %Classes = classifyType($RTid, $TInfo, $Arch, $System, $Word); 374 375 foreach my $Offset (sort {int($a)<=>int($b)} keys(%Classes)) 376 { 377 my $Elems = undef; 378 if(defined $Classes{$Offset}{"Elems"}) 379 { 380 foreach (keys(%{$Classes{$Offset}{"Elems"}})) { 381 $Classes{$Offset}{"Elems"}{$_} = joinFields(".result", $Classes{$Offset}{"Elems"}{$_}); 382 } 383 $Elems = $Classes{$Offset}{"Elems"}; 384 } 385 else { 386 $Elems = { 0 => ".result" }; 387 } 388 389 my $CName = $Classes{$Offset}{"Class"}; 390 391 if($CName eq "VOID") { 392 next; 393 } 394 395 if($System=~/\A(unix|linux|macos|freebsd)\Z/) 396 { # GCC 397 if($Arch eq "x86") 398 { 399 if($CName eq "FLOAT") 400 { # x87 register 401 useRegister("st0", "f", $Elems, $SInfo); 402 } 403 elsif($CName eq "INTEGRAL") 404 { 405 useRegister("eax", "f", $Elems, $SInfo); 406 } 407 elsif($CName eq "MEMORY") { 408 pushStack_R($SInfo, $Word); 409 } 410 } 411 elsif($Arch eq "x86_64") 412 { 413 my @INT = ("rax", "rdx"); 414 my @SSE = ("xmm0", "xmm1"); 415 if($CName eq "INTEGER") 416 { 417 if(my $R = getLastAvailable($SInfo, "f", @INT)) 418 { 419 useRegister($R, "f", $Elems, $SInfo); 420 } 421 else 422 { # revert registers 423 # pass as MEMORY 424 %UsedReg = %UsedReg_Copy; 425 useHidden($SInfo, $Arch, $System, $Word); 426 $Conv{"Hidden"} = 1; 427 last; 428 } 429 } 430 elsif($CName eq "SSE") 431 { 432 if(my $R = getLastAvailable($SInfo, "8l", @SSE)) 433 { 434 useRegister($R, "8l", $Elems, $SInfo); 435 } 436 else 437 { 438 %UsedReg = %UsedReg_Copy; 439 useHidden($SInfo, $Arch, $System, $Word); 440 $Conv{"Hidden"} = 1; 441 last; 442 } 443 } 444 elsif($CName eq "SSEUP") 445 { 446 if(my $R = getLastUsed($SInfo, "xmm0", "xmm1")) 447 { 448 useRegister($R, "8h", $Elems, $SInfo); 449 } 450 else 451 { 452 %UsedReg = %UsedReg_Copy; 453 useHidden($SInfo, $Arch, $System, $Word); 454 $Conv{"Hidden"} = 1; 455 last; 456 } 457 } 458 elsif($CName eq "X87") 459 { 460 useRegister("st0", "8l", $Elems, $SInfo); 461 } 462 elsif($CName eq "X87UP") 463 { 464 useRegister("st0", "8h", $Elems, $SInfo); 465 } 466 elsif($CName eq "COMPLEX_X87") 467 { 468 useRegister("st0", "f", $Elems, $SInfo); 469 useRegister("st1", "f", $Elems, $SInfo); 470 } 471 elsif($CName eq "MEMORY") 472 { 473 useHidden($SInfo, $Arch, $System, $Word); 474 $Conv{"Hidden"} = 1; 475 last; 476 } 477 } 478 elsif($Arch eq "arm") 479 { # TODO 480 } 481 } 482 elsif($System eq "windows") 483 { # MS C++ Compiler 484 if($Arch eq "x86") 485 { 486 if($CName eq "FLOAT") 487 { 488 useRegister("fp0", "f", $Elems, $SInfo); 489 } 490 elsif($CName eq "INTEGRAL") 491 { 492 useRegister("eax", "f", $Elems, $SInfo); 493 } 494 elsif($CName eq "POD") 495 { 496 useRegister("eax", "f", $Elems, $SInfo); 497 useRegister("edx", "f", $Elems, $SInfo); 498 } 499 elsif($CName eq "MEMORY" or $CName eq "M128") 500 { 501 useHidden($SInfo, $Arch, $System, $Word); 502 $Conv{"Hidden"} = 1; 503 } 504 } 505 elsif($Arch eq "x86_64") 506 { 507 if($CName eq "FLOAT" or $CName eq "M128") 508 { 509 useRegister("xmm0", "f", $Elems, $SInfo); 510 } 511 elsif($CName eq "INTEGRAL") 512 { 513 useRegister("eax", "f", $Elems, $SInfo); 514 } 515 elsif($CName eq "MEMORY") 516 { 517 useHidden($SInfo, $Arch, $System, $Word); 518 $Conv{"Hidden"} = 1; 519 } 520 } 521 } 522 } 523 524 525 if(my %Regs = usedBy(".result", $SInfo)) 526 { 527 $Conv{"Method"} = "reg"; 528 $Conv{"Registers"} = join(", ", sort(keys(%Regs))); 529 } 530 elsif(my %Regs = usedBy(".result_ptr", $SInfo)) 531 { 532 $Conv{"Method"} = "reg"; 533 $Conv{"Registers"} = join(", ", sort(keys(%Regs))); 534 } 535 536 if(not $Conv{"Method"}) 537 { # unknown 538 if($Type{"Name"} ne "void") 539 { 540 $Conv{"Method"} = "stack"; 541 $Conv{"Hidden"} = 1; 542 } 543 } 544 545 return %Conv; 546 } 547 548 sub usedBy($$) 549 { 550 my ($Name, $SInfo) = @_; 551 my %Regs = (); 552 foreach my $Reg (sort keys(%{$UsedReg{$SInfo}})) 553 { 554 foreach my $Size (sort keys(%{$UsedReg{$SInfo}{$Reg}})) 555 { 556 foreach my $Offset (sort keys(%{$UsedReg{$SInfo}{$Reg}{$Size}})) 557 { 558 if($UsedReg{$SInfo}{$Reg}{$Size}{$Offset}=~/\A\Q$Name\E(\.|\Z)/) { 559 $Regs{$Reg} = 1; 560 } 561 } 562 } 563 } 564 return %Regs; 565 } 566 567 sub useHidden($$$$) 568 { 569 my ($SInfo, $Arch, $System, $Word) = @_; 570 if($System=~/\A(unix|linux|macos|freebsd)\Z/) 571 { # GCC 572 if($Arch eq "x86") { 573 pushStack_R($SInfo, $Word); 574 } 575 elsif($Arch eq "x86_64") 576 { 577 my $Elems = { 0 => ".result_ptr" }; 578 useRegister("rdi", "f", $Elems, $SInfo); 579 } 580 } 581 elsif($System eq "windows") 582 { # MS C++ Compiler 583 if($Arch eq "x86") { 584 pushStack_R($SInfo, $Word); 585 } 586 elsif($Arch eq "x86_64") 587 { 588 my $Elems = { 0 => ".result_ptr" }; 589 useRegister("rcx", "f", $Elems, $SInfo); 590 } 591 } 592 } 593 594 sub pushStack_P($$$$) 595 { 596 my ($SInfo, $Pos, $TInfo, $StackAlgn) = @_; 597 my $PTid = $SInfo->{"Param"}{$Pos}{"type"}; 598 my $PName = $SInfo->{"Param"}{$Pos}{"name"}; 599 600 if(my $Offset = $SInfo->{"Param"}{$Pos}{"offset"}) 601 { # DWARF ABI Dump 602 return pushStack_Offset($SInfo, $Offset, $TInfo->{$PTid}{"Size"}, { 0 => $PName }); 603 } 604 else 605 { 606 my $Alignment = $SInfo->{"Param"}{$Pos}{"algn"}; 607 if($Alignment<$StackAlgn) { 608 $Alignment = $StackAlgn; 609 } 610 return pushStack($SInfo, $Alignment, $TInfo->{$PTid}{"Size"}, { 0 => $PName }); 611 } 612 } 613 614 sub pushStack_R($$) 615 { 616 my ($SInfo, $Word) = @_; 617 return pushStack($SInfo, $Word, $Word, { 0 => ".result_ptr" }); 618 } 619 620 sub pushStack_C($$$) 621 { 622 my ($SInfo, $Class, $TInfo) = @_; 623 return pushStack($SInfo, $Class->{"Algn"}, $Class->{"Size"}, $Class->{"Elems"}); 624 } 625 626 sub pushStack($$$$) 627 { 628 my ($SInfo, $Algn, $Size, $Elem) = @_; 629 my $Offset = 0; 630 if(my @Offsets = sort {int($a)<=>int($b)} keys(%{$UsedStack{$SInfo}})) 631 { 632 $Offset = $Offsets[$#Offsets]; 633 $Offset += $UsedStack{$SInfo}{$Offset}{"Size"}; 634 $Offset += getPadding($Offset, $Algn); 635 } 636 return pushStack_Offset($SInfo, $Offset, $Size, $Elem); 637 } 638 639 sub pushStack_Offset($$$$) 640 { 641 my ($SInfo, $Offset, $Size, $Elem) = @_; 642 my %Info = ( 643 "Size" => $Size, 644 "Elem" => $Elem 645 ); 646 $UsedStack{$SInfo}{$Offset} = \%Info; 647 return $Offset; 648 } 649 650 sub useRegister($$$$) 651 { 652 my ($R, $Offset, $Elems, $SInfo) = @_; 653 if(defined $UsedReg{$SInfo}{$R}) 654 { 655 if(defined $UsedReg{$SInfo}{$R}{$Offset}) 656 { # busy 657 return 0; 658 } 659 } 660 $UsedReg{$SInfo}{$R}{$Offset}=$Elems; 661 return $R; 662 } 663 664 sub getLastAvailable(@) 665 { 666 my $SInfo = shift(@_); 667 my $Offset = shift(@_); 668 my $Pos = 0; 669 foreach (@_) 670 { 671 if(not defined $UsedReg{$SInfo}{$_}) { 672 return $_; 673 } 674 elsif(not defined $UsedReg{$SInfo}{$_}{$Offset}) { 675 return $_; 676 } 677 } 678 return undef; 679 } 680 681 sub getLastUsed(@) 682 { 683 my $SInfo = shift(@_); 684 my $Pos = 0; 685 foreach (@_) 686 { 687 if(not defined $UsedReg{$SInfo}{$_}) 688 { 689 if($Pos>0) { 690 return @_[$Pos-1]; 691 } 692 else { 693 return @_[0]; 694 } 695 } 696 $Pos+=1; 697 } 698 return undef; 699 } 700 701 sub callingConvention_P_Model($$$$$$) { 702 return callingConvention_P_I_Model(@_, 1); 703 } 704 705 sub callingConvention_P_I_Model($$$$$$$) 706 { # calling conventions for different compilers and operating systems 707 my ($SInfo, $Pos, $TInfo, $Arch, $System, $Word, $Target) = @_; 708 my %Conv = (); 709 my $ParamTypeId = $SInfo->{"Param"}{$Pos}{"type"}; 710 my $PName = $SInfo->{"Param"}{$Pos}{"name"}; 711 my %Type = get_PureType($ParamTypeId, $TInfo); 712 713 if($Target) 714 { 715 %UsedReg = (); 716 717 # distribute return value 718 if(my $RTid = $SInfo->{"Return"}) { 719 callingConvention_R_I_Model($SInfo, $TInfo, $Arch, $System, $Word, 0); 720 } 721 # distribute other parameters 722 if($Pos>0) 723 { 724 my %PConv = (); 725 my $PPos = 0; 726 while($PConv{"Next"} ne $Pos) 727 { 728 %PConv = callingConvention_P_I_Model($SInfo, $PPos++, $TInfo, $Arch, $System, $Word, 0); 729 if(not $PConv{"Next"}) { 730 last; 731 } 732 } 733 } 734 } 735 736 my %UsedReg_Copy = %UsedReg; 737 738 my %Classes = classifyType($ParamTypeId, $TInfo, $Arch, $System, $Word); 739 740 my $Error = 0; 741 foreach my $Offset (sort {int($a)<=>int($b)} keys(%Classes)) 742 { 743 my $Elems = undef; 744 if(defined $Classes{$Offset}{"Elems"}) 745 { 746 foreach (keys(%{$Classes{$Offset}{"Elems"}})) { 747 $Classes{$Offset}{"Elems"}{$_} = joinFields($PName, $Classes{$Offset}{"Elems"}{$_}); 748 } 749 $Elems = $Classes{$Offset}{"Elems"}; 750 } 751 else { 752 $Elems = { 0 => $PName }; 753 } 754 755 my $CName = $Classes{$Offset}{"Class"}; 756 757 if($CName eq "VOID") { 758 next; 759 } 760 761 if($System=~/\A(unix|linux|macos|freebsd)\Z/) 762 { # GCC 763 if($Arch eq "x86") 764 { 765 pushStack_P($SInfo, $Pos, $TInfo, $Word); 766 last; 767 } 768 elsif($Arch eq "x86_64") 769 { 770 my @INT = ("rdi", "rsi", "rdx", "rcx", "r8", "r9"); 771 my @SSE = ("xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"); 772 773 if($CName eq "INTEGER") 774 { 775 if(my $R = getLastAvailable($SInfo, "f", @INT)) { 776 useRegister($R, "f", $Elems, $SInfo); 777 } 778 else 779 { # revert registers and 780 # push the argument on the stack 781 %UsedReg = %UsedReg_Copy; 782 pushStack_P($SInfo, $Pos, $TInfo, $Word); 783 last; 784 } 785 } 786 elsif($CName eq "SSE") 787 { 788 if(my $R = getLastAvailable($SInfo, "8l", @SSE)) { 789 useRegister($R, "8l", $Elems, $SInfo); 790 } 791 else 792 { 793 %UsedReg = %UsedReg_Copy; 794 pushStack_P($SInfo, $Pos, $TInfo, $Word); 795 last; 796 } 797 } 798 elsif($CName eq "SSEUP") 799 { 800 if(my $R = getLastUsed($SInfo, @SSE)) { 801 useRegister($R, "8h", $Elems, $SInfo); 802 } 803 else 804 { 805 %UsedReg = %UsedReg_Copy; 806 pushStack_P($SInfo, $Pos, $TInfo, $Word); 807 last; 808 } 809 } 810 elsif($CName=~/X87|MEMORY/) 811 { # MEMORY, X87, X87UP, COMPLEX_X87 812 pushStack_P($SInfo, $Pos, $TInfo, $Word); 813 last; 814 } 815 else 816 { 817 pushStack_P($SInfo, $Pos, $TInfo, $Word); 818 last; 819 } 820 } 821 elsif($Arch eq "arm") 822 { # Procedure Call Standard for the ARM Architecture 823 # TODO 824 pushStack_P($SInfo, $Pos, $TInfo, $Word); 825 last; 826 } 827 else 828 { # TODO 829 pushStack_P($SInfo, $Pos, $TInfo, $Word); 830 last; 831 } 832 } 833 elsif($System eq "windows") 834 { # MS C++ Compiler 835 if($Arch eq "x86") 836 { 837 pushStack_P($SInfo, $Pos, $TInfo, $Word); 838 last; 839 } 840 elsif($Arch eq "x86_64") 841 { 842 if($Pos<=3) 843 { 844 if($CName eq "FLOAT") 845 { 846 useRegister("xmm".$Pos, "8l", $Elems, $SInfo); 847 } 848 elsif($CName eq "INTEGRAL") 849 { 850 if($Pos==0) { 851 useRegister("rcx", "f", $Elems, $SInfo); 852 } 853 elsif($Pos==1) { 854 useRegister("rdx", "f", $Elems, $SInfo); 855 } 856 elsif($Pos==2) { 857 useRegister("r8", "f", $Elems, $SInfo); 858 } 859 elsif($Pos==3) { 860 useRegister("r9", "f", $Elems, $SInfo); 861 } 862 else 863 { 864 pushStack_P($SInfo, $Pos, $TInfo, $Word); 865 last; 866 } 867 } 868 else 869 { 870 pushStack_P($SInfo, $Pos, $TInfo, $Word); 871 last; 872 } 873 } 874 else 875 { 876 pushStack_P($SInfo, $Pos, $TInfo, $Word); 877 last; 878 } 879 } 880 } 881 else 882 { # TODO 883 pushStack_P($SInfo, $Pos, $TInfo, $Word); 884 last; 885 } 886 } 887 888 if(my %Regs = usedBy($PName, $SInfo)) 889 { 890 $Conv{"Method"} = "reg"; 891 $Conv{"Registers"} = join(", ", sort(keys(%Regs))); 892 } 893 else 894 { 895 if($Type{"Name"} ne "void") { 896 $Conv{"Method"} = "stack"; 897 } 898 } 899 900 if(defined $SInfo->{"Param"}{$Pos+1}) 901 { # TODO 902 $Conv{"Next"} = $Pos+1; 903 } 904 905 return %Conv; 906 } 907 908 sub getAlignment_Model($$$) 909 { 910 my ($Tid, $TInfo, $Arch) = @_; 911 912 if(not $Tid) 913 { # incomplete ABI dump 914 return 0; 915 } 916 917 if(defined $TInfo->{$Tid}{"Algn"}) { 918 return $TInfo->{$Tid}{"Algn"}; 919 } 920 else 921 { 922 if($TInfo->{$Tid}{"Type"}=~/Struct|Class|Union|MethodPtr/) 923 { 924 if(defined $TInfo->{$Tid}{"Memb"}) 925 { 926 my $Max = 0; 927 foreach my $Pos (keys(%{$TInfo->{$Tid}{"Memb"}})) 928 { 929 my $Algn = $TInfo->{$Tid}{"Memb"}{$Pos}{"algn"}; 930 if(not $Algn) { 931 $Algn = getAlignment_Model($TInfo->{$Tid}{"Memb"}{$Pos}{"type"}, $TInfo, $Arch); 932 } 933 if($Algn>$Max) { 934 $Max = $Algn; 935 } 936 } 937 return $Max; 938 } 939 return 0; 940 } 941 elsif($TInfo->{$Tid}{"Type"} eq "Array") 942 { 943 my %Base = get_OneStep_BaseType($Tid, $TInfo); 944 945 if($Base{"Tid"} eq $Tid) 946 { # emergency exit 947 return 0; 948 } 949 950 return getAlignment_Model($Base{"Tid"}, $TInfo, $Arch); 951 } 952 elsif($TInfo->{$Tid}{"Type"}=~/Intrinsic|Enum|Pointer|FuncPtr/) 953 { # model 954 return getInt_Algn($Tid, $TInfo, $Arch); 955 } 956 else 957 { 958 my %PureType = get_PureType($Tid, $TInfo); 959 960 if($PureType{"Tid"} eq $Tid) 961 { # emergency exit 962 return 0; 963 } 964 965 return getAlignment_Model($PureType{"Tid"}, $TInfo, $Arch); 966 } 967 } 968 } 969 970 sub getInt_Algn($$$) 971 { 972 my ($Tid, $TInfo, $Arch) = @_; 973 my $Name = $TInfo->{$Tid}{"Name"}; 974 if(my $Algn = $IntAlgn{$Arch}{$Name}) { 975 return $Algn; 976 } 977 else 978 { 979 my $Size = $TInfo->{$Tid}{"Size"}; 980 if($Arch eq "x86_64") 981 { # x86_64: sizeof==alignment 982 return $Size; 983 } 984 elsif($Arch eq "arm") 985 { 986 if($Size>8) 987 { # 128-bit vector (16) 988 return 8; 989 } 990 return $Size; 991 } 992 elsif($Arch eq "x86") 993 { 994 if($Size>4) 995 { # "double" (8) and "long double" (12) 996 return 4; 997 } 998 return $Size; 999 } 1000 return $Size; 1001 } 1002 } 1003 1004 sub getAlignment($$$$$) 1005 { 1006 my ($Pos, $TypePtr, $TInfo, $Arch, $Word) = @_; 1007 my $Tid = $TypePtr->{"Memb"}{$Pos}{"type"}; 1008 my %Type = get_PureType($Tid, $TInfo); 1009 my $Computed = $TypePtr->{"Memb"}{$Pos}{"algn"}; 1010 my $Alignment = 0; 1011 1012 if(my $BSize = $TypePtr->{"Memb"}{$Pos}{"bitfield"}) 1013 { # bitfields 1014 if($Computed) 1015 { # real in bits 1016 $Alignment = $Computed; 1017 } 1018 else 1019 { # model 1020 if($BSize eq $Type{"Size"}*$BYTE) 1021 { 1022 $Alignment = $BSize; 1023 } 1024 else { 1025 $Alignment = 1; 1026 } 1027 } 1028 return ($Alignment, $BSize); 1029 } 1030 else 1031 { # other fields 1032 if($Computed) 1033 { # real in bytes 1034 $Alignment = $Computed*$BYTE; 1035 } 1036 else 1037 { # model 1038 $Alignment = getAlignment_Model($Tid, $TInfo, $Arch)*$BYTE; 1039 } 1040 return ($Alignment, $Type{"Size"}*$BYTE); 1041 } 1042 } 1043 1044 sub getOffset($$$$$) 1045 { # offset of the field including padding 1046 my ($FieldPos, $TypePtr, $TInfo, $Arch, $Word) = @_; 1047 1048 if($TypePtr->{"Type"} eq "Union") { 1049 return 0; 1050 } 1051 1052 # if((my $Off = $TypePtr->{"Memb"}{$FieldPos}{"offset"}) ne "") 1053 # { # DWARF ABI Dump (generated by the ABI Dumper tool) 1054 # return $Off*$BYTE; 1055 # } 1056 1057 my $Offset = 0; 1058 my $Buffer=0; 1059 1060 foreach my $Pos (0 .. keys(%{$TypePtr->{"Memb"}})-1) 1061 { 1062 my ($Alignment, $MSize) = getAlignment($Pos, $TypePtr, $TInfo, $Arch, $Word); 1063 1064 if(not $Alignment) 1065 { # support for old ABI dumps 1066 if($MSize=~/\A(8|16|32|64)\Z/) 1067 { 1068 if($Buffer+$MSize<$Word*$BYTE) 1069 { 1070 $Alignment = 1; 1071 $Buffer += $MSize; 1072 } 1073 else 1074 { 1075 $Alignment = $MSize; 1076 $Buffer = 0; 1077 } 1078 } 1079 else 1080 { 1081 $Alignment = 1; 1082 $Buffer += $MSize; 1083 } 1084 } 1085 1086 # padding 1087 $Offset += getPadding($Offset, $Alignment); 1088 if($Pos==$FieldPos) 1089 { # after the padding 1090 # before the field 1091 return $Offset; 1092 } 1093 $Offset += $MSize; 1094 } 1095 return $FieldPos; # if something is going wrong 1096 } 1097 1098 sub getPadding($$) 1099 { 1100 my ($Offset, $Alignment) = @_; 1101 my $Padding = 0; 1102 if($Offset % $Alignment!=0) 1103 { # not aligned, add padding 1104 $Padding = $Alignment - $Offset % $Alignment; 1105 } 1106 return $Padding; 1107 } 1108 1109 sub isMemPadded($$$$$$) 1110 { # check if the target field can be added/removed/changed 1111 # without shifting other fields because of padding bits 1112 my ($FieldPos, $Size, $TypePtr, $Skip, $TInfo, $Arch, $Word) = @_; 1113 return 0 if($FieldPos==0); 1114 delete($TypePtr->{"Memb"}{""}); 1115 my $Offset = 0; 1116 my (%Alignment, %MSize) = (); 1117 my $MaxAlgn = 0; 1118 my $End = keys(%{$TypePtr->{"Memb"}})-1; 1119 my $NextField = $FieldPos+1; 1120 foreach my $Pos (0 .. $End) 1121 { 1122 if($Skip and $Skip->{$Pos}) 1123 { # skip removed/added fields 1124 if($Pos > $FieldPos) 1125 { # after the target 1126 $NextField += 1; 1127 next; 1128 } 1129 } 1130 ($Alignment{$Pos}, $MSize{$Pos}) = getAlignment($Pos, $TypePtr, $TInfo, $Arch, $Word); 1131 1132 if(not $Alignment{$Pos}) 1133 { # emergency exit 1134 return 0; 1135 } 1136 1137 if($Alignment{$Pos}>$MaxAlgn) { 1138 $MaxAlgn = $Alignment{$Pos}; 1139 } 1140 if($Pos==$FieldPos) 1141 { 1142 if($Size==-1) 1143 { # added/removed fields 1144 if($Pos!=$End) 1145 { # skip target field and see 1146 # if enough padding will be 1147 # created on the next step 1148 # to include this field 1149 next; 1150 } 1151 } 1152 } 1153 # padding 1154 my $Padding = 0; 1155 if($Offset % $Alignment{$Pos}!=0) 1156 { # not aligned, add padding 1157 $Padding = $Alignment{$Pos} - $Offset % $Alignment{$Pos}; 1158 } 1159 if($Pos==$NextField) 1160 { # try to place target field in the padding 1161 if($Size==-1) 1162 { # added/removed fields 1163 my $TPadding = 0; 1164 if($Offset % $Alignment{$FieldPos}!=0) 1165 {# padding of the target field 1166 $TPadding = $Alignment{$FieldPos} - $Offset % $Alignment{$FieldPos}; 1167 } 1168 if($TPadding+$MSize{$FieldPos}<=$Padding) 1169 { # enough padding to place target field 1170 return 1; 1171 } 1172 else { 1173 return 0; 1174 } 1175 } 1176 else 1177 { # changed fields 1178 my $Delta = $Size-$MSize{$FieldPos}; 1179 if($Delta>=0) 1180 { # increased 1181 if($Size-$MSize{$FieldPos}<=$Padding) 1182 { # enough padding to change target field 1183 return 1; 1184 } 1185 else { 1186 return 0; 1187 } 1188 } 1189 else 1190 { # decreased 1191 $Delta = abs($Delta); 1192 if($Delta+$Padding>=$MSize{$Pos}) 1193 { # try to place the next field 1194 if(($Offset-$Delta) % $Alignment{$Pos} != 0) 1195 { # padding of the next field in new place 1196 my $NPadding = $Alignment{$Pos} - ($Offset-$Delta) % $Alignment{$Pos}; 1197 if($NPadding+$MSize{$Pos}<=$Delta+$Padding) 1198 { # enough delta+padding to store next field 1199 return 0; 1200 } 1201 } 1202 else 1203 { 1204 return 0; 1205 } 1206 } 1207 return 1; 1208 } 1209 } 1210 } 1211 elsif($Pos==$End) 1212 { # target field is the last field 1213 if($Size==-1) 1214 { # added/removed fields 1215 if($Offset % $MaxAlgn!=0) 1216 { # tail padding 1217 my $TailPadding = $MaxAlgn - $Offset % $MaxAlgn; 1218 if($Padding+$MSize{$Pos}<=$TailPadding) 1219 { # enough tail padding to place the last field 1220 return 1; 1221 } 1222 } 1223 return 0; 1224 } 1225 else 1226 { # changed fields 1227 # scenario #1 1228 my $Offset1 = $Offset+$Padding+$MSize{$Pos}; 1229 if($Offset1 % $MaxAlgn != 0) 1230 { # tail padding 1231 $Offset1 += $MaxAlgn - $Offset1 % $MaxAlgn; 1232 } 1233 # scenario #2 1234 my $Offset2 = $Offset+$Padding+$Size; 1235 if($Offset2 % $MaxAlgn != 0) 1236 { # tail padding 1237 $Offset2 += $MaxAlgn - $Offset2 % $MaxAlgn; 1238 } 1239 if($Offset1!=$Offset2) 1240 { # different sizes of structure 1241 return 0; 1242 } 1243 return 1; 1244 } 1245 } 1246 $Offset += $Padding+$MSize{$Pos}; 1247 } 1248 return 0; 1249 } 1250 1251 sub isScalar($) { 1252 return ($_[0]=~/\A(unsigned |)(char|short|int|long|long long)\Z/); 1253 } 1254 1255 sub isFloat($) { 1256 return ($_[0]=~/\A(float|double|long double)\Z/); 1257 } 1258 1259 sub callingConvention_R_Real($) 1260 { 1261 my $SInfo = $_[0]; 1262 my %Conv = (); 1263 my %Regs = (); 1264 my $Hidden = 0; 1265 foreach my $Elem (keys(%{$SInfo->{"Reg"}})) 1266 { 1267 my $Reg = $SInfo->{"Reg"}{$Elem}; 1268 if($Elem eq ".result_ptr") 1269 { 1270 $Hidden = 1; 1271 $Regs{$Reg} = 1; 1272 } 1273 elsif(index($Elem, ".result")==0) { 1274 $Regs{$Reg} = 1; 1275 } 1276 } 1277 if(my @R = sort keys(%Regs)) 1278 { 1279 $Conv{"Method"} = "reg"; 1280 $Conv{"Registers"} = join(", ", @R); 1281 if($Hidden) { 1282 $Conv{"Hidden"} = 1; 1283 } 1284 } 1285 else 1286 { 1287 $Conv{"Method"} = "stack"; 1288 $Conv{"Hidden"} = 1; 1289 } 1290 return %Conv; 1291 } 1292 1293 sub callingConvention_P_Real($$) 1294 { 1295 my ($SInfo, $Pos) = @_; 1296 my %Conv = (); 1297 my %Regs = (); 1298 foreach my $Elem (keys(%{$SInfo->{"Reg"}})) 1299 { 1300 my $Reg = $SInfo->{"Reg"}{$Elem}; 1301 if($Elem=~/\A$Pos([\.\+]|\Z)/) { 1302 $Regs{$Reg} = 1; 1303 } 1304 } 1305 if(my @R = sort keys(%Regs)) 1306 { 1307 $Conv{"Method"} = "reg"; 1308 $Conv{"Registers"} = join(", ", @R); 1309 } 1310 else 1311 { 1312 $Conv{"Method"} = "stack"; 1313 1314 if(defined $SInfo->{"Param"} 1315 and defined $SInfo->{"Param"}{0}) 1316 { 1317 if(not defined $SInfo->{"Param"}{0}{"offset"}) 1318 { 1319 $Conv{"Method"} = "unknown"; 1320 } 1321 } 1322 } 1323 1324 return %Conv; 1325 } 1326 1327 return 1;