a一级爱做片免费观看欧美,久久国产一区二区,日本一二三区免费,久草视频手机在线观看

新聞中心

EEPW首頁 > 嵌入式系統 > 牛人業話 > C語言的那些小秘密之函數的調用關系

C語言的那些小秘密之函數的調用關系

作者: 時間:2015-03-09 來源:網絡 收藏

  參數的壓棧是從右向左的,即先壓最后一個參數,在壓倒數第二個,以此類推,最后才壓入第一個參數。為了加深大家的印象,下面我給出一個測試代碼:

本文引用地址:http://www.j9360.com/article/270700.htm

  #include

  void turn(int x, int y, int z)

  {

  printf("x = %d at [%X]n", x, &x);

  printf("y = %d at [%X]n", y, &y);

  printf("z = %d at [%X]n", z, &z);

  }

  int main(int argc, char *argv[])

  {

  turn(1, 2, 3);

  return 0;

  }

  運行結果為:

  

 

  比較打印出來的地址可以看出參數z的地址是最大的,x的地址最小。

  參數的壓棧工作完成之后,接下來就依次是EIP、EBP、臨時變量的壓棧操作了。最后壓入的是被調用本身,并為它分配臨時的變量空間,而對于不同版本的gcc的處理方式各有不同,老版本的gcc第一個臨時變量放在最高的地址,第二個其次,依次順序分布,新版本的gcc則與之相反。

  實現backtrace()的調用關系,其步驟如下:

  1.獲取當前函數的EBP;

  2.通過EBP獲得調用者得EIP;

  3.通過EBP獲得上一級的EBP;

  4.重復這個過程,知道結束。

  自己實現的backtrace()函數,代碼如下:

  #include

  #define MAX_LEVEL 4

  #define OFFSET 4

  int backtrace(void** buffer, int size)

  {

  int n = 0x23f;

  int* p = &n;

  int i = 0;

  int ebp = p[1 + OFFSET];

  int eip = p[2 + OFFSET];

  for(i = 0; i < size; i++)

  {

  buffer[i] = (void*)eip;

  p = (int*)ebp;

  ebp = p[0];

  eip = p[1];

  }

  return size;

  }

  static void call2()

  {

  int i = 0;

  void* buffer[MAX_LEVEL] = {0};

  int size=backtrace(buffer, MAX_LEVEL);

  for(i = 0; i < size; i++)

  {

  printf("called by %pn", buffer[i]);

  }

  return;

  }

  static void call1()

  {

  call2();

  return;

  }

  static void call()

  {

  call1();

  return;

  }

  int main(int argc, char* argv[])

  {

  call();

  return 0;

  }

  運行結果如下:

  root@ubuntu:/home/shiyan# gcc -g bac.c -o tt

  root@ubuntu:/home/shiyan# ./tt

  called by 0x8048491

  called by 0x80484ce

  called by 0x80484db

  called by 0x80484e8

  轉換為源代碼位置:root@ubuntu:/home/shiyan# ./tt |awk '{print "addr2line "$3" -e tt"}'>t.sh;. t.sh;rm -f t.sh

  root@ubuntu:/home/shiyan# ./tt |awk '{print "addr2line "$3" -e tt"}'>t.sh;. t.sh;rm -f t.sh

  /home/shiyan/bac.c:32

  /home/shiyan/bac.c:47

  /home/shiyan/bac.c:54

  /home/shiyan/bac.c:60

  在此重點介紹下backtrace()函數的實現原理。

  通過 int* p = &n;來獲取第一個臨時變量的位置,因為我使用的是新版本的gcc,有5個臨時變量,所以EIP的值存放在p[6]中,EBP的的值存放在p[5],通過 buffer[i] = (void*)eip;可以把eip的強制轉換為可以指向任意類型的指針, 接下來通過 p = (int*)ebp;來獲得上一個函數的ebp,獲得ebp之后由ebp和eip的位置關系可以得到eip,由于ebp指向的單元存儲的是上一個函數的ebp,所以用一個簡單的for循環就能實現了。

  另外在頭文件"execinfo.h"中除了聲明backtrace()函數外,還有如下兩個函數也用于獲取當前線程的函數調用堆棧。

  char ** backtrace_symbols (void *const *buffer, int size)

  backtrace_symbols將從backtrace函數獲取的信息轉化為一個字符串數組. 參數buffer應該是從backtrace函數獲取的數組指針,size是該數組中的元素個數(backtrace的返回值)

  函數返回值是一個指向字符串數組的指針,它的大小同buffer相同.每個字符串包含了一個相對于buffer中對應元素的可打印信息.它包括函數名,函數的偏移地址,和實際的返回地址

  現在,只有使用ELF二進制格式的程序和苦衷才能獲取函數名稱和偏移地址.在其他系統,只有16進制的返回地址能被獲取.另外,你可能需要傳遞相應的標志給鏈接器,以能支持函數名功能(比如,在使用GNU ld的系統中,你需要傳遞(-rdynamic)),該函數的返回值是通過malloc函數申請的空間,因此調用這必須使用free函數來釋放指針。

  注意:如果不能為字符串獲取足夠的空間函數的返回值將會為NULL。

  Function:void backtrace_symbols_fd (void *const *buffer, int size, int fd)

  backtrace_symbols_fd與backtrace_symbols 函數具有相同的功能,不同的是它不會給調用者返回字符串數組,而是將結果寫入文件描述符為fd的文件中,每個函數對應一行.它不需要調用malloc函數,因此適用于有可能調用該函數會失敗的情況。

  還是那句話,以上內容難免有誤,如有錯誤,請糾正。

c語言相關文章:c語言教程



上一頁 1 2 下一頁

關鍵詞: C語言 函數

評論


相關推薦

技術專區

關閉