一個LINUX可執行文件(可執行檔)原來包含了這麼多信息

一個LINUX可執行文件(可執行檔)原來包含了這麼多信息

一個LINUX可執行文件(可執行檔)原來包含了這麼多信息


資料來源: https://mp.weixin.qq.com/s?__biz=MzA3MTU1MzMzNQ==&mid=2247485531&idx=1&sn=fa4477999fbb42e8bebc9161e6b5aef7&chksm=9f2a9003a85d191575c9c29b910aff504341ba0dafe04d892192770650343eaa5d366a64532a&scene=126&sessionid=1593575246&key=fa39e04f3e1bcf44bee7346eb13355604abf9c1f216bec37539c170683c769c5d76037463417e1863ecc31712db6c5bb199c634ef692bf35540953220fc1e25968b97002baa9c83682495d0e5b922caa&ascene=1&uin=MjIwODk2NDgxNw%3D%3D&devicetype=Windows+10+x64&version=62090523&lang=zh_TW&exportkey=AuZLtd%2FhFIyPkIj0SvdoQN0%3D&pass_ticket=OY70c9Ez82dFgHJuUmbZvyLuQWqpM6rITLwiPgNP9K3WGj%2BC8bRGstlT%2FjWgF2pl


00.測試程式碼

//main.c
#include<stdio.h>
void testFun()
{
    printf("公众号:编程珠玑\n");
}
int main(void)
{
    testFun();
    return 0;
}


01.編譯成執行檔

$ gcc -o main main.c


02.讀取擋頭

$ readelf -h main
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400430
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6648 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 28

程序位數

Class:     ELF64 

Class展示了該程序的位數,如這裡顯示的是ELF64,如果你將它放到一個32位系統中運行,運行得起來就怪了。換句話說,64位系統上能運行32位和64位的程序,但是32位系統上,無法運行64位的程序

大小端

  Data:   2's complement, little endian 

還記得那個到處可見的面試題嗎?如何判斷當前CPU是大端還是小端?除了各種秀代碼的方式,你想到這個方式了嗎?

找一個該平台上的正運行的可執行文件或系統庫,然後使用readelf -h看一下,是不是很快就看出來了?多麼明顯的little endian。

運行平台

Machine:   Advanced Micro Devices X86-64 

做嵌入式相關的可能經常需要做交叉編譯,而編譯出來的程序到底對不對呢?比如你在86平台編譯arm的程序,最終生成的可執行文件到底能不能運行在arm平台呢?通過Machine字段就可以很容易確定,從這裡可以看到,它是運行在x86平台的。

同樣的,當你在交叉編譯的時候,發現總有一個庫鏈接不上,但是庫又存在,不妨看看這個庫和你要編譯的平台是否匹配


03.使用那些函示庫(函數庫)

$ ldd main
    linux-vdso.so.1 =>  (0x00007ffe750e7000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f749920a000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f74995d4000)



04.有符號表嗎

我們都知道,沒有符號表的程序,在core之後是沒有太多有效信息可看的,也是無法使用gdb正常調試的,這個在《GDB調試入門,看這篇就夠了》中已經有提到了,那麼怎麼看有沒有符號表呢?

$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=0d9a7eb860459b585d2b33ae28d7c67d5ba12669, not stripped

如果使用file命令看到最後是not stripped,那麼則含有符號表,一般線上的程序可能會選擇去掉符號表信息,因為可以大大減少可執行文件的空間佔用。

$ strip main 

這個時候再看看:

$ nm main no main symbols



05.程序佔用空間太大?

為什麼程序的佔用空間這麼大?不妨看看是不是使用了過多的靜態變量或全局變量:

$ size main
   text       data     bss     dec     hex filename
   1261        552       8    1821     71d main 

看到data部分的大小了嗎?看起來並沒有多少,如果這裡佔用空間過大,那可能是你程序中用到了太多的全局變量和靜態變量或常量。當然了,如果你的全局變量都是初始化為0的,那麼data這裡是不會有明顯的變化的(為什麼?)

在開頭分別加下面這一行,其影響可執行文件的效果不一樣奧。

char str[1000] = {0}; char str[1000] = {1};



06.C還是C++?

如果將前面的程序按照C++編譯:

$ g++ -o main main.c $ nm main |grep test 0000000000400526 T _Z7testFunv 

你會發現使用g++編譯出來的test函數符號前帶頭,後帶尾,這也是C++中有重載和C中沒有重載的原因之一。


發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *