1160814Ssimon#!/usr/bin/env perl 2160814Ssimon 3238405Sjkim# Ascetic x86_64 AT&T to MASM/NASM assembler translator by <appro>. 4160814Ssimon# 5160814Ssimon# Why AT&T to MASM and not vice versa? Several reasons. Because AT&T 6160814Ssimon# format is way easier to parse. Because it's simpler to "gear" from 7160814Ssimon# Unix ABI to Windows one [see cross-reference "card" at the end of 8160814Ssimon# file]. Because Linux targets were available first... 9160814Ssimon# 10160814Ssimon# In addition the script also "distills" code suitable for GNU 11160814Ssimon# assembler, so that it can be compiled with more rigid assemblers, 12160814Ssimon# such as Solaris /usr/ccs/bin/as. 13160814Ssimon# 14160814Ssimon# This translator is not designed to convert *arbitrary* assembler 15160814Ssimon# code from AT&T format to MASM one. It's designed to convert just 16160814Ssimon# enough to provide for dual-ABI OpenSSL modules development... 17160814Ssimon# There *are* limitations and you might have to modify your assembler 18160814Ssimon# code or this script to achieve the desired result... 19160814Ssimon# 20160814Ssimon# Currently recognized limitations: 21160814Ssimon# 22160814Ssimon# - can't use multiple ops per line; 23160814Ssimon# 24160814Ssimon# Dual-ABI styling rules. 25160814Ssimon# 26238405Sjkim# 1. Adhere to Unix register and stack layout [see cross-reference 27238405Sjkim# ABI "card" at the end for explanation]. 28160814Ssimon# 2. Forget about "red zone," stick to more traditional blended 29160814Ssimon# stack frame allocation. If volatile storage is actually required 30160814Ssimon# that is. If not, just leave the stack as is. 31160814Ssimon# 3. Functions tagged with ".type name,@function" get crafted with 32160814Ssimon# unified Win64 prologue and epilogue automatically. If you want 33160814Ssimon# to take care of ABI differences yourself, tag functions as 34160814Ssimon# ".type name,@abi-omnipotent" instead. 35160814Ssimon# 4. To optimize the Win64 prologue you can specify number of input 36160814Ssimon# arguments as ".type name,@function,N." Keep in mind that if N is 37160814Ssimon# larger than 6, then you *have to* write "abi-omnipotent" code, 38160814Ssimon# because >6 cases can't be addressed with unified prologue. 39160814Ssimon# 5. Name local labels as .L*, do *not* use dynamic labels such as 1: 40160814Ssimon# (sorry about latter). 41160814Ssimon# 6. Don't use [or hand-code with .byte] "rep ret." "ret" mnemonic is 42160814Ssimon# required to identify the spots, where to inject Win64 epilogue! 43160814Ssimon# But on the pros, it's then prefixed with rep automatically:-) 44238405Sjkim# 7. Stick to explicit ip-relative addressing. If you have to use 45238405Sjkim# GOTPCREL addressing, stick to mov symbol@GOTPCREL(%rip),%r??. 46238405Sjkim# Both are recognized and translated to proper Win64 addressing 47238405Sjkim# modes. To support legacy code a synthetic directive, .picmeup, 48238405Sjkim# is implemented. It puts address of the *next* instruction into 49238405Sjkim# target register, e.g.: 50160814Ssimon# 51160814Ssimon# .picmeup %rax 52160814Ssimon# lea .Label-.(%rax),%rax 53238405Sjkim# 54238405Sjkim# 8. In order to provide for structured exception handling unified 55238405Sjkim# Win64 prologue copies %rsp value to %rax. For further details 56238405Sjkim# see SEH paragraph at the end. 57238405Sjkim# 9. .init segment is allowed to contain calls to functions only. 58238405Sjkim# a. If function accepts more than 4 arguments *and* >4th argument 59238405Sjkim# is declared as non 64-bit value, do clear its upper part. 60238405Sjkim 61238405Sjkimmy $flavour = shift; 62238405Sjkimmy $output = shift; 63238405Sjkimif ($flavour =~ /\./) { $output = $flavour; undef $flavour; } 64160814Ssimon 65238405Sjkimopen STDOUT,">$output" || die "can't open $output: $!" 66238405Sjkim if (defined($output)); 67160814Ssimon 68238405Sjkimmy $gas=1; $gas=0 if ($output =~ /\.asm$/); 69238405Sjkimmy $elf=1; $elf=0 if (!$gas); 70238405Sjkimmy $win64=0; 71238405Sjkimmy $prefix=""; 72238405Sjkimmy $decor=".L"; 73160814Ssimon 74238405Sjkimmy $masmref=8 + 50727*2**-32; # 8.00.50727 shipped with VS2005 75238405Sjkimmy $masm=0; 76238405Sjkimmy $PTR=" PTR"; 77238405Sjkim 78238405Sjkimmy $nasmref=2.03; 79238405Sjkimmy $nasm=0; 80238405Sjkim 81238405Sjkimif ($flavour eq "mingw64") { $gas=1; $elf=0; $win64=1; 82238405Sjkim $prefix=`echo __USER_LABEL_PREFIX__ | $ENV{CC} -E -P -`; 83238405Sjkim chomp($prefix); 84238405Sjkim } 85238405Sjkimelsif ($flavour eq "macosx") { $gas=1; $elf=0; $prefix="_"; $decor="L\$"; } 86238405Sjkimelsif ($flavour eq "masm") { $gas=0; $elf=0; $masm=$masmref; $win64=1; $decor="\$L\$"; } 87238405Sjkimelsif ($flavour eq "nasm") { $gas=0; $elf=0; $nasm=$nasmref; $win64=1; $decor="\$L\$"; $PTR=""; } 88238405Sjkimelsif (!$gas) 89238405Sjkim{ if ($ENV{ASM} =~ m/nasm/ && `nasm -v` =~ m/version ([0-9]+)\.([0-9]+)/i) 90238405Sjkim { $nasm = $1 + $2*0.01; $PTR=""; } 91238405Sjkim elsif (`ml64 2>&1` =~ m/Version ([0-9]+)\.([0-9]+)(\.([0-9]+))?/) 92238405Sjkim { $masm = $1 + $2*2**-16 + $4*2**-32; } 93238405Sjkim die "no assembler found on %PATH" if (!($nasm || $masm)); 94238405Sjkim $win64=1; 95238405Sjkim $elf=0; 96238405Sjkim $decor="\$L\$"; 97194206Ssimon} 98194206Ssimon 99160814Ssimonmy $current_segment; 100160814Ssimonmy $current_function; 101238405Sjkimmy %globals; 102160814Ssimon 103160814Ssimon{ package opcode; # pick up opcodes 104160814Ssimon sub re { 105160814Ssimon my $self = shift; # single instance in enough... 106160814Ssimon local *line = shift; 107160814Ssimon undef $ret; 108160814Ssimon 109194206Ssimon if ($line =~ /^([a-z][a-z0-9]*)/i) { 110160814Ssimon $self->{op} = $1; 111160814Ssimon $ret = $self; 112160814Ssimon $line = substr($line,@+[0]); $line =~ s/^\s+//; 113160814Ssimon 114160814Ssimon undef $self->{sz}; 115238405Sjkim if ($self->{op} =~ /^(movz)x?([bw]).*/) { # movz is pain... 116160814Ssimon $self->{op} = $1; 117238405Sjkim $self->{sz} = $2; 118238405Sjkim } elsif ($self->{op} =~ /call|jmp/) { 119238405Sjkim $self->{sz} = ""; 120238405Sjkim } elsif ($self->{op} =~ /^p/ && $' !~ /^(ush|op|insrw)/) { # SSEn 121238405Sjkim $self->{sz} = ""; 122238405Sjkim } elsif ($self->{op} =~ /^v/) { # VEX 123238405Sjkim $self->{sz} = ""; 124296341Sdelphij } elsif ($self->{op} =~ /mov[dq]/ && $line =~ /%xmm/) { 125238405Sjkim $self->{sz} = ""; 126194206Ssimon } elsif ($self->{op} =~ /([a-z]{3,})([qlwb])$/) { 127160814Ssimon $self->{op} = $1; 128160814Ssimon $self->{sz} = $2; 129160814Ssimon } 130160814Ssimon } 131160814Ssimon $ret; 132160814Ssimon } 133160814Ssimon sub size { 134160814Ssimon my $self = shift; 135160814Ssimon my $sz = shift; 136160814Ssimon $self->{sz} = $sz if (defined($sz) && !defined($self->{sz})); 137160814Ssimon $self->{sz}; 138160814Ssimon } 139160814Ssimon sub out { 140160814Ssimon my $self = shift; 141238405Sjkim if ($gas) { 142194206Ssimon if ($self->{op} eq "movz") { # movz is pain... 143160814Ssimon sprintf "%s%s%s",$self->{op},$self->{sz},shift; 144194206Ssimon } elsif ($self->{op} =~ /^set/) { 145194206Ssimon "$self->{op}"; 146160814Ssimon } elsif ($self->{op} eq "ret") { 147238405Sjkim my $epilogue = ""; 148238405Sjkim if ($win64 && $current_function->{abi} eq "svr4") { 149238405Sjkim $epilogue = "movq 8(%rsp),%rdi\n\t" . 150238405Sjkim "movq 16(%rsp),%rsi\n\t"; 151238405Sjkim } 152238405Sjkim $epilogue . ".byte 0xf3,0xc3"; 153238405Sjkim } elsif ($self->{op} eq "call" && !$elf && $current_segment eq ".init") { 154238405Sjkim ".p2align\t3\n\t.quad"; 155160814Ssimon } else { 156160814Ssimon "$self->{op}$self->{sz}"; 157160814Ssimon } 158160814Ssimon } else { 159194206Ssimon $self->{op} =~ s/^movz/movzx/; 160160814Ssimon if ($self->{op} eq "ret") { 161160814Ssimon $self->{op} = ""; 162238405Sjkim if ($win64 && $current_function->{abi} eq "svr4") { 163238405Sjkim $self->{op} = "mov rdi,QWORD${PTR}[8+rsp]\t;WIN64 epilogue\n\t". 164238405Sjkim "mov rsi,QWORD${PTR}[16+rsp]\n\t"; 165160814Ssimon } 166160814Ssimon $self->{op} .= "DB\t0F3h,0C3h\t\t;repret"; 167238405Sjkim } elsif ($self->{op} =~ /^(pop|push)f/) { 168238405Sjkim $self->{op} .= $self->{sz}; 169238405Sjkim } elsif ($self->{op} eq "call" && $current_segment eq ".CRT\$XCU") { 170238405Sjkim $self->{op} = "\tDQ"; 171238405Sjkim } 172160814Ssimon $self->{op}; 173160814Ssimon } 174160814Ssimon } 175238405Sjkim sub mnemonic { 176238405Sjkim my $self=shift; 177238405Sjkim my $op=shift; 178238405Sjkim $self->{op}=$op if (defined($op)); 179238405Sjkim $self->{op}; 180238405Sjkim } 181160814Ssimon} 182160814Ssimon{ package const; # pick up constants, which start with $ 183160814Ssimon sub re { 184160814Ssimon my $self = shift; # single instance in enough... 185160814Ssimon local *line = shift; 186160814Ssimon undef $ret; 187160814Ssimon 188160814Ssimon if ($line =~ /^\$([^,]+)/) { 189160814Ssimon $self->{value} = $1; 190160814Ssimon $ret = $self; 191160814Ssimon $line = substr($line,@+[0]); $line =~ s/^\s+//; 192160814Ssimon } 193160814Ssimon $ret; 194160814Ssimon } 195160814Ssimon sub out { 196160814Ssimon my $self = shift; 197160814Ssimon 198238405Sjkim if ($gas) { 199194206Ssimon # Solaris /usr/ccs/bin/as can't handle multiplications 200194206Ssimon # in $self->{value} 201238405Sjkim $self->{value} =~ s/(?<![\w\$\.])(0x?[0-9a-f]+)/oct($1)/egi; 202194206Ssimon $self->{value} =~ s/([0-9]+\s*[\*\/\%]\s*[0-9]+)/eval($1)/eg; 203160814Ssimon sprintf "\$%s",$self->{value}; 204160814Ssimon } else { 205238405Sjkim $self->{value} =~ s/(0b[0-1]+)/oct($1)/eig; 206238405Sjkim $self->{value} =~ s/0x([0-9a-f]+)/0$1h/ig if ($masm); 207160814Ssimon sprintf "%s",$self->{value}; 208160814Ssimon } 209160814Ssimon } 210160814Ssimon} 211160814Ssimon{ package ea; # pick up effective addresses: expr(%reg,%reg,scale) 212160814Ssimon sub re { 213160814Ssimon my $self = shift; # single instance in enough... 214160814Ssimon local *line = shift; 215160814Ssimon undef $ret; 216160814Ssimon 217238405Sjkim # optional * ---vvv--- appears in indirect jmp/call 218238405Sjkim if ($line =~ /^(\*?)([^\(,]*)\(([%\w,]+)\)/) { 219238405Sjkim $self->{asterisk} = $1; 220238405Sjkim $self->{label} = $2; 221238405Sjkim ($self->{base},$self->{index},$self->{scale})=split(/,/,$3); 222160814Ssimon $self->{scale} = 1 if (!defined($self->{scale})); 223160814Ssimon $ret = $self; 224160814Ssimon $line = substr($line,@+[0]); $line =~ s/^\s+//; 225160814Ssimon 226238405Sjkim if ($win64 && $self->{label} =~ s/\@GOTPCREL//) { 227238405Sjkim die if (opcode->mnemonic() ne "mov"); 228238405Sjkim opcode->mnemonic("lea"); 229238405Sjkim } 230160814Ssimon $self->{base} =~ s/^%//; 231160814Ssimon $self->{index} =~ s/^%// if (defined($self->{index})); 232160814Ssimon } 233160814Ssimon $ret; 234160814Ssimon } 235160814Ssimon sub size {} 236160814Ssimon sub out { 237160814Ssimon my $self = shift; 238160814Ssimon my $sz = shift; 239160814Ssimon 240238405Sjkim $self->{label} =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei; 241238405Sjkim $self->{label} =~ s/\.L/$decor/g; 242238405Sjkim 243194206Ssimon # Silently convert all EAs to 64-bit. This is required for 244194206Ssimon # elder GNU assembler and results in more compact code, 245194206Ssimon # *but* most importantly AES module depends on this feature! 246194206Ssimon $self->{index} =~ s/^[er](.?[0-9xpi])[d]?$/r\1/; 247194206Ssimon $self->{base} =~ s/^[er](.?[0-9xpi])[d]?$/r\1/; 248194206Ssimon 249238405Sjkim # Solaris /usr/ccs/bin/as can't handle multiplications 250238405Sjkim # in $self->{label}, new gas requires sign extension... 251238405Sjkim use integer; 252238405Sjkim $self->{label} =~ s/(?<![\w\$\.])(0x?[0-9a-f]+)/oct($1)/egi; 253238405Sjkim $self->{label} =~ s/([0-9]+\s*[\*\/\%]\s*[0-9]+)/eval($1)/eg; 254238405Sjkim $self->{label} =~ s/([0-9]+)/$1<<32>>32/eg; 255160814Ssimon 256238405Sjkim if ($gas) { 257238405Sjkim $self->{label} =~ s/^___imp_/__imp__/ if ($flavour eq "mingw64"); 258238405Sjkim 259160814Ssimon if (defined($self->{index})) { 260238405Sjkim sprintf "%s%s(%s,%%%s,%d)",$self->{asterisk}, 261238405Sjkim $self->{label}, 262238405Sjkim $self->{base}?"%$self->{base}":"", 263160814Ssimon $self->{index},$self->{scale}; 264160814Ssimon } else { 265238405Sjkim sprintf "%s%s(%%%s)", $self->{asterisk},$self->{label},$self->{base}; 266160814Ssimon } 267160814Ssimon } else { 268238405Sjkim %szmap = ( b=>"BYTE$PTR", w=>"WORD$PTR", l=>"DWORD$PTR", 269238405Sjkim q=>"QWORD$PTR",o=>"OWORD$PTR",x=>"XMMWORD$PTR" ); 270160814Ssimon 271160814Ssimon $self->{label} =~ s/\./\$/g; 272238405Sjkim $self->{label} =~ s/(?<![\w\$\.])0x([0-9a-f]+)/0$1h/ig; 273160814Ssimon $self->{label} = "($self->{label})" if ($self->{label} =~ /[\*\+\-\/]/); 274238405Sjkim $sz="q" if ($self->{asterisk} || opcode->mnemonic() eq "movq"); 275238405Sjkim $sz="l" if (opcode->mnemonic() eq "movd"); 276160814Ssimon 277160814Ssimon if (defined($self->{index})) { 278238405Sjkim sprintf "%s[%s%s*%d%s]",$szmap{$sz}, 279238405Sjkim $self->{label}?"$self->{label}+":"", 280160814Ssimon $self->{index},$self->{scale}, 281238405Sjkim $self->{base}?"+$self->{base}":""; 282194206Ssimon } elsif ($self->{base} eq "rip") { 283238405Sjkim sprintf "%s[%s]",$szmap{$sz},$self->{label}; 284160814Ssimon } else { 285238405Sjkim sprintf "%s[%s%s]",$szmap{$sz}, 286238405Sjkim $self->{label}?"$self->{label}+":"", 287238405Sjkim $self->{base}; 288160814Ssimon } 289160814Ssimon } 290160814Ssimon } 291160814Ssimon} 292160814Ssimon{ package register; # pick up registers, which start with %. 293160814Ssimon sub re { 294160814Ssimon my $class = shift; # muliple instances... 295160814Ssimon my $self = {}; 296160814Ssimon local *line = shift; 297160814Ssimon undef $ret; 298160814Ssimon 299238405Sjkim # optional * ---vvv--- appears in indirect jmp/call 300238405Sjkim if ($line =~ /^(\*?)%(\w+)/) { 301160814Ssimon bless $self,$class; 302238405Sjkim $self->{asterisk} = $1; 303238405Sjkim $self->{value} = $2; 304160814Ssimon $ret = $self; 305160814Ssimon $line = substr($line,@+[0]); $line =~ s/^\s+//; 306160814Ssimon } 307160814Ssimon $ret; 308160814Ssimon } 309160814Ssimon sub size { 310160814Ssimon my $self = shift; 311160814Ssimon undef $ret; 312160814Ssimon 313160814Ssimon if ($self->{value} =~ /^r[\d]+b$/i) { $ret="b"; } 314160814Ssimon elsif ($self->{value} =~ /^r[\d]+w$/i) { $ret="w"; } 315160814Ssimon elsif ($self->{value} =~ /^r[\d]+d$/i) { $ret="l"; } 316160814Ssimon elsif ($self->{value} =~ /^r[\w]+$/i) { $ret="q"; } 317160814Ssimon elsif ($self->{value} =~ /^[a-d][hl]$/i){ $ret="b"; } 318160814Ssimon elsif ($self->{value} =~ /^[\w]{2}l$/i) { $ret="b"; } 319160814Ssimon elsif ($self->{value} =~ /^[\w]{2}$/i) { $ret="w"; } 320160814Ssimon elsif ($self->{value} =~ /^e[a-z]{2}$/i){ $ret="l"; } 321160814Ssimon 322160814Ssimon $ret; 323160814Ssimon } 324160814Ssimon sub out { 325160814Ssimon my $self = shift; 326238405Sjkim if ($gas) { sprintf "%s%%%s",$self->{asterisk},$self->{value}; } 327238405Sjkim else { $self->{value}; } 328160814Ssimon } 329160814Ssimon} 330160814Ssimon{ package label; # pick up labels, which end with : 331160814Ssimon sub re { 332160814Ssimon my $self = shift; # single instance is enough... 333160814Ssimon local *line = shift; 334160814Ssimon undef $ret; 335160814Ssimon 336238405Sjkim if ($line =~ /(^[\.\w]+)\:/) { 337160814Ssimon $self->{value} = $1; 338160814Ssimon $ret = $self; 339160814Ssimon $line = substr($line,@+[0]); $line =~ s/^\s+//; 340160814Ssimon 341238405Sjkim $self->{value} =~ s/^\.L/$decor/; 342160814Ssimon } 343160814Ssimon $ret; 344160814Ssimon } 345160814Ssimon sub out { 346160814Ssimon my $self = shift; 347160814Ssimon 348238405Sjkim if ($gas) { 349238405Sjkim my $func = ($globals{$self->{value}} or $self->{value}) . ":"; 350238405Sjkim if ($win64 && 351238405Sjkim $current_function->{name} eq $self->{value} && 352238405Sjkim $current_function->{abi} eq "svr4") { 353238405Sjkim $func .= "\n"; 354238405Sjkim $func .= " movq %rdi,8(%rsp)\n"; 355238405Sjkim $func .= " movq %rsi,16(%rsp)\n"; 356238405Sjkim $func .= " movq %rsp,%rax\n"; 357238405Sjkim $func .= "${decor}SEH_begin_$current_function->{name}:\n"; 358238405Sjkim my $narg = $current_function->{narg}; 359238405Sjkim $narg=6 if (!defined($narg)); 360238405Sjkim $func .= " movq %rcx,%rdi\n" if ($narg>0); 361238405Sjkim $func .= " movq %rdx,%rsi\n" if ($narg>1); 362238405Sjkim $func .= " movq %r8,%rdx\n" if ($narg>2); 363238405Sjkim $func .= " movq %r9,%rcx\n" if ($narg>3); 364238405Sjkim $func .= " movq 40(%rsp),%r8\n" if ($narg>4); 365238405Sjkim $func .= " movq 48(%rsp),%r9\n" if ($narg>5); 366238405Sjkim } 367238405Sjkim $func; 368238405Sjkim } elsif ($self->{value} ne "$current_function->{name}") { 369238405Sjkim $self->{value} .= ":" if ($masm && $ret!~m/^\$/); 370238405Sjkim $self->{value} . ":"; 371238405Sjkim } elsif ($win64 && $current_function->{abi} eq "svr4") { 372238405Sjkim my $func = "$current_function->{name}" . 373238405Sjkim ($nasm ? ":" : "\tPROC $current_function->{scope}") . 374238405Sjkim "\n"; 375238405Sjkim $func .= " mov QWORD${PTR}[8+rsp],rdi\t;WIN64 prologue\n"; 376238405Sjkim $func .= " mov QWORD${PTR}[16+rsp],rsi\n"; 377238405Sjkim $func .= " mov rax,rsp\n"; 378238405Sjkim $func .= "${decor}SEH_begin_$current_function->{name}:"; 379238405Sjkim $func .= ":" if ($masm); 380238405Sjkim $func .= "\n"; 381160814Ssimon my $narg = $current_function->{narg}; 382160814Ssimon $narg=6 if (!defined($narg)); 383160814Ssimon $func .= " mov rdi,rcx\n" if ($narg>0); 384160814Ssimon $func .= " mov rsi,rdx\n" if ($narg>1); 385160814Ssimon $func .= " mov rdx,r8\n" if ($narg>2); 386160814Ssimon $func .= " mov rcx,r9\n" if ($narg>3); 387238405Sjkim $func .= " mov r8,QWORD${PTR}[40+rsp]\n" if ($narg>4); 388238405Sjkim $func .= " mov r9,QWORD${PTR}[48+rsp]\n" if ($narg>5); 389160814Ssimon $func .= "\n"; 390160814Ssimon } else { 391238405Sjkim "$current_function->{name}". 392238405Sjkim ($nasm ? ":" : "\tPROC $current_function->{scope}"); 393160814Ssimon } 394160814Ssimon } 395160814Ssimon} 396160814Ssimon{ package expr; # pick up expressioins 397160814Ssimon sub re { 398160814Ssimon my $self = shift; # single instance is enough... 399160814Ssimon local *line = shift; 400160814Ssimon undef $ret; 401160814Ssimon 402160814Ssimon if ($line =~ /(^[^,]+)/) { 403160814Ssimon $self->{value} = $1; 404160814Ssimon $ret = $self; 405160814Ssimon $line = substr($line,@+[0]); $line =~ s/^\s+//; 406160814Ssimon 407238405Sjkim $self->{value} =~ s/\@PLT// if (!$elf); 408238405Sjkim $self->{value} =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei; 409238405Sjkim $self->{value} =~ s/\.L/$decor/g; 410160814Ssimon } 411160814Ssimon $ret; 412160814Ssimon } 413160814Ssimon sub out { 414160814Ssimon my $self = shift; 415238405Sjkim if ($nasm && opcode->mnemonic()=~m/^j/) { 416238405Sjkim "NEAR ".$self->{value}; 417238405Sjkim } else { 418238405Sjkim $self->{value}; 419238405Sjkim } 420160814Ssimon } 421160814Ssimon} 422160814Ssimon{ package directive; # pick up directives, which start with . 423160814Ssimon sub re { 424160814Ssimon my $self = shift; # single instance is enough... 425160814Ssimon local *line = shift; 426160814Ssimon undef $ret; 427160814Ssimon my $dir; 428160814Ssimon my %opcode = # lea 2f-1f(%rip),%dst; 1: nop; 2: 429160814Ssimon ( "%rax"=>0x01058d48, "%rcx"=>0x010d8d48, 430160814Ssimon "%rdx"=>0x01158d48, "%rbx"=>0x011d8d48, 431160814Ssimon "%rsp"=>0x01258d48, "%rbp"=>0x012d8d48, 432160814Ssimon "%rsi"=>0x01358d48, "%rdi"=>0x013d8d48, 433160814Ssimon "%r8" =>0x01058d4c, "%r9" =>0x010d8d4c, 434160814Ssimon "%r10"=>0x01158d4c, "%r11"=>0x011d8d4c, 435160814Ssimon "%r12"=>0x01258d4c, "%r13"=>0x012d8d4c, 436160814Ssimon "%r14"=>0x01358d4c, "%r15"=>0x013d8d4c ); 437160814Ssimon 438160814Ssimon if ($line =~ /^\s*(\.\w+)/) { 439160814Ssimon $dir = $1; 440160814Ssimon $ret = $self; 441160814Ssimon undef $self->{value}; 442160814Ssimon $line = substr($line,@+[0]); $line =~ s/^\s+//; 443238405Sjkim 444160814Ssimon SWITCH: for ($dir) { 445238405Sjkim /\.picmeup/ && do { if ($line =~ /(%r[\w]+)/i) { 446238405Sjkim $dir="\t.long"; 447238405Sjkim $line=sprintf "0x%x,0x90000000",$opcode{$1}; 448238405Sjkim } 449160814Ssimon last; 450160814Ssimon }; 451238405Sjkim /\.global|\.globl|\.extern/ 452238405Sjkim && do { $globals{$line} = $prefix . $line; 453238405Sjkim $line = $globals{$line} if ($prefix); 454238405Sjkim last; 455238405Sjkim }; 456160814Ssimon /\.type/ && do { ($sym,$type,$narg) = split(',',$line); 457160814Ssimon if ($type eq "\@function") { 458160814Ssimon undef $current_function; 459160814Ssimon $current_function->{name} = $sym; 460160814Ssimon $current_function->{abi} = "svr4"; 461160814Ssimon $current_function->{narg} = $narg; 462238405Sjkim $current_function->{scope} = defined($globals{$sym})?"PUBLIC":"PRIVATE"; 463160814Ssimon } elsif ($type eq "\@abi-omnipotent") { 464160814Ssimon undef $current_function; 465160814Ssimon $current_function->{name} = $sym; 466238405Sjkim $current_function->{scope} = defined($globals{$sym})?"PUBLIC":"PRIVATE"; 467160814Ssimon } 468238405Sjkim $line =~ s/\@abi\-omnipotent/\@function/; 469238405Sjkim $line =~ s/\@function.*/\@function/; 470160814Ssimon last; 471160814Ssimon }; 472238405Sjkim /\.asciz/ && do { if ($line =~ /^"(.*)"$/) { 473238405Sjkim $dir = ".byte"; 474238405Sjkim $line = join(",",unpack("C*",$1),0); 475238405Sjkim } 476238405Sjkim last; 477238405Sjkim }; 478238405Sjkim /\.rva|\.long|\.quad/ 479238405Sjkim && do { $line =~ s/([_a-z][_a-z0-9]*)/$globals{$1} or $1/gei; 480238405Sjkim $line =~ s/\.L/$decor/g; 481238405Sjkim last; 482238405Sjkim }; 483238405Sjkim } 484238405Sjkim 485238405Sjkim if ($gas) { 486238405Sjkim $self->{value} = $dir . "\t" . $line; 487238405Sjkim 488238405Sjkim if ($dir =~ /\.extern/) { 489238405Sjkim $self->{value} = ""; # swallow extern 490238405Sjkim } elsif (!$elf && $dir =~ /\.type/) { 491238405Sjkim $self->{value} = ""; 492238405Sjkim $self->{value} = ".def\t" . ($globals{$1} or $1) . ";\t" . 493238405Sjkim (defined($globals{$1})?".scl 2;":".scl 3;") . 494238405Sjkim "\t.type 32;\t.endef" 495238405Sjkim if ($win64 && $line =~ /([^,]+),\@function/); 496238405Sjkim } elsif (!$elf && $dir =~ /\.size/) { 497238405Sjkim $self->{value} = ""; 498238405Sjkim if (defined($current_function)) { 499238405Sjkim $self->{value} .= "${decor}SEH_end_$current_function->{name}:" 500238405Sjkim if ($win64 && $current_function->{abi} eq "svr4"); 501238405Sjkim undef $current_function; 502238405Sjkim } 503238405Sjkim } elsif (!$elf && $dir =~ /\.align/) { 504238405Sjkim $self->{value} = ".p2align\t" . (log($line)/log(2)); 505238405Sjkim } elsif ($dir eq ".section") { 506238405Sjkim $current_segment=$line; 507238405Sjkim if (!$elf && $current_segment eq ".init") { 508238405Sjkim if ($flavour eq "macosx") { $self->{value} = ".mod_init_func"; } 509238405Sjkim elsif ($flavour eq "mingw64") { $self->{value} = ".section\t.ctors"; } 510238405Sjkim } 511238405Sjkim } elsif ($dir =~ /\.(text|data)/) { 512238405Sjkim $current_segment=".$1"; 513238405Sjkim } elsif ($dir =~ /\.hidden/) { 514238405Sjkim if ($flavour eq "macosx") { $self->{value} = ".private_extern\t$prefix$line"; } 515238405Sjkim elsif ($flavour eq "mingw64") { $self->{value} = ""; } 516238405Sjkim } elsif ($dir =~ /\.comm/) { 517238405Sjkim $self->{value} = "$dir\t$prefix$line"; 518238405Sjkim $self->{value} =~ s|,([0-9]+),([0-9]+)$|",$1,".log($2)/log(2)|e if ($flavour eq "macosx"); 519238405Sjkim } 520238405Sjkim $line = ""; 521238405Sjkim return $self; 522238405Sjkim } 523238405Sjkim 524238405Sjkim # non-gas case or nasm/masm 525238405Sjkim SWITCH: for ($dir) { 526238405Sjkim /\.text/ && do { my $v=undef; 527238405Sjkim if ($nasm) { 528238405Sjkim $v="section .text code align=64\n"; 529238405Sjkim } else { 530238405Sjkim $v="$current_segment\tENDS\n" if ($current_segment); 531238405Sjkim $current_segment = ".text\$"; 532238405Sjkim $v.="$current_segment\tSEGMENT "; 533238405Sjkim $v.=$masm>=$masmref ? "ALIGN(64)" : "PAGE"; 534238405Sjkim $v.=" 'CODE'"; 535238405Sjkim } 536238405Sjkim $self->{value} = $v; 537238405Sjkim last; 538238405Sjkim }; 539238405Sjkim /\.data/ && do { my $v=undef; 540238405Sjkim if ($nasm) { 541238405Sjkim $v="section .data data align=8\n"; 542238405Sjkim } else { 543238405Sjkim $v="$current_segment\tENDS\n" if ($current_segment); 544238405Sjkim $current_segment = "_DATA"; 545238405Sjkim $v.="$current_segment\tSEGMENT"; 546238405Sjkim } 547238405Sjkim $self->{value} = $v; 548238405Sjkim last; 549238405Sjkim }; 550238405Sjkim /\.section/ && do { my $v=undef; 551238405Sjkim $line =~ s/([^,]*).*/$1/; 552238405Sjkim $line = ".CRT\$XCU" if ($line eq ".init"); 553238405Sjkim if ($nasm) { 554238405Sjkim $v="section $line"; 555238405Sjkim if ($line=~/\.([px])data/) { 556238405Sjkim $v.=" rdata align="; 557238405Sjkim $v.=$1 eq "p"? 4 : 8; 558238405Sjkim } elsif ($line=~/\.CRT\$/i) { 559238405Sjkim $v.=" rdata align=8"; 560238405Sjkim } 561238405Sjkim } else { 562238405Sjkim $v="$current_segment\tENDS\n" if ($current_segment); 563238405Sjkim $v.="$line\tSEGMENT"; 564238405Sjkim if ($line=~/\.([px])data/) { 565238405Sjkim $v.=" READONLY"; 566238405Sjkim $v.=" ALIGN(".($1 eq "p" ? 4 : 8).")" if ($masm>=$masmref); 567238405Sjkim } elsif ($line=~/\.CRT\$/i) { 568238405Sjkim $v.=" READONLY "; 569238405Sjkim $v.=$masm>=$masmref ? "ALIGN(8)" : "DWORD"; 570238405Sjkim } 571238405Sjkim } 572238405Sjkim $current_segment = $line; 573238405Sjkim $self->{value} = $v; 574238405Sjkim last; 575238405Sjkim }; 576238405Sjkim /\.extern/ && do { $self->{value} = "EXTERN\t".$line; 577238405Sjkim $self->{value} .= ":NEAR" if ($masm); 578238405Sjkim last; 579238405Sjkim }; 580238405Sjkim /\.globl|.global/ 581238405Sjkim && do { $self->{value} = $masm?"PUBLIC":"global"; 582238405Sjkim $self->{value} .= "\t".$line; 583238405Sjkim last; 584238405Sjkim }; 585160814Ssimon /\.size/ && do { if (defined($current_function)) { 586238405Sjkim undef $self->{value}; 587238405Sjkim if ($current_function->{abi} eq "svr4") { 588238405Sjkim $self->{value}="${decor}SEH_end_$current_function->{name}:"; 589238405Sjkim $self->{value}.=":\n" if($masm); 590238405Sjkim } 591238405Sjkim $self->{value}.="$current_function->{name}\tENDP" if($masm && $current_function->{name}); 592160814Ssimon undef $current_function; 593160814Ssimon } 594160814Ssimon last; 595160814Ssimon }; 596160814Ssimon /\.align/ && do { $self->{value} = "ALIGN\t".$line; last; }; 597238405Sjkim /\.(value|long|rva|quad)/ 598238405Sjkim && do { my $sz = substr($1,0,1); 599238405Sjkim my @arr = split(/,\s*/,$line); 600160814Ssimon my $last = pop(@arr); 601194206Ssimon my $conv = sub { my $var=shift; 602238405Sjkim $var=~s/^(0b[0-1]+)/oct($1)/eig; 603238405Sjkim $var=~s/^0x([0-9a-f]+)/0$1h/ig if ($masm); 604238405Sjkim if ($sz eq "D" && ($current_segment=~/.[px]data/ || $dir eq ".rva")) 605238405Sjkim { $var=~s/([_a-z\$\@][_a-z0-9\$\@]*)/$nasm?"$1 wrt ..imagebase":"imagerel $1"/egi; } 606238405Sjkim $var; 607194206Ssimon }; 608160814Ssimon 609238405Sjkim $sz =~ tr/bvlrq/BWDDQ/; 610160814Ssimon $self->{value} = "\tD$sz\t"; 611194206Ssimon for (@arr) { $self->{value} .= &$conv($_).","; } 612194206Ssimon $self->{value} .= &$conv($last); 613160814Ssimon last; 614160814Ssimon }; 615238405Sjkim /\.byte/ && do { my @str=split(/,\s*/,$line); 616238405Sjkim map(s/(0b[0-1]+)/oct($1)/eig,@str); 617238405Sjkim map(s/0x([0-9a-f]+)/0$1h/ig,@str) if ($masm); 618238405Sjkim while ($#str>15) { 619238405Sjkim $self->{value}.="DB\t" 620238405Sjkim .join(",",@str[0..15])."\n"; 621238405Sjkim foreach (0..15) { shift @str; } 622238405Sjkim } 623238405Sjkim $self->{value}.="DB\t" 624238405Sjkim .join(",",@str) if (@str); 625160814Ssimon last; 626160814Ssimon }; 627238405Sjkim /\.comm/ && do { my @str=split(/,\s*/,$line); 628238405Sjkim my $v=undef; 629238405Sjkim if ($nasm) { 630238405Sjkim $v.="common $prefix@str[0] @str[1]"; 631238405Sjkim } else { 632238405Sjkim $v="$current_segment\tENDS\n" if ($current_segment); 633238405Sjkim $current_segment = "_DATA"; 634238405Sjkim $v.="$current_segment\tSEGMENT\n"; 635238405Sjkim $v.="COMM @str[0]:DWORD:".@str[1]/4; 636194206Ssimon } 637238405Sjkim $self->{value} = $v; 638194206Ssimon last; 639194206Ssimon }; 640160814Ssimon } 641160814Ssimon $line = ""; 642160814Ssimon } 643160814Ssimon 644160814Ssimon $ret; 645160814Ssimon } 646160814Ssimon sub out { 647160814Ssimon my $self = shift; 648160814Ssimon $self->{value}; 649160814Ssimon } 650160814Ssimon} 651160814Ssimon 652238405Sjkimsub rex { 653238405Sjkim local *opcode=shift; 654238405Sjkim my ($dst,$src,$rex)=@_; 655238405Sjkim 656238405Sjkim $rex|=0x04 if($dst>=8); 657238405Sjkim $rex|=0x01 if($src>=8); 658238405Sjkim push @opcode,($rex|0x40) if ($rex); 659238405Sjkim} 660238405Sjkim 661238405Sjkim# older gas and ml64 don't handle SSE>2 instructions 662238405Sjkimmy %regrm = ( "%eax"=>0, "%ecx"=>1, "%edx"=>2, "%ebx"=>3, 663238405Sjkim "%esp"=>4, "%ebp"=>5, "%esi"=>6, "%edi"=>7 ); 664238405Sjkim 665238405Sjkimmy $movq = sub { # elderly gas can't handle inter-register movq 666238405Sjkim my $arg = shift; 667238405Sjkim my @opcode=(0x66); 668238405Sjkim if ($arg =~ /%xmm([0-9]+),\s*%r(\w+)/) { 669238405Sjkim my ($src,$dst)=($1,$2); 670238405Sjkim if ($dst !~ /[0-9]+/) { $dst = $regrm{"%e$dst"}; } 671238405Sjkim rex(\@opcode,$src,$dst,0x8); 672238405Sjkim push @opcode,0x0f,0x7e; 673238405Sjkim push @opcode,0xc0|(($src&7)<<3)|($dst&7); # ModR/M 674238405Sjkim @opcode; 675238405Sjkim } elsif ($arg =~ /%r(\w+),\s*%xmm([0-9]+)/) { 676238405Sjkim my ($src,$dst)=($2,$1); 677238405Sjkim if ($dst !~ /[0-9]+/) { $dst = $regrm{"%e$dst"}; } 678238405Sjkim rex(\@opcode,$src,$dst,0x8); 679238405Sjkim push @opcode,0x0f,0x6e; 680238405Sjkim push @opcode,0xc0|(($src&7)<<3)|($dst&7); # ModR/M 681238405Sjkim @opcode; 682238405Sjkim } else { 683238405Sjkim (); 684238405Sjkim } 685238405Sjkim}; 686238405Sjkim 687238405Sjkimmy $pextrd = sub { 688238405Sjkim if (shift =~ /\$([0-9]+),\s*%xmm([0-9]+),\s*(%\w+)/) { 689238405Sjkim my @opcode=(0x66); 690238405Sjkim $imm=$1; 691238405Sjkim $src=$2; 692238405Sjkim $dst=$3; 693238405Sjkim if ($dst =~ /%r([0-9]+)d/) { $dst = $1; } 694238405Sjkim elsif ($dst =~ /%e/) { $dst = $regrm{$dst}; } 695238405Sjkim rex(\@opcode,$src,$dst); 696238405Sjkim push @opcode,0x0f,0x3a,0x16; 697238405Sjkim push @opcode,0xc0|(($src&7)<<3)|($dst&7); # ModR/M 698238405Sjkim push @opcode,$imm; 699238405Sjkim @opcode; 700238405Sjkim } else { 701238405Sjkim (); 702238405Sjkim } 703238405Sjkim}; 704238405Sjkim 705238405Sjkimmy $pinsrd = sub { 706238405Sjkim if (shift =~ /\$([0-9]+),\s*(%\w+),\s*%xmm([0-9]+)/) { 707238405Sjkim my @opcode=(0x66); 708238405Sjkim $imm=$1; 709238405Sjkim $src=$2; 710238405Sjkim $dst=$3; 711238405Sjkim if ($src =~ /%r([0-9]+)/) { $src = $1; } 712238405Sjkim elsif ($src =~ /%e/) { $src = $regrm{$src}; } 713238405Sjkim rex(\@opcode,$dst,$src); 714238405Sjkim push @opcode,0x0f,0x3a,0x22; 715238405Sjkim push @opcode,0xc0|(($dst&7)<<3)|($src&7); # ModR/M 716238405Sjkim push @opcode,$imm; 717238405Sjkim @opcode; 718238405Sjkim } else { 719238405Sjkim (); 720238405Sjkim } 721238405Sjkim}; 722238405Sjkim 723238405Sjkimmy $pshufb = sub { 724238405Sjkim if (shift =~ /%xmm([0-9]+),\s*%xmm([0-9]+)/) { 725238405Sjkim my @opcode=(0x66); 726238405Sjkim rex(\@opcode,$2,$1); 727238405Sjkim push @opcode,0x0f,0x38,0x00; 728238405Sjkim push @opcode,0xc0|($1&7)|(($2&7)<<3); # ModR/M 729238405Sjkim @opcode; 730238405Sjkim } else { 731238405Sjkim (); 732238405Sjkim } 733238405Sjkim}; 734238405Sjkim 735238405Sjkimmy $palignr = sub { 736238405Sjkim if (shift =~ /\$([0-9]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) { 737238405Sjkim my @opcode=(0x66); 738238405Sjkim rex(\@opcode,$3,$2); 739238405Sjkim push @opcode,0x0f,0x3a,0x0f; 740238405Sjkim push @opcode,0xc0|($2&7)|(($3&7)<<3); # ModR/M 741238405Sjkim push @opcode,$1; 742238405Sjkim @opcode; 743238405Sjkim } else { 744238405Sjkim (); 745238405Sjkim } 746238405Sjkim}; 747238405Sjkim 748238405Sjkimmy $pclmulqdq = sub { 749238405Sjkim if (shift =~ /\$([x0-9a-f]+),\s*%xmm([0-9]+),\s*%xmm([0-9]+)/) { 750238405Sjkim my @opcode=(0x66); 751238405Sjkim rex(\@opcode,$3,$2); 752238405Sjkim push @opcode,0x0f,0x3a,0x44; 753238405Sjkim push @opcode,0xc0|($2&7)|(($3&7)<<3); # ModR/M 754238405Sjkim my $c=$1; 755238405Sjkim push @opcode,$c=~/^0/?oct($c):$c; 756238405Sjkim @opcode; 757238405Sjkim } else { 758238405Sjkim (); 759238405Sjkim } 760238405Sjkim}; 761238405Sjkim 762238405Sjkimmy $rdrand = sub { 763238405Sjkim if (shift =~ /%[er](\w+)/) { 764238405Sjkim my @opcode=(); 765238405Sjkim my $dst=$1; 766238405Sjkim if ($dst !~ /[0-9]+/) { $dst = $regrm{"%e$dst"}; } 767238405Sjkim rex(\@opcode,0,$1,8); 768238405Sjkim push @opcode,0x0f,0xc7,0xf0|($dst&7); 769238405Sjkim @opcode; 770238405Sjkim } else { 771238405Sjkim (); 772238405Sjkim } 773238405Sjkim}; 774238405Sjkim 775238405Sjkimif ($nasm) { 776238405Sjkim print <<___; 777238405Sjkimdefault rel 778238405Sjkim%define XMMWORD 779238405Sjkim___ 780238405Sjkim} elsif ($masm) { 781238405Sjkim print <<___; 782238405SjkimOPTION DOTNAME 783238405Sjkim___ 784238405Sjkim} 785160814Ssimonwhile($line=<>) { 786160814Ssimon 787160814Ssimon chomp($line); 788160814Ssimon 789160814Ssimon $line =~ s|[#!].*$||; # get rid of asm-style comments... 790160814Ssimon $line =~ s|/\*.*\*/||; # ... and C-style comments... 791160814Ssimon $line =~ s|^\s+||; # ... and skip white spaces in beginning 792160814Ssimon 793160814Ssimon undef $label; 794160814Ssimon undef $opcode; 795238405Sjkim undef @args; 796160814Ssimon 797160814Ssimon if ($label=label->re(\$line)) { print $label->out(); } 798160814Ssimon 799160814Ssimon if (directive->re(\$line)) { 800160814Ssimon printf "%s",directive->out(); 801238405Sjkim } elsif ($opcode=opcode->re(\$line)) { 802238405Sjkim my $asm = eval("\$".$opcode->mnemonic()); 803238405Sjkim undef @bytes; 804238405Sjkim 805238405Sjkim if ((ref($asm) eq 'CODE') && scalar(@bytes=&$asm($line))) { 806238405Sjkim print $gas?".byte\t":"DB\t",join(',',@bytes),"\n"; 807238405Sjkim next; 808238405Sjkim } 809160814Ssimon 810238405Sjkim ARGUMENT: while (1) { 811238405Sjkim my $arg; 812160814Ssimon 813238405Sjkim if ($arg=register->re(\$line)) { opcode->size($arg->size()); } 814238405Sjkim elsif ($arg=const->re(\$line)) { } 815238405Sjkim elsif ($arg=ea->re(\$line)) { } 816238405Sjkim elsif ($arg=expr->re(\$line)) { } 817238405Sjkim else { last ARGUMENT; } 818160814Ssimon 819238405Sjkim push @args,$arg; 820160814Ssimon 821238405Sjkim last ARGUMENT if ($line !~ /^,/); 822160814Ssimon 823238405Sjkim $line =~ s/^,\s*//; 824160814Ssimon } # ARGUMENT: 825160814Ssimon 826238405Sjkim if ($#args>=0) { 827238405Sjkim my $insn; 828238405Sjkim my $sz=opcode->size(); 829160814Ssimon 830238405Sjkim if ($gas) { 831238405Sjkim $insn = $opcode->out($#args>=1?$args[$#args]->size():$sz); 832238405Sjkim @args = map($_->out($sz),@args); 833238405Sjkim printf "\t%s\t%s",$insn,join(",",@args); 834160814Ssimon } else { 835238405Sjkim $insn = $opcode->out(); 836238405Sjkim foreach (@args) { 837238405Sjkim my $arg = $_->out(); 838238405Sjkim # $insn.=$sz compensates for movq, pinsrw, ... 839238405Sjkim if ($arg =~ /^xmm[0-9]+$/) { $insn.=$sz; $sz="x" if(!$sz); last; } 840238405Sjkim if ($arg =~ /^mm[0-9]+$/) { $insn.=$sz; $sz="q" if(!$sz); last; } 841238405Sjkim } 842238405Sjkim @args = reverse(@args); 843238405Sjkim undef $sz if ($nasm && $opcode->mnemonic() eq "lea"); 844238405Sjkim printf "\t%s\t%s",$insn,join(",",map($_->out($sz),@args)); 845160814Ssimon } 846160814Ssimon } else { 847160814Ssimon printf "\t%s",$opcode->out(); 848160814Ssimon } 849160814Ssimon } 850160814Ssimon 851160814Ssimon print $line,"\n"; 852160814Ssimon} 853160814Ssimon 854238405Sjkimprint "\n$current_segment\tENDS\n" if ($current_segment && $masm); 855238405Sjkimprint "END\n" if ($masm); 856160814Ssimon 857160814Ssimonclose STDOUT; 858160814Ssimon 859238405Sjkim################################################# 860160814Ssimon# Cross-reference x86_64 ABI "card" 861160814Ssimon# 862160814Ssimon# Unix Win64 863160814Ssimon# %rax * * 864160814Ssimon# %rbx - - 865160814Ssimon# %rcx #4 #1 866160814Ssimon# %rdx #3 #2 867160814Ssimon# %rsi #2 - 868160814Ssimon# %rdi #1 - 869160814Ssimon# %rbp - - 870160814Ssimon# %rsp - - 871160814Ssimon# %r8 #5 #3 872160814Ssimon# %r9 #6 #4 873160814Ssimon# %r10 * * 874160814Ssimon# %r11 * * 875160814Ssimon# %r12 - - 876160814Ssimon# %r13 - - 877160814Ssimon# %r14 - - 878160814Ssimon# %r15 - - 879160814Ssimon# 880160814Ssimon# (*) volatile register 881160814Ssimon# (-) preserved by callee 882160814Ssimon# (#) Nth argument, volatile 883160814Ssimon# 884160814Ssimon# In Unix terms top of stack is argument transfer area for arguments 885160814Ssimon# which could not be accomodated in registers. Or in other words 7th 886160814Ssimon# [integer] argument resides at 8(%rsp) upon function entry point. 887160814Ssimon# 128 bytes above %rsp constitute a "red zone" which is not touched 888160814Ssimon# by signal handlers and can be used as temporal storage without 889160814Ssimon# allocating a frame. 890160814Ssimon# 891160814Ssimon# In Win64 terms N*8 bytes on top of stack is argument transfer area, 892160814Ssimon# which belongs to/can be overwritten by callee. N is the number of 893160814Ssimon# arguments passed to callee, *but* not less than 4! This means that 894160814Ssimon# upon function entry point 5th argument resides at 40(%rsp), as well 895160814Ssimon# as that 32 bytes from 8(%rsp) can always be used as temporal 896194206Ssimon# storage [without allocating a frame]. One can actually argue that 897194206Ssimon# one can assume a "red zone" above stack pointer under Win64 as well. 898194206Ssimon# Point is that at apparently no occasion Windows kernel would alter 899194206Ssimon# the area above user stack pointer in true asynchronous manner... 900160814Ssimon# 901160814Ssimon# All the above means that if assembler programmer adheres to Unix 902160814Ssimon# register and stack layout, but disregards the "red zone" existense, 903160814Ssimon# it's possible to use following prologue and epilogue to "gear" from 904160814Ssimon# Unix to Win64 ABI in leaf functions with not more than 6 arguments. 905160814Ssimon# 906160814Ssimon# omnipotent_function: 907160814Ssimon# ifdef WIN64 908160814Ssimon# movq %rdi,8(%rsp) 909160814Ssimon# movq %rsi,16(%rsp) 910160814Ssimon# movq %rcx,%rdi ; if 1st argument is actually present 911160814Ssimon# movq %rdx,%rsi ; if 2nd argument is actually ... 912160814Ssimon# movq %r8,%rdx ; if 3rd argument is ... 913160814Ssimon# movq %r9,%rcx ; if 4th argument ... 914160814Ssimon# movq 40(%rsp),%r8 ; if 5th ... 915160814Ssimon# movq 48(%rsp),%r9 ; if 6th ... 916160814Ssimon# endif 917160814Ssimon# ... 918160814Ssimon# ifdef WIN64 919160814Ssimon# movq 8(%rsp),%rdi 920160814Ssimon# movq 16(%rsp),%rsi 921160814Ssimon# endif 922160814Ssimon# ret 923238405Sjkim# 924238405Sjkim################################################# 925238405Sjkim# Win64 SEH, Structured Exception Handling. 926238405Sjkim# 927238405Sjkim# Unlike on Unix systems(*) lack of Win64 stack unwinding information 928238405Sjkim# has undesired side-effect at run-time: if an exception is raised in 929238405Sjkim# assembler subroutine such as those in question (basically we're 930238405Sjkim# referring to segmentation violations caused by malformed input 931238405Sjkim# parameters), the application is briskly terminated without invoking 932238405Sjkim# any exception handlers, most notably without generating memory dump 933238405Sjkim# or any user notification whatsoever. This poses a problem. It's 934238405Sjkim# possible to address it by registering custom language-specific 935238405Sjkim# handler that would restore processor context to the state at 936238405Sjkim# subroutine entry point and return "exception is not handled, keep 937238405Sjkim# unwinding" code. Writing such handler can be a challenge... But it's 938238405Sjkim# doable, though requires certain coding convention. Consider following 939238405Sjkim# snippet: 940238405Sjkim# 941238405Sjkim# .type function,@function 942238405Sjkim# function: 943238405Sjkim# movq %rsp,%rax # copy rsp to volatile register 944238405Sjkim# pushq %r15 # save non-volatile registers 945238405Sjkim# pushq %rbx 946238405Sjkim# pushq %rbp 947238405Sjkim# movq %rsp,%r11 948238405Sjkim# subq %rdi,%r11 # prepare [variable] stack frame 949238405Sjkim# andq $-64,%r11 950238405Sjkim# movq %rax,0(%r11) # check for exceptions 951238405Sjkim# movq %r11,%rsp # allocate [variable] stack frame 952238405Sjkim# movq %rax,0(%rsp) # save original rsp value 953238405Sjkim# magic_point: 954238405Sjkim# ... 955238405Sjkim# movq 0(%rsp),%rcx # pull original rsp value 956238405Sjkim# movq -24(%rcx),%rbp # restore non-volatile registers 957238405Sjkim# movq -16(%rcx),%rbx 958238405Sjkim# movq -8(%rcx),%r15 959238405Sjkim# movq %rcx,%rsp # restore original rsp 960238405Sjkim# ret 961238405Sjkim# .size function,.-function 962238405Sjkim# 963238405Sjkim# The key is that up to magic_point copy of original rsp value remains 964238405Sjkim# in chosen volatile register and no non-volatile register, except for 965238405Sjkim# rsp, is modified. While past magic_point rsp remains constant till 966238405Sjkim# the very end of the function. In this case custom language-specific 967238405Sjkim# exception handler would look like this: 968238405Sjkim# 969238405Sjkim# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame, 970238405Sjkim# CONTEXT *context,DISPATCHER_CONTEXT *disp) 971238405Sjkim# { ULONG64 *rsp = (ULONG64 *)context->Rax; 972238405Sjkim# if (context->Rip >= magic_point) 973238405Sjkim# { rsp = ((ULONG64 **)context->Rsp)[0]; 974238405Sjkim# context->Rbp = rsp[-3]; 975238405Sjkim# context->Rbx = rsp[-2]; 976238405Sjkim# context->R15 = rsp[-1]; 977238405Sjkim# } 978238405Sjkim# context->Rsp = (ULONG64)rsp; 979238405Sjkim# context->Rdi = rsp[1]; 980238405Sjkim# context->Rsi = rsp[2]; 981238405Sjkim# 982238405Sjkim# memcpy (disp->ContextRecord,context,sizeof(CONTEXT)); 983238405Sjkim# RtlVirtualUnwind(UNW_FLAG_NHANDLER,disp->ImageBase, 984238405Sjkim# dips->ControlPc,disp->FunctionEntry,disp->ContextRecord, 985238405Sjkim# &disp->HandlerData,&disp->EstablisherFrame,NULL); 986238405Sjkim# return ExceptionContinueSearch; 987238405Sjkim# } 988238405Sjkim# 989238405Sjkim# It's appropriate to implement this handler in assembler, directly in 990238405Sjkim# function's module. In order to do that one has to know members' 991238405Sjkim# offsets in CONTEXT and DISPATCHER_CONTEXT structures and some constant 992238405Sjkim# values. Here they are: 993238405Sjkim# 994238405Sjkim# CONTEXT.Rax 120 995238405Sjkim# CONTEXT.Rcx 128 996238405Sjkim# CONTEXT.Rdx 136 997238405Sjkim# CONTEXT.Rbx 144 998238405Sjkim# CONTEXT.Rsp 152 999238405Sjkim# CONTEXT.Rbp 160 1000238405Sjkim# CONTEXT.Rsi 168 1001238405Sjkim# CONTEXT.Rdi 176 1002238405Sjkim# CONTEXT.R8 184 1003238405Sjkim# CONTEXT.R9 192 1004238405Sjkim# CONTEXT.R10 200 1005238405Sjkim# CONTEXT.R11 208 1006238405Sjkim# CONTEXT.R12 216 1007238405Sjkim# CONTEXT.R13 224 1008238405Sjkim# CONTEXT.R14 232 1009238405Sjkim# CONTEXT.R15 240 1010238405Sjkim# CONTEXT.Rip 248 1011238405Sjkim# CONTEXT.Xmm6 512 1012238405Sjkim# sizeof(CONTEXT) 1232 1013238405Sjkim# DISPATCHER_CONTEXT.ControlPc 0 1014238405Sjkim# DISPATCHER_CONTEXT.ImageBase 8 1015238405Sjkim# DISPATCHER_CONTEXT.FunctionEntry 16 1016238405Sjkim# DISPATCHER_CONTEXT.EstablisherFrame 24 1017238405Sjkim# DISPATCHER_CONTEXT.TargetIp 32 1018238405Sjkim# DISPATCHER_CONTEXT.ContextRecord 40 1019238405Sjkim# DISPATCHER_CONTEXT.LanguageHandler 48 1020238405Sjkim# DISPATCHER_CONTEXT.HandlerData 56 1021238405Sjkim# UNW_FLAG_NHANDLER 0 1022238405Sjkim# ExceptionContinueSearch 1 1023238405Sjkim# 1024238405Sjkim# In order to tie the handler to the function one has to compose 1025238405Sjkim# couple of structures: one for .xdata segment and one for .pdata. 1026238405Sjkim# 1027238405Sjkim# UNWIND_INFO structure for .xdata segment would be 1028238405Sjkim# 1029238405Sjkim# function_unwind_info: 1030238405Sjkim# .byte 9,0,0,0 1031238405Sjkim# .rva handler 1032238405Sjkim# 1033238405Sjkim# This structure designates exception handler for a function with 1034238405Sjkim# zero-length prologue, no stack frame or frame register. 1035238405Sjkim# 1036238405Sjkim# To facilitate composing of .pdata structures, auto-generated "gear" 1037238405Sjkim# prologue copies rsp value to rax and denotes next instruction with 1038238405Sjkim# .LSEH_begin_{function_name} label. This essentially defines the SEH 1039238405Sjkim# styling rule mentioned in the beginning. Position of this label is 1040238405Sjkim# chosen in such manner that possible exceptions raised in the "gear" 1041238405Sjkim# prologue would be accounted to caller and unwound from latter's frame. 1042238405Sjkim# End of function is marked with respective .LSEH_end_{function_name} 1043238405Sjkim# label. To summarize, .pdata segment would contain 1044238405Sjkim# 1045238405Sjkim# .rva .LSEH_begin_function 1046238405Sjkim# .rva .LSEH_end_function 1047238405Sjkim# .rva function_unwind_info 1048238405Sjkim# 1049238405Sjkim# Reference to functon_unwind_info from .xdata segment is the anchor. 1050238405Sjkim# In case you wonder why references are 32-bit .rvas and not 64-bit 1051238405Sjkim# .quads. References put into these two segments are required to be 1052238405Sjkim# *relative* to the base address of the current binary module, a.k.a. 1053238405Sjkim# image base. No Win64 module, be it .exe or .dll, can be larger than 1054238405Sjkim# 2GB and thus such relative references can be and are accommodated in 1055238405Sjkim# 32 bits. 1056238405Sjkim# 1057238405Sjkim# Having reviewed the example function code, one can argue that "movq 1058238405Sjkim# %rsp,%rax" above is redundant. It is not! Keep in mind that on Unix 1059238405Sjkim# rax would contain an undefined value. If this "offends" you, use 1060238405Sjkim# another register and refrain from modifying rax till magic_point is 1061238405Sjkim# reached, i.e. as if it was a non-volatile register. If more registers 1062238405Sjkim# are required prior [variable] frame setup is completed, note that 1063238405Sjkim# nobody says that you can have only one "magic point." You can 1064238405Sjkim# "liberate" non-volatile registers by denoting last stack off-load 1065238405Sjkim# instruction and reflecting it in finer grade unwind logic in handler. 1066238405Sjkim# After all, isn't it why it's called *language-specific* handler... 1067238405Sjkim# 1068238405Sjkim# Attentive reader can notice that exceptions would be mishandled in 1069238405Sjkim# auto-generated "gear" epilogue. Well, exception effectively can't 1070238405Sjkim# occur there, because if memory area used by it was subject to 1071238405Sjkim# segmentation violation, then it would be raised upon call to the 1072238405Sjkim# function (and as already mentioned be accounted to caller, which is 1073238405Sjkim# not a problem). If you're still not comfortable, then define tail 1074238405Sjkim# "magic point" just prior ret instruction and have handler treat it... 1075238405Sjkim# 1076238405Sjkim# (*) Note that we're talking about run-time, not debug-time. Lack of 1077238405Sjkim# unwind information makes debugging hard on both Windows and 1078238405Sjkim# Unix. "Unlike" referes to the fact that on Unix signal handler 1079238405Sjkim# will always be invoked, core dumped and appropriate exit code 1080238405Sjkim# returned to parent (for user notification). 1081