将加载的目标地址保留在寄存器中,直到指令退出

2023-11-21

我想使用基于事件的精确采样 (PEBS) 来记录 XeonE5 Sandy Bridge 上特定事件的所有地址(例如缓存未命中)。

However, the Performance Analysis Guide for CoreTM i7 Processor and Intel® XeonTM 5500 processors, p.24, contains the following warning:

由于 PEBS 机制捕获寄存器的值 指令完成后,取消引用的地址 以下类型的加载指令(Intel asm 约定)不能 重建。
MOV RAX, [RAX+const]
这种 指令主要与指针追踪相关
mystruc = mystruc->next;
这是本作的一个重大缺点 捕获内存指令地址的方法。

根据 objdump,我的程序中有许多这种形式的加载指令。我有什么办法可以避免这些吗?

由于这是英特尔特有的问题,因此该解决方案不必以任何方式移植,只需能够工作即可。我的代码是用 C 编写的,我理想地寻找编译器级解决方案(gcc 或 icc),但欢迎提出任何建议。


一些例子:

mov    0x18(%rdi),%rdi

mov    (%rcx,%rax,8),%rax

在这两种情况下,指令退出后(因此当我查看寄存器值以找出加载到/从哪里加载时)地址的值(分别为%rdi + 18 and %rcx + 8 * %rax在这些例子中)被结果覆盖mov.


我现在能想到的唯一方法是使用 & (与符号)汇编器约束。这意味着我必须在出现此类指令的任何地方检查我的代码,并替换每个指针取消引用mystruc = mystruc->next;类似的东西:
asm volatile("mov (%1),%0" : "=&r" (mystruc) : "r" (&(mystruc->next)))

然而,这是一种非常简单的方法,并且在某些情况下可能比结构内的指针更复杂。我知道这基本上会增加寄存器压力,因此编译器正在积极尝试避免这种情况。还有其他方法可以做到这一点吗?


您想要做的是转换表单的所有指令:

mov    (%rcx,%rax,8),%rax

Into:

mov    (%rcx,%rax,8),%r11
mov    %r11,%rax

通过修改编译器生成的汇编程序源代码,可以更轻松地完成此操作。下面是一个perl脚本将通过读取和修改来完成所有必要的转换.s file.

只需更改构建即可生成.s文件而不是.o文件,应用脚本,然后生成.o与任一as or gcc


这是实际的脚本。我已经按照下面评论中的构建过程在我自己的一些来源上对其进行了测试。

该脚本具有以下特点:

  1. 扫描并定位所有函数定义
  2. 标识给定函数中使用的所有寄存器
  3. 找到函数的所有返回点
  4. 根据函数的寄存器使用情况选择要使用的临时寄存器(即它将使用一个临时寄存器not已被该函数使用)
  5. 用两个指令序列替换所有“麻烦”的指令
  6. 尝试使用未使用的临时寄存器(例如%r11或未使用的参数寄存器)在尝试使用之前callee保存的寄存器
  7. 如果所选寄存器是callee已保存,将添加push函数序言和pop发挥作用[多个]ret声明
  8. 维护所有分析和转换的日志,并将其作为注释附加到输出中.s file

#!/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:

我更新了脚本以修复错误、添加更多检查和更多选项。Note:我必须删除底部的 ABI 以适应 30,000 的限制。

否则,奇怪的结果会出现在带括号的其他命令上cmpl %ebp, (%rax,%r14)分裂成lhs='cmpl %ebp, (%rax' and rhs='%r14)'这又导致/$rhs/失败。

是的,那是一个错误。固定的。

Your $rhs =~ /%[er](.x|\d+)/与字节或字加载不匹配di, or ax。但这不太可能。哦,我也觉得不匹配rdi / rsi。所以你不需要 r10d 中的尾随 d

固定的。查找所有变体。

哇,我认为这样的事情必须在编译时发生,而事后才这样做会太混乱。

无耻插件:谢谢你的“哇!”。perl非常适合像这样混乱的工作。我以前写过这样的汇编器“注入”脚本。 (例如)回到过去[在编译器支持之前]添加分析调用。

您可以将 %r10 标记为另一个调用保留寄存器。

经过几次网络搜索后,我只找到了大约 84 个匹配项"static chain" x86。唯一相关的是 x86 ABI。而且,除了作为脚注提及之外,它没有提供任何解释。另外,一些gcc代码使用r10 without任何另存为callee登记。所以,我现在默认使用该程序r10(如果需要,可以使用命令行选项禁用它)。

如果函数已经使用了所有寄存器会发生什么?

If it's truly全部,那么我们就不走运了。该脚本将检测并报告此情况,如果找不到备用寄存器,则会抑制修复。

And, it will通过注入使用“被调用者必须保留”寄存器push作为函数的第一个实例和相应的pop就在之前retinst [可以有多个]。可以通过一个选项禁用此功能。

你不能只是推/弹出,因为这会踩到红色区域

不,确实如此not。出于以下几个原因:

(1) 几乎作为旁注:红色区域仅在叶函数中有用。否则,如果fncA calls fncB,仅仅这样做的行为fncA会踩到自己的红色区域。请参阅脚本顶部注释块中的编译选项。

(2) 更重要的是,由于push/pop被注入。这push occurs before任何其他实例。这pop occurs after任何其他实例[就在ret].

红色区域仍然在那里——完好无损。它只是从原本的位置偏移了 -8。所有红色区域活动都被保留,因为这些insts使用negative偏移量%rsp

This is 不同的 than a push/pop在内联 asm 块内完成。通常的情况是红色区域代码正在执行(例如)mov $23,-4(%rsp)。随后出现一个内联汇编块,它执行以下操作push/pop would踩在那上面。

一些函数可以显示这一点:

# 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

哪里的push/pop does给我们带来麻烦的是如果该函数使用more超过 6 个参数(即 args 7+ 在堆栈上)。访问这些参数使用positive偏移量%rsp:

mov     32(%rsp),%rax

用我们的“绝招”push,偏移将不正确。正确的偏移量现在应该高 8:

mov     40(%rsp),%rax

该脚本将detect这并抱怨。但是,它[尚未]调整正偏移量,因为这种情况的概率很低。可能还需要大约五行代码才能解决这个问题。暂时踢球...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将加载的目标地址保留在寄存器中,直到指令退出 的相关文章

随机推荐

  • Shiny App 中的不同页面

    我想在我闪亮的仪表板上有不同的页面 首先 我创建了一个登录页面来向用户和管理员进行身份验证 之后 如果管理员登录系统想要查看一些用户无法访问的选项 问题 当我以用户或管理员身份登录时 我可以在后台看到主 ui r 页面 如何解决此问题以仅看
  • 如何使用 R 中的 gtsummary 包向表中的标签添加下标?

    I would like to introduce a subscript in a variable name for a regression summary table generated using tbl regression i
  • 预期 LP_c_double 实例而不是 c_double_Array - python ctypes 错误

    我在 DLL 中有一个函数 必须用 python 代码包装它 该函数需要一个指向双精度数组的指针 这是我收到的错误 Traceback most recent call last File C FROGmoduleTEST py line
  • android 中是否可以使用 loadDataWithBaseURL() 方法显示图像?

    我正在尝试使用以下命令显示 html 文件的内容loadDataWithBaseURL 安卓中的方法 我只有一个字符串 其中包含一个名为 source 的字符串中的 Html 文件数据 然后将其传递给该方法 for e g String s
  • 将 HTML 部分预加载到 AngularJS UI-Router 应用程序中

    我注意到 在完全刷新我的 Angular 应用程序时 状态转换 我使用 ui router 但也可能类似于本机 Angular 路由 在第一次访问时有轻微的延迟 因为浏览器会执行 GET 请求来检索与给定状态关联的 HTML 部分 所有后续
  • jsoup线程安全

    Jsoup parse String html 没有被记录为线程安全的 如何使用 Jsoup 同时解析多个文档 谢谢 通过使用Joup parse 只要您不处理同一个文档 它就会在内部创建新对象 https groups google co
  • 使用“==”和“is”比较字符串[重复]

    这个问题在这里已经有答案了 可能的重复 is 关键字可能相当于 Python 中的相等运算符的类型 Python is 运算符对整数的行为异常 Hi 我有一个问题 也许它比我所问的问题更能启发我 考虑一下 gt gt gt x Hello
  • 在Google App Engine下从数据库动态加载Python应用程序代码

    我需要将 python 代码存储在数据库中并将其加载到某种 bootstrap py 应用程序中以供执行 我无法使用文件系统 因为我正在使用 GAE 所以这是我唯一的选择 然而我不是一个有经验的Python用户 我已经能够加载 1 行代码并
  • 以编程方式创建图层列表

    我正在尝试以编程方式创建一个图层列表 其中调整大小的位图作为项目 据我所知 BitmapDrawable 已被弃用 新的构造函数需要以下参数 public BitmapDrawable Resources res Bitmap bitmap
  • QWebEnginePage 中的透明背景

    我们正在尝试将一些应用程序从 Qt 4 移植到 Qt 5 4 Qt 5 4 有一个新的 Web 引擎 我们曾经制作过背景QWebView and QWebPage做到透明 view new QWebView this QPalette pa
  • PHP switch 语句跳转到另一个 case

    假设我有这样的事情 switch GET func case foo dobar break case orange if GET aubergine catdog DO DEFAULT OPTION else dosomethingEls
  • jQuery 克隆问题

    我正在尝试克隆一个 div 并更改该 div 中输入字段的名称 它适用于大多数浏览器 但 IE 7 不会更改输入字段的名称属性 Demo http jsbin com iduro 7 HTML pre pre div div
  • 如何缓存 AVPlayerItem(视频)以便在 UITableview 中重用

    我有许多视频在 UITableView 中显示 视频远程存储在服务器上 我可以使用以下一些代码将视频加载到表格视图中 NSString urlString NSString stringWithFormat row objectForKey
  • PHP、使用 htaccess 进行 URL 重写和 Microsoft IIS Url 重写

    我习惯使用 Apache 服务器 因此当启用 mod rewrite 时 我可以创建一个 htaccess 文件并使用 URL 重写 这是我的 htaccess 文件 RewriteEngine On RewriteCond REQUEST
  • 多线程Z3?

    我正在开发一个 Python 项目 目前我正在尝试以一些可怕的方式加快速度 我设置了 Z3 求解器 然后分叉该进程 让 Z3 在子进程中执行求解并传递将模型的可腌制表示返回给父级 这非常有效 并且代表了我正在尝试做的第一阶段 父进程现在不再
  • MacOS 10.10 上的 ipython - 未找到命令

    我正在尝试让 ipython 在 Mac OS 10 10 中工作 problem ipython returns bash ipython command not found context 我在 MacOS 10 10 上运行 pyth
  • twisted 文档字符串中这些格式的含义是什么?

    在twisted的源代码中 许多文档字符串包含这样的格式 L xxx 或C xxx 或以 开头的行 它们的含义是什么 例如 在twisted internet interfaces py中 def registerProducer prod
  • 如何在ListView中显示行号?

    显而易见的解决方案是在 ModelView 元素上拥有行号属性 但缺点是在添加记录或更改排序顺序时必须重新生成这些属性 有没有一个elegant解决方案 我觉得你have优雅的解决方案 但这可行 XAML
  • 无法获得没有哈希值的主干路由?

    我想要浏览器可以捕获和处理的可添加书签的 URL 如果我只是使用Backbone history start 然后我可以使用哈希 URL 例如 账户 但我想要没有哈希值的 URL a la 账户 但我无法使用它来工作Backbone his
  • 将加载的目标地址保留在寄存器中,直到指令退出

    我想使用基于事件的精确采样 PEBS 来记录 XeonE5 Sandy Bridge 上特定事件的所有地址 例如缓存未命中 However the Performance Analysis Guide for CoreTM i7 Proce