2016-02-25 16 views
10

Tôi muốn sử dụng Lấy mẫu dựa trên sự kiện chính xác (PEBS) để ghi lại tất cả các địa chỉ của các sự kiện cụ thể (ví dụ: nhớ cache), trên XeonE5 Sandy Bridge.Giữ địa chỉ đích tải khi đăng ký cho đến khi lệnh được gỡ bỏ

Tuy nhiên, Performance Analysis Hướng dẫn cho Core TM Processor i7 và Intel XeonTM 5500 bộ xử lý, p.24, chứa các cảnh báo sau đây:

Là cơ chế PEB nắm bắt được giá trị của các đăng ký tại hoàn thành hướng dẫn, địa chỉ được đăng ký lại cho loại lệnh tải sau đây (quy ước asm của Intel) không được được xây dựng lại.
MOV RAX, [RAX+const]
Loại hướng dẫn chủ yếu liên quan đến con trỏ đuổi
mystruc = mystruc->next;
Đây là một thiếu sót đáng kể của phương pháp này để chụp các địa chỉ hướng dẫn bộ nhớ.

Tôi có một số hướng dẫn tải của biểu mẫu đó trong chương trình của tôi theo objdump. Có cách nào để tránh những thứ đó không?

Vì đây là một vấn đề cụ thể về intel, giải pháp không cần phải di động theo bất kỳ cách nào, nó chỉ hoạt động. Mã của tôi được viết bằng C và tôi lý tưởng tìm giải pháp cấp trình biên dịch (gcc hoặc icc) nhưng mọi đề xuất đều được chào đón.


Một số ví dụ:

mov 0x18(%rdi),%rdi 

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

Trong cả hai trường hợp, sau khi hướng dẫn về hưu (do đó khi tôi nhìn vào các giá trị đăng ký để tìm ra nơi tôi nạp đến/từ) các giá trị của địa chỉ (tương ứng %rdi + 18%rcx + 8 * %rax trong các ví dụ này) được ghi đè bởi kết quả của mov.

+0

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ả. –

+0

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. –

+1

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

Trả lời

3

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:

  1. quét và đặt tất cả các chức năng các định nghĩa
  2. xác định tất cả thanh ghi được sử dụng trong một chức năng nhất định
  3. nằm tất cả các điểm trả về của hàm
  4. 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)
  5. thay thế tất cả các hướng dẫn "phiền hà" bằng hai chuỗi lệnh
  6. 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ý
  7. 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
  8. 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'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 r10mà 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/popsẽ.

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/popkhô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ờ ...

+0

Ngoài ra, hãy tìm hiểu về 'r10'. Ấn tượng của tôi là đầu ra của trình biên dịch C sẽ xử lý nó như là một bản quét đầu, nhưng tôi đã đưa ra gợi ý an toàn để xử lý nó như là bảo quản cuộc gọi. (Mặc dù tôi thậm chí không chắc chắn * làm thế nào * nó được sử dụng cho các ngôn ngữ sử dụng nó. Giá trị của nó trong các cuộc gọi con thực sự quan trọng, không chỉ khi trở về với phụ huynh.) Đây sẽ là một dấu hiệu nếu bạn là mã thiết bị từ một ngôn ngữ sử dụng nó, nhưng không phải cho C/C++. –

+1

@PeterCordes Ah, cuối cùng đã tìm thấy thứ gì đó trên chuỗi tĩnh. Xem các trang thông tin cho 'gcc'. Chỉ cần tìm thấy nó, nhưng chưa đọc đủ của nó. Ngoài ra, 'gcc-manual.pdf'" Sử dụng và chuyển Bộ sưu tập trình biên dịch GNU "[Stallman] cho gcc-3.0 [cập nhật lần cuối 14 tháng 6 năm 2001]. –

+0

@Cimbali Tôi đã cập nhật tập lệnh để sử dụng 'lea' [cộng với một bugfix chống lại tiếng kêu' .s', một số tính năng và nhiều nhận xét khác]. Vui vì bạn có một cái gì đó làm việc. Đăng ký "đã sử dụng" quét là tốt hơn/cố định từ orig, vì vậy hãy chú ý đến điều đó. Ngay cả khi bạn muốn làm việc từ orig, có một cái nhìn. Tôi muốn được quan tâm để biết làm thế nào những điều làm việc ra khi kịch bản là "sản xuất đầy đủ", vì vậy gửi cho tôi một bình luận –

1

Cách duy nhất tôi có thể nghĩ ngay bây giờ là sử dụng ràng buộc lắp ráp & (dấu và). Điều đó sẽ có nghĩa là tôi sẽ phải đi qua mã của tôi bất cứ nơi nào một lệnh như vậy xuất hiện, và thay thế tất cả các con trỏ dereferencing mystruc = mystruc->next; với một cái gì đó như:
asm volatile("mov (%1),%0" : "=&r" (mystruc) : "r" (&(mystruc->next)))

Tuy nhiên đó là một cách tiếp cận rất người đi bộ và có lẽ trường hợp phức tạp hơn hơn một con trỏ bên trong một cấu trúc. Tôi hiểu điều này về cơ bản là tăng áp lực đăng ký để một trình biên dịch đang tích cực cố gắng tránh, và tôi vẫn đang tìm cách khác để làm điều này.

Các vấn đề liên quan