gì bạn muốn làm là chuyển đổi tất cả các hướng dẫn có dạng:
mov (%rcx,%rax,8),%rax
Into:
mov (%rcx,%rax,8),%r11
mov %r11,%rax
Điều này có thể được thực hiện dễ dàng hơn nhiều bằng cách thay đổi nguồn lắp ráp được tạo bởi trình biên dịch.Dưới đây là tập lệnh perl
sẽ thực hiện tất cả các biến đổi cần thiết bằng cách đọc và sửa đổi tệp .s
.
Chỉ cần thay đổi việc xây dựng để sản xuất .s
file thay vì .o
tác phẩm, áp dụng các kịch bản, và sau đó tạo ra các .o
với một trong hai as
hoặc gcc
Dưới đây là kịch bản thực tế. Tôi đã thử nghiệm nó trên một vài nguồn của riêng tôi, theo các thủ tục xây dựng trong các ý kiến dưới đây.
Các kịch bản có các tính năng sau:
- quét và đặt tất cả các chức năng các định nghĩa
- xác định tất cả thanh ghi được sử dụng trong một chức năng nhất định
- nằm tất cả các điểm trả về của hàm
- chọn một tạm thời đăng ký sử dụng dựa trên việc sử dụng đăng ký của hàm (tức là nó sẽ sử dụng một thanh ghi tạm thời là không đã được sử dụng bởi hàm này)
- thay thế tất cả các hướng dẫn "phiền hà" bằng hai chuỗi lệnh
- cố gắng sử dụng các thanh ghi tạm thời chưa sử dụng (ví dụ:
%r11
hoặc không sử dụng thanh ghi lập luận) trước khi cố gắng sử dụng một callee lưu đăng ký
- nếu đăng ký chọn là callee lưu, sẽ bổ sung thêm
push
hoạt prolog và pop
thực hiện chức năng [nhiều] ret
báo cáo
- duy trì một bản ghi của tất cả các phân tích và biến đổi và gắn thêm này như ý kiến cho sản lượng
.s
tập tin
#!/usr/bin/perl
# pebsfix/pebsfixup -- fix assembler source for PEBS usage
#
# command line options:
# "-a" -- use only full 64 bit targets
# "-l" -- do _not_ use lea
# "-D[diff-file]" -- show differences (default output: "./DIFF")
# "-n10" -- do _not_ use register %r10 for temporary (default is use it)
# "-o" -- overwrite input files (can be multiple)
# "-O<outfile>" -- output file (only one .s input allowed)
# "-q" -- suppress warnings
# "-T[lvl]" -- debug trace
#
# "-o" and "-O" are mutually exclusive
#
# command line script test options:
# "-N[TPA]" -- disable temp register types [for testing]
# "-P" -- force push/pop on all functions
#
# command line arguments:
# 1-- list of .s files to process [or directory to search]
# for a given file "foo.s", output is to "foo.TMP"
# if (-o is given, "foo.TMP" is renamed to "foo.s")
#
# suggested usage:
# change build to produce .s files
# FROM:
# cc [options] -c foo.c
# TO:
# cc [options] -c -S foo.c
# pebsfixup -o foo.s
# cc -c foo.s
#
# suggested compiler options:
# [probably only really needed if push/pop required. use -NP to verify]
# (1) use either of
# -O2 -fno-optimize-sibling-calls
# -O1
# (2) use -mno-omit-leaf-frame-pointer
# (3) use -mno-red-zone [probably not required in any case]
#
# NOTES:
# (1) red zones are only really useful for leaf functions (i.e. if fncA calls
# fncB, fncA's red zone would be clobbered)
# (2) pushing onto the stack isn't a problem if there is a formal stack frame
# (3) the push is okay if the function has no more than six arguments (i.e.
# does _not_ use positive offsets from %rsp to access them)
#pragma pgmlns
use strict qw(vars subs);
our $pgmtail;
our $opt_a;
our $opt_T;
our $opt_D;
our $opt_l;
our $opt_n10;
our $opt_N;
our $opt_P;
our $opt_q;
our $opt_o;
our $opt_O;
our $opt_s;
our @reguse;
our %reguse_tobase;
our %reguse_isbase;
our $regusergx;
our @regtmplist;
our %regtmp_type;
our $diff;
our $sepflg;
our $fatal;
our @cmtprt;
master(@ARGV);
exit(0);
# master -- master control
sub master
{
my(@argv) = @_;
my($xfsrc);
my($file,@files);
my($bf);
$pgmtail = "pebsfixup";
optget(\@argv);
# define all known/usable registers
regusejoin();
# define all registers that we may use as a temporary
regtmpall();
if (defined($opt_D)) {
unlink($opt_D);
}
# show usage
if (@argv <= 0) {
$file = $0;
open($xfsrc,"<$file") ||
sysfault("$pgmtail: unable to open '%s' -- $!\n",$file);
while ($bf = <$xfsrc>) {
chomp($bf);
next if ($bf =~ /^#!/);
last unless ($bf =~ s/^#//);
$bf =~ s/^# ?//;
print($bf,"\n");
}
close($xfsrc);
exit(1);
}
foreach $file (@argv) {
if (-d $file) {
dodir(\@files,$file);
}
else {
push(@files,$file);
}
}
if (defined($opt_O)) {
sysfault("$pgmtail: -O may have only one input file\n")
if (@files != 1);
sysfault("$pgmtail: -O and -o are mutually exclusive\n")
if ($opt_o);
}
foreach $file (@files) {
dofile($file);
}
if (defined($opt_D)) {
exec("less",$opt_D);
}
}
# dodir -- process directory
sub dodir
{
my($files,$dir) = @_;
my($file,@files);
@files = (`find $dir -type f -name '*.s'`);
foreach $file (@files) {
chomp($file);
push(@$files,$file);
}
}
# dofile -- process file
sub dofile
{
my($file) = @_;
my($ofile);
my($xfsrc);
my($xfdst);
my($bf,$lno,$outoff);
my($fixoff);
my($lhs,$rhs);
my($xop,$arg);
my($ix);
my($sym,$val,$typ);
my(%sym_type);
my($fnc,$fnx,%fnx_lookup,@fnxlist);
my($retlist);
my($uselook,@uselist,%avail);
my($fixreg,$fixrtyp);
my($sixlist);
my($fix,$fixlist);
my($fixtot);
my(@fix);
my(@outlist);
my($relaxflg);
my($cmtchr);
undef($fatal);
undef(@cmtprt);
msgprt("\n")
if ($sepflg);
$sepflg = 1;
msgprt("$pgmtail: processing %s ...\n",$file);
$cmtchr = "#";
cmtprt("%s\n","-" x 78);
cmtprt("FILE: %s\n",$file);
# get the output file
$ofile = $file;
sysfault("$pgmtail: bad suffix -- file='%s'\n",$file)
unless ($ofile =~ s/[.]s$//);
$ofile .= ".TMP";
# use explicit output file
if (defined($opt_O)) {
$ofile = $opt_O;
sysfault("$pgmtail: output file may not be input file -- use -o instead\n")
if ($ofile eq $file);
}
open($xfsrc,"<$file") ||
sysfault("$pgmtail: unable to open '%s' -- $!\n",$file);
$lno = 0;
while ($bf = <$xfsrc>) {
chomp($bf);
$bf =~ s/\s+$//;
$outoff = $lno;
++$lno;
push(@outlist,$bf);
# clang adds comments
$ix = index($bf,"#");
if ($ix >= 0) {
$bf = substr($bf,0,$ix);
$bf =~ s/\s+$//;
}
# look for ".type blah, @function"
# NOTE: this always comes before the actual label line [we hope ;-)]
if ($bf =~ /^\s+[.]type\s+([^,]+),\s*(\S+)/) {
($sym,$val) = ($1,$2);
$val =~ s/^\@//;
$sym_type{$sym} = $val;
cmtprt("\n");
cmtprt("TYPE: %s --> %s\n",$sym,$val);
next;
}
# look for "label:"
if ($bf =~ /^([a-z_A-Z][a-z_A-Z0-9]*):$/) {
$sym = $1;
next if ($sym_type{$sym} ne "function");
$fnc = $sym;
cmtprt("FUNCTION: %s\n",$fnc);
$fnx = {};
$fnx_lookup{$sym} = $fnx;
push(@fnxlist,$fnx);
$fnx->{fnx_fnc} = $fnc;
$fnx->{fnx_outoff} = $outoff;
$uselook = {};
$fnx->{fnx_used} = $uselook;
$retlist = [];
$fnx->{fnx_retlist} = $retlist;
$fixlist = [];
$fnx->{fnx_fixlist} = $fixlist;
$sixlist = [];
$fnx->{fnx_sixlist} = $sixlist;
next;
}
# remember all registers used by function:
while ($bf =~ /($regusergx)/gpo) {
$sym = ${^MATCH};
$val = $reguse_tobase{$sym};
dbgprt(3,"dofile: REGUSE sym='%s' val='%s'\n",$sym,$val);
$uselook->{$sym} += 1;
$uselook->{$val} += 1
if ($val ne $sym);
}
# handle returns
if ($bf =~ /^\s+ret/) {
push(@$retlist,$outoff);
next;
}
if ($bf =~ /^\s+rep[a-z]*\s+ret/) {
push(@$retlist,$outoff);
next;
}
# split up "movq 16(%rax),%rax" ...
$ix = rindex($bf,",");
next if ($ix < 0);
# ... into "movq 16(%rax)"
$lhs = substr($bf,0,$ix);
$lhs =~ s/\s+$//;
# check for "movq 16(%rsp)" -- this means that the function has/uses
# more than six arguments (i.e. we may _not_ push/pop because it
# wreaks havoc with positive offsets)
# FIXME/CAE -- we'd have to adjust them by 8 which we don't do
(undef,$rhs) = split(" ",$lhs);
if ($rhs =~ /^(\d+)[(]%rsp[)]$/) {
push(@$sixlist,$outoff);
cmtprt("SIXARG: %s (line %d)\n",$rhs,$lno);
}
# ... and "%rax"
$rhs = substr($bf,$ix + 1);
$rhs =~ s/^\s+//;
# target must be a [simple] register [or source scan will blow up]
# (e.g. we actually had "cmp %ebp,(%rax,%r14)")
next if ($rhs =~ /[)]/);
# ensure we have the "%" prefix
next unless ($rhs =~ /^%/);
# we only want the full 64 bit reg as target
# (e.g. "mov (%rbx),%al" doesn't count)
$val = $reguse_tobase{$rhs};
if ($opt_a) {
next if ($val ne $rhs);
}
else {
next unless (defined($val));
}
# source operand must contain target [base] register
next unless ($lhs =~ /$val/);
###cmtprt("1: %s,%s\n",$lhs,$rhs);
# source operand must be of the "right" type
# FIXME/CAE -- we may need to revise this
next unless ($lhs =~ /[(]/);
cmtprt("NEEDFIX: %s,%s (line %d)\n",$lhs,$rhs,$lno);
# remember the place we need to fix for later
$fix = {};
push(@$fixlist,$fix);
$fix->{fix_outoff} = $outoff;
$fix->{fix_lhs} = $lhs;
$fix->{fix_rhs} = $rhs;
}
close($xfsrc);
# get total number of fixups
foreach $fnx (@fnxlist) {
$fixlist = $fnx->{fnx_fixlist};
$fixtot += @$fixlist;
}
msgprt("$pgmtail: needs %d fixups\n",$fixtot)
if ($fixtot > 0);
# fix each function
foreach $fnx (@fnxlist) {
cmtprt("\n");
cmtprt("FNC: %s\n",$fnx->{fnx_fnc});
$fixlist = $fnx->{fnx_fixlist};
# get the fixup register
($fixreg,$fixrtyp) = regtmploc($fnx,$fixlist);
# show number of return points
{
$retlist = $fnx->{fnx_retlist};
cmtprt(" RET: %d\n",scalar(@$retlist));
last if (@$retlist >= 1);
# NOTE: we display this warning because we may not be able to
# handle all situations
$relaxflg = (@$fixlist <= 0) || ($fixrtyp ne "P");
last if ($relaxflg && $opt_q);
errprt("$pgmtail: in file '%s'\n",$file);
errprt("$pgmtail: function '%s' has no return points\n",
$fnx->{fnx_fnc});
errprt("$pgmtail: suggest recompile with correct options\n");
if (@$fixlist <= 0) {
errprt("$pgmtail: working around because function needs no fixups\n");
last;
}
if ($fixrtyp ne "P") {
errprt("$pgmtail: working around because fixup reg does not need to be saved\n");
last;
}
}
# show stats on register usage in function
$uselook = $fnx->{fnx_used};
@uselist = sort(keys(%$uselook));
cmtprt(" USED:\n");
%avail = %reguse_isbase;
foreach $sym (@uselist) {
$val = $uselook->{$sym};
$typ = $regtmp_type{$sym};
$typ = sprintf(" (TYPE: %s)",$typ)
if (defined($typ));
cmtprt(" %s used %d%s\n",$sym,$val,$typ);
$val = $reguse_tobase{$sym};
delete($avail{$val});
}
# show function's available [unused] registers
@uselist = keys(%avail);
@uselist = sort(regusesort @uselist);
if (@uselist > 0) {
cmtprt(" AVAIL:\n");
foreach $sym (@uselist) {
$typ = $regtmp_type{$sym};
$typ = sprintf(" (TYPE: %s)",$typ)
if (defined($typ));
cmtprt(" %s%s\n",$sym,$typ);
}
}
# skip over any functions that don't need fixing _and_ have a temp
# register
if (@$fixlist <= 0 && (! $opt_P)) {
next if (defined($fixreg));
}
msgprt("$pgmtail: function %s\n",$fnx->{fnx_fnc});
# skip function because we don't have a fixup register but report it
# here
unless (defined($fixreg)) {
$bf = (@$fixlist > 0) ? "FATAL" : "can be ignored -- no fixups needed";
msgprt("$pgmtail: FIXNOREG (%s)\n",$bf);
cmtprt(" FIXNOREG (%s)\n",$bf);
next;
}
msgprt("$pgmtail: FIXREG --> %s (TYPE: %s)\n",$fixreg,$fixrtyp);
cmtprt(" FIXREG --> %s (TYPE: %s)\n",$fixreg,$fixrtyp);
foreach $fix (@$fixlist) {
$outoff = $fix->{fix_outoff};
undef(@fix);
cmtprt(" FIXOLD %s\n",$outlist[$outoff]);
# original
if ($opt_l) {
$bf = sprintf("%s,%s",$fix->{fix_lhs},$fixreg);
push(@fix,$bf);
$bf = sprintf("\tmov\t%s,%s",$fixreg,$fix->{fix_rhs});
push(@fix,$bf);
}
# use lea
else {
($xop,$arg) = split(" ",$fix->{fix_lhs});
$bf = sprintf("\tlea\t\t%s,%s",$arg,$fixreg);
push(@fix,$bf);
$bf = sprintf("\t%s\t(%s),%s",$xop,$fixreg,$fix->{fix_rhs});
push(@fix,$bf);
}
foreach $bf (@fix) {
cmtprt(" FIXNEW %s\n",$bf);
}
$outlist[$outoff] = [@fix];
}
unless ($opt_P) {
next if ($fixrtyp ne "P");
}
# fix the function prolog
$outoff = $fnx->{fnx_outoff};
$lhs = $outlist[$outoff];
$rhs = sprintf("\tpush\t%s",$fixreg);
$bf = [$lhs,$rhs,""];
$outlist[$outoff] = $bf;
# fix the function return points
$retlist = $fnx->{fnx_retlist};
foreach $outoff (@$retlist) {
$rhs = $outlist[$outoff];
$lhs = sprintf("\tpop\t%s",$fixreg);
$bf = ["",$lhs,$rhs];
$outlist[$outoff] = $bf;
}
}
open($xfdst,">$ofile") ||
sysfault("$pgmtail: unable to open '%s' -- $!\n",$ofile);
# output all the assembler text
foreach $bf (@outlist) {
# ordinary line
unless (ref($bf)) {
print($xfdst $bf,"\n");
next;
}
# apply a fixup
foreach $rhs (@$bf) {
print($xfdst $rhs,"\n");
}
}
# output all our reasoning as comments at the bottom
foreach $bf (@cmtprt) {
if ($bf eq "") {
print($xfdst $cmtchr,$bf,"\n");
}
else {
print($xfdst $cmtchr," ",$bf,"\n");
}
}
close($xfdst);
# get difference
if (defined($opt_D)) {
system("diff -u $file $ofile >> $opt_D");
}
# install fixed/modified file
{
last unless ($opt_o || defined($opt_O));
last if ($fatal);
msgprt("$pgmtail: installing ...\n");
rename($ofile,$file);
}
}
# regtmpall -- define all temporary register candidates
sub regtmpall
{
dbgprt(1,"regtmpall: ENTER\n");
regtmpdef("%r11","T");
# NOTES:
# (1) see notes on %r10 in ABI at bottom -- should we use it?
# (2) a web search on "shared chain" and "x86" only produces 28 results
# (3) some gcc code uses it as an ordinary register
# (4) so, use it unless told not to
regtmpdef("%r10","T")
unless ($opt_n10);
# argument registers (a6-a1)
regtmpdef("%r9","A6");
regtmpdef("%r8","A5");
regtmpdef("%rcx","A4");
regtmpdef("%rdx","A3");
regtmpdef("%rsi","A2");
regtmpdef("%rdi","A1");
# callee preserved registers
regtmpdef("%r15","P");
regtmpdef("%r14","P");
regtmpdef("%r13","P");
regtmpdef("%r12","P");
dbgprt(1,"regtmpall: EXIT\n");
}
# regtmpdef -- define usable temp registers
sub regtmpdef
{
my($sym,$typ) = @_;
dbgprt(1,"regtmpdef: SYM sym='%s' typ='%s'\n",$sym,$typ);
push(@regtmplist,$sym);
$regtmp_type{$sym} = $typ;
}
# regtmploc -- locate temp register to fix problem
sub regtmploc
{
my($fnx,$fixlist) = @_;
my($sixlist);
my($uselook);
my($regrhs);
my($fixcnt);
my($coretyp);
my($reglhs,$regtyp);
dbgprt(2,"regtmploc: ENTER fnx_fnc='%s'\n",$fnx->{fnx_fnc});
$sixlist = $fnx->{fnx_sixlist};
$fixcnt = @$fixlist;
$fixcnt = 1
if ($opt_P);
$uselook = $fnx->{fnx_used};
foreach $regrhs (@regtmplist) {
dbgprt(2,"regtmploc: TRYREG regrhs='%s' uselook=%d\n",
$regrhs,$uselook->{$regrhs});
unless ($uselook->{$regrhs}) {
$regtyp = $regtmp_type{$regrhs};
$coretyp = $regtyp;
$coretyp =~ s/\d+$//;
# function uses stack arguments -- we can't push/pop
if (($coretyp eq "P") && (@$sixlist > 0)) {
dbgprt(2,"regtmploc: SIXREJ\n");
next;
}
if (defined($opt_N)) {
dbgprt(2,"regtmploc: TRYREJ opt_N='%s' regtyp='%s'\n",
$opt_N,$regtyp);
next if ($opt_N =~ /$coretyp/);
}
$reglhs = $regrhs;
last;
}
}
{
last if (defined($reglhs));
errprt("regtmploc: unable to locate usable fixup register for function '%s'\n",
$fnx->{fnx_fnc});
last if ($fixcnt <= 0);
$fatal = 1;
}
dbgprt(2,"regtmploc: EXIT reglhs='%s' regtyp='%s'\n",$reglhs,$regtyp);
($reglhs,$regtyp);
}
# regusejoin -- get regex for all registers
sub regusejoin
{
my($reg);
dbgprt(1,"regusejoin: ENTER\n");
# rax
foreach $reg (qw(a b c d)) {
regusedef($reg,"r_x","e_x","_l","_h");
}
# rdi/rsi
foreach $reg (qw(d s)) {
regusedef($reg,"r_i","e_i","_i","_il");
}
# rsp/rbp
foreach $reg (qw(b s)) {
regusedef($reg,"r_p","e_p");
}
foreach $reg (8,9,10,11,12,13,14,15) {
regusedef($reg,"r_","r_d","r_w","r_b");
}
$regusergx = join("|",reverse(sort(@reguse)));
dbgprt(1,"regusejoin: EXIT regusergx='%s'\n",$regusergx);
}
# regusedef -- define all registers
sub regusedef
{
my(@argv) = @_;
my($mid);
my($pat);
my($base);
$mid = shift(@argv);
dbgprt(1,"regusedef: ENTER mid='%s'\n",$mid);
foreach $pat (@argv) {
$pat = "%" . $pat;
$pat =~ s/_/$mid/;
$base //= $pat;
dbgprt(1,"regusedef: PAT pat='%s' base='%s'\n",$pat,$base);
push(@reguse,$pat);
$reguse_tobase{$pat} = $base;
}
$reguse_isbase{$base} = 1;
dbgprt(1,"regusedef: EXIT\n");
}
# regusesort -- sort base register names
sub regusesort
{
my($symlhs,$numlhs);
my($symrhs,$numrhs);
my($cmpflg);
{
($symlhs,$numlhs) = _regusesort($a);
($symrhs,$numrhs) = _regusesort($b);
$cmpflg = $symlhs cmp $symrhs;
last if ($cmpflg);
$cmpflg = $numlhs <=> $numrhs;
}
$cmpflg;
}
# _regusesort -- split up base register name
sub _regusesort
{
my($sym) = @_;
my($num);
if ($sym =~ s/(\d+)$//) {
$num = $1;
$num += 0;
$sym =~ s/[^%]/z/g;
}
($sym,$num);
}
# optget -- get options
sub optget
{
my($argv) = @_;
my($bf);
my($sym,$val);
my($dft,%dft);
foreach $sym (qw(a l n10 P q o s T)) {
$dft{$sym} = 1;
}
$dft{"N"} = "T";
$dft{"D"} = "DIFF";
while (1) {
$bf = $argv->[0];
$sym = $bf;
last unless ($sym =~ s/^-//);
last if ($sym eq "-");
shift(@$argv);
{
if ($sym =~ /([^=]+)=(.+)$/) {
($sym,$val) = ($1,$2);
last;
}
if ($sym =~ /^(.)(.+)$/) {
($sym,$val) = ($1,$2);
last;
}
undef($val);
}
$dft = $dft{$sym};
sysfault("$pgmtail: unknown option -- '%s'\n",$bf)
unless (defined($dft));
$val //= $dft;
${"opt_" . $sym} = $val;
}
}
# cmtprt -- transformation comments
sub cmtprt
{
$_ = shift(@_);
$_ = sprintf($_,@_);
chomp($_);
push(@cmtprt,$_);
}
# msgprt -- progress output
sub msgprt
{
printf(STDERR @_);
}
# errprt -- show errors
sub errprt
{
cmtprt(@_);
printf(STDERR @_);
}
# sysfault -- abort on error
sub sysfault
{
printf(STDERR @_);
exit(1);
}
# dbgprt -- debug print
sub dbgprt
{
$_ = shift(@_);
goto &_dbgprt
if ($opt_T >= $_);
}
# _dbgprt -- debug print
sub _dbgprt
{
printf(STDERR @_);
}
UPDATE:
tôi đã cập nhật các kịch bản để sửa lỗi, thêm kiểm tra nhiều hơn, và nhiều lựa chọn hơn. Lưu ý: Tôi phải loại bỏ ABI ở phía dưới để phù hợp với giới hạn 30.000.
kết quả Nếu không lạ xuất hiện trên lệnh khác với ngoặc ví dụ cmpl %ebp, (%rax,%r14)
chia thành lhs='cmpl %ebp, (%rax'
và rhs='%r14)'
nó sẽ gây ra /$rhs/
thất bại.
Vâng, đó là lỗi. Đã sửa.
bạn $rhs =~ /%[er](.x|\d+)/
không phù hợp với byte hoặc từ tải để di
, hoặc ax
. Đó là không chắc chắn, mặc dù. Oh, ngoài ra, tôi nghĩ rằng nó không phù hợp với rdi/rsi
. do đó bạn không cần dấu chấm d trong r10d
Đã sửa lỗi. Tìm tất cả các biến thể.
Chà, tôi cho rằng một cái gì đó như thế này sẽ phải xảy ra vào thời gian biên dịch, và làm điều đó sau khi thực tế sẽ quá lộn xộn.
Shameless plug: Cảm ơn bạn đã "Wow!". perl
là công cụ tuyệt vời cho những công việc lộn xộn như thế này. Tôi đã viết kịch bản "tiêm" lắp ráp như thế này trước đây. (ví dụ) Quay lại trong ngày [trước khi hỗ trợ trình biên dịch] để thêm các cuộc gọi định hình.
Bạn có thể đánh dấu% r10 là một thanh ghi được giữ nguyên cuộc gọi khác.
Sau khi thực hiện một vài tìm kiếm trên web, tôi chỉ có thể tìm thấy khoảng 84 kết quả phù hợp trên "static chain" x86
. Chỉ có một sự liên quan là x86 ABI. Và, nó không đưa ra lời giải thích nào ngoài việc đề cập đến nó như một chú thích. Ngoài ra, một số mã gcc
sử dụng r10
mà không cần bất kỳ lưu dưới dạng số callee đăng ký. Vì vậy, bây giờ tôi đã mặc định chương trình sử dụng r10
(với tùy chọn dòng lệnh để vô hiệu hóa nó nếu muốn).
Điều gì xảy ra nếu một hàm đã sử dụng tất cả các thanh ghi?
Nếu nó thực sự tất cả, sau đó chúng tôi không gặp may. Kịch bản sẽ phát hiện và báo cáo điều này và ngăn chặn fixups nếu nó không thể tìm thấy một đăng ký phụ tùng.
Và, nó sẽ sử dụng một "callee phải giữ gìn" đăng ký bằng cách tiêm một push
như inst 1 của chức năng và một tương ứng pop
ngay trước ret
inst [có thể có nhiều]. Điều này có thể bị vô hiệu hóa với một tùy chọn.
Bạn có thể không chỉ đẩy/pop, vì đó bước vào màu đỏ-zone
Không, nó không . Vì một vài lý do:
(1) Gần như một lưu ý phụ: Vùng màu đỏ chỉ hữu dụng trong các chức năng lá. Nếu không, nếu fncA
gọi fncB
, hành động duy nhất làm điều này bằng cách fncA
sẽ bước vào vùng màu đỏ riêng của nó. Xem tùy chọn biên dịch trong khối nhận xét hàng đầu của tập lệnh.
(2) Quan trọng hơn, do cách mà push/pop
được tiêm. push
xảy ra trước bất kỳ nội dung nào khác. pop
xảy ra sau bất kỳ insts nào khác [ngay trước ret
].
Vùng màu đỏ vẫn còn đó - nguyên vẹn. Nó chỉ là bù đắp bởi -8 từ nơi nó sẽ có được nếu không. Tất cả các hoạt động khu vực màu đỏ được bảo tồn vì những insts sử dụng tiêu cực offsets từ %rsp
Đây là khác nhau hơn một push/pop
thực hiện bên trong một khối inline asm.Trường hợp thông thường là mã vùng màu đỏ đang hoạt động (ví dụ: mov $23,-4(%rsp)
. Một khối asm nội tuyến xuất hiện sau đó sẽ thực hiện một bước push/pop
sẽ.
Một số chức năng để hiển thị này:
# function_original -- original function before pebsfixup
# RETURNS: 23
function_original:
mov $23,-4(%rsp) # red zone code generated by compiler
...
mov -4(%rsp),%rax # will still have $23
ret
# function_pebsfixup -- pebsfixup modified
# RETURNS: 23
function_pebsfixup:
push %r12 # pebsfixup injected
mov $23,-4(%rsp) # red zone code generated by compiler
...
mov -4(%rsp),%rax # will still have $23
pop %r12 # pebsfixup injected
ret
# function_inline -- function with inline asm block and red zone
# RETURNS: unknown value
function_inline:
mov $23,-4(%rsp) # red zone code generated by compiler
# inline asm block -- steps on red zone
push %rdx
push %rcx
...
pop %rcx
pop %rdx
...
mov -4(%rsp),%rax # now -4(%rsp) no longer has $23
ret
Trường hợp push/pop
không đưa chúng ta gặp rắc rối là nếu chức năng sử dụng hơn hơn sáu đối số (ví dụ: args 7+ là trên stack). Tiếp cận với những lập luận này sử dụng một dương bù đắp từ %rsp
:
mov 32(%rsp),%rax
Với "lừa" của chúng tôi push
, bù đắp sẽ là không chính xác. Việc bù đắp đúng bây giờ sẽ là 8 trở lên:
mov 40(%rsp),%rax
Các kịch bản sẽ phát hiện này và phàn nàn. Tuy nhiên, nó chưa [điều chỉnh] các điều chỉnh tích cực như trường hợp này là xác suất thấp. Nó có lẽ sẽ mất khoảng năm dòng mã để sửa lỗi này. Đang làm săn chắc bây giờ ...
Có thể sẽ có rất nhiều công việc để hack lên gcc hoặc clang để tránh tải trọng ghi đè lên một trong các yếu tố đầu vào của họ. Có thể có một số cơ sở hạ tầng để quan tâm nhiều hơn là chỉ đăng ký áp lực, mặc dù, bởi vì Intel P6 (ppro để nehalem) thích nó khi bạn đọc từ sổ đăng ký được viết gần đây. Quá nhiều lần đọc từ sổ đăng ký cũ (trong đó giá trị đã được cam kết từ ROB đến tệp sổ đăng ký vĩnh viễn) sẽ dẫn đến các quầy đăng ký đọc. SnB đã thay đổi thành thiết kế tệp đăng ký vật lý và mức độ gián đoạn trong ROB và không bị các quầy đăng ký đọc ở tất cả. –
Dù sao, các trình biên dịch có thể đã có thể ưu tiên ghi đè lên các thanh ghi khác nhau, bởi vì tối ưu hóa cho P6 là một điều trong nhiều năm. Nhưng họ sẽ không làm điều đó khi nó chi phí thêm hướng dẫn. Một vòng lặp con trỏ-đuổi sẽ phải được unrolled để không chi tiêu một hướng dẫn thêm mov. –
Không ghi đè là một tùy chọn, có thể sao chép địa chỉ trước khi ghi đè là một tùy chọn khác. Tôi đọc trên các kiến trúc IBM này được thực hiện tự động khi sử dụng lấy mẫu dựa trên sự kiện, sử dụng thanh ghi chuyên dụng, Thanh ghi địa chỉ dữ liệu được lấy mẫu (SDAR). Có thể thi đua đây là một hướng dẫn đầy hứa hẹn hơn? – Cimbali