iT邦幫忙

1

來嘗試打破x86的習俗

  • 分享至 

  • xImage
  •  

先看一下以下的程式碼,以及用他來編譯出來的組合語言

// add.c
#include <stdio.h>

long add(long a, long b)
{
    return a + b;
}

int main()
{
    int a, b, c;
    a = -1;
    b = -2;
    c = add(a, b);
    printf("%d\n", c);
    return 0;
}

在終端機輸入:

$ gcc -Og -S add.c    #產生組合語言

雖然產生出來的組合語言看起來很雜亂,但等等只要看看重點的幾行就好

  1     .file   "add.c"
  2     .text
  3     .globl  add
  4     .type   add, @function
  5 add:
  6 .LFB11:
  7     .cfi_startproc
  8     leaq    (%rdi,%rsi), %rax
  9     ret
 10     .cfi_endproc
 11 .LFE11:
 12     .size   add, .-add
 13     .section    .rodata.str1.1,"aMS",@progbits,1
 14 .LC0:
 15     .string "%d\n"
 16     .text
 17     .globl  main
 18     .type   main, @function
 19 main:
 20 .LFB12:
 21     .cfi_startproc
 22     subq    $8, %rsp
 23     .cfi_def_cfa_offset 16
 24     movq    $-2, %rsi
 25     movq    $-1, %rdi
 26     call    add
 27     movq    %rax, %rsi
 28     leaq    .LC0(%rip), %rdi
 29     movl    $0, %eax
 30     call    printf@PLT
 31     movl    $0, %eax
 32     addq    $8, %rsp
 33     .cfi_def_cfa_offset 8
 34     ret
 35     .cfi_endproc
 36 .LFE12:
 37     .size   main, .-main
 38     .ident  "GCC: (Debian 10.2.1-6) 10.2.1 20210110"
 39     .section    .note.GNU-stack,"",@progbits

重點的幾行:

  5 add:
 ...
  8     leaq    (%rdi,%rsi), %rax   # a + b
  9     ret
 
 19 main:
...
 24     movq    $-2, %rsi           # b = -2
 25     movq    $-1, %rdi           # a = -1
 26     call    add                 # add(a, b)
...

從這幾行可以看到c語言中的function call在組合語言中是如何被實作的:
1.先把-1丟進%rdi、-2丟進%rsi(24,25行)
2.call add(26行)
3.add執行%rdi * rsi的動作,並把內容放到%rax
4.從addreturn回main

add這個subroutine之所以拿%rdi%rsi這兩個暫存器來做相乘,是因為他相信第一個跟第二個參數分別被放在%rdi%rsi

而這種信任的合作模式,通常書上會使用convention(習俗、習慣)來稱呼

我的疑問:

既然這是習俗,應該代表並不是強制上必須要這麼做的吧?
習慣上用%rdi及%rsi來當第1第2個參數,但用其他的暫存器(如%r8,%r9)應該也可以吧?

所以來做個小小的實驗,來修改gcc產生出來的add.s檔:

  5 add:
 ...
  8     leaq    (%r8,%r9), %rax     # a + b (改成r8乘r9)
  9     ret
 
 19 main:
...
 24     movq    $-2, %r8            # b = -2 (從%rsi改成%r9)
 25     movq    $-1, %r9            # a = -1 (從%rdi改成%r8)
 26     call    add                 # add(a, b)
...
# 編譯修改後的add.s
$ gcc -c add.s -Og
$ gcc -o add add.o -Og

執行:

$ ./add
-3

可以看到就算使用了其他的暫存器,也一樣是可以的,gcc也沒有給出任何的警告或錯誤


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
player
iT邦大師 1 級 ‧ 2021-08-11 17:40:38

會用到那些暫存器
是由編譯器去決定的
如果你有可能在C/C++裡混用組合語言的話
就得自己保存暫存器的值到堆疊
以免因為暫存器的值被覆蓋
導致程式錯亂掉

ian9501 iT邦新手 4 級 ‧ 2021-08-11 18:43:21 檢舉

喔喔感謝~~~

我要留言

立即登入留言