天天看點

ELF符号表分析Symbol Table

Symbol Table

An object file's symbol table holds information needed to locate and relocate a program's symbolic definitions and references. A symbol table index is a subscript into this array. Index 0 both designates the first entry in the table and serves as the undefined symbol index. The contents of the initial entry are specified later in this section.

ELF檔案中的“符号表(symbol table)”包含的是程式中的符号資訊 -- 這些符号代表的或許是定義(例如定義全局變量時使用的變量名,或者定義函數時使用的函數名),或許代表的是引用(例如使用關鍵字extern聲明的變量或函數時使用的符号名稱)。當代表的是定義時,在連結階段連結器需要為它們重定位;當代表的是引用時,在連結階段連結器需要在其他編譯子產品定位到該符号的定義。 

符号表其實是所有符号資訊的集合統稱,即符号表是所有符号資訊一起組成的一個數組,是以一個符号表索引(symbol table index) 對應該數組中的一個(符号表)表項。

其中,索引值0有雙重含義:一般情況下代表的意思是指代符号表的第一個表項,然而一個未定義符号卻也使用STN_UNDEF(數值為0)來指定其節頭表索引值。

關于符号表第一表項的内容,下文會提到。

ELF符号表分析Symbol Table

A symbol table entry has the following format.

一個符号表表項的資料結構定義如下:

ELF符号表分析Symbol Table
/* Symbol table entry.  */

typedef struct
{
  Elf32_Word    st_name;                /* Symbol name (string tbl index) */
  Elf32_Addr    st_value;               /* Symbol value */
  Elf32_Word    st_size;                /* Symbol size */
  unsigned char st_info;                /* Symbol type and binding */
  unsigned char st_other;               /* Symbol visibility */
  Elf32_Section st_shndx;               /* Section index */
} Elf32_Sym;

typedef struct
{
  Elf64_Word    st_name;                /* Symbol name (string tbl index) */
  unsigned char st_info;                /* Symbol type and binding */
  unsigned char st_other;               /* Symbol visibility */
  Elf64_Section st_shndx;               /* Section index */
  Elf64_Addr    st_value;               /* Symbol value */
  Elf64_Xword   st_size;                /* Symbol size */
} Elf64_Sym;
           

* st_name 符号名稱

This member holds an index into the object file's symbol string table, which holds the character representations of the symbol names. If the value is non-zero, it represents a string table index that gives the symbol name. Otherwise, the symbol table entry has no name.

st_name成員其實不是一個字元串,而是一個數值,代表的是目标檔案中 字元串表 中的一個索引值, 那裡才真正存儲着該符号的名稱對應的字元串。如果st_name成員數值不為0,則代表該符号有符号名稱。否則,說明該符号沒有名稱。

NOTE:External C symbols have the same names in C and object files' symbol tables.

注意:在C程式中的 具有外部連結屬性的符号 的名稱 和 最後生成的目标檔案中的符号表中的符号名稱 是相同的,這點與C++不同。

* st_value 符号數值

This member gives the value of the associated symbol. Depending on the context, this may be an absolute value, an address, and so on; details appear below.

st_value成員給出了相應的符号值。這個符号值具體是什麼意思,是要依據上下文的(主要依據不同的符号屬性和不同的目标檔案),也許是個絕對值,也許是個位址值,等等。

* st_size 符号大小

Many symbols have associated sizes. For example, a data object's size is the number of bytes contained in the object. This member holds 0 if the symbol has no size or an unknown size.

很多類型的符号都是有大小屬性的。例如,一個資料對象的大小指的是它實際在目标檔案中占的位元組數。st_size成員如果是0的話,說明這個符号在目标檔案中不占用任何位元組數(例如common symbols)或者目前是未知大小的(例如undefined symbols)。

* st_info 符号的類型 和 綁定屬性

This member specifies the symbol's type and binding attributes. A list of the values and meanings appears below. The following code shows how to manipulate the values for both 32 and 64-bit objects.

成員st_info指定了符号的類型(低四位)和綁定屬性(高四位)。不同的符号類型/符号綁定屬性以及意義将在下文列出。下面的宏分别展現了如何操作成員st_info。通過st_info得到類型和綁定屬性,以及如何通過類型和綁定屬性而得到st_info。

ELF符号表分析Symbol Table

* st_other 符号的可見性

This member currently specifies a symbol's visibility. A list of the values and meanings appears below. The following code shows how to manipulate the values for both 32 and 64-bit objects. Other bits contain 0 and have no defined meaning.

st_other成員目前指定符号的可見性屬性。不同的可見性屬性值以及意義将在文章後續給出。下面的宏分别展示了在32位和64位機器上如何操作st_other。除了低兩位,其餘的位都為0并且沒有意義。

ELF符号表分析Symbol Table

* st_shndx  符号(關聯的節)的節頭表索引值

Every symbol table entry is defined in relation to some section. This member holds the relevant section header table index. As the sh_link and sh_info interpretation table and the related text describe, some section indexes indicate special meanings.

每一個符号表項所代表的特定符号資訊都是和一個特定的 “節” 相關聯的,st_shndx成員代表的就是這個特定“節”的節頭表索引(比如一個定義的全局變量global,那麼符号global的屬性st_shndx值應該就是.data節所對應的節頭表索引;定義的函數foo,那麼符号foo的屬性st_shndx值應該就是.text節所對應的節頭表索引)。部分符号的節頭表索引會有特殊的含義。

If this member contains SHN_XINDEX, then the actual section header index is too large to fit in this field. The actual value is contained in the associated section of type SHT_SYMTAB_SHNDX.

如果這個成員的值是SHN_XINDEX時,證明該符号關聯的節的節頭表索引值過大,超出了st_shndx所能代表的最大數值。那麼真正的節頭表索引值存儲在一個類型為SHT_SYMTAB_SHNDX的擴充節中。

Symbol Binding

A symbol's binding determines the linkage visibility and behavior.

一個符号的綁定屬性決定了該符号在連結階段的可見性以及連結時的處理方式。 

例如全局符号和本地符号的連結可見性是不同的,而當出現同名的全局符号和弱符号時,連結器會做出相應的處理(這點可參考下文對全局符号和弱符号不同點的描述)。

ELF符号表分析Symbol Table

* STB_LOCAL 本地符号

Local symbols are not visible outside the object file containing their definition. Local symbols of the same name may exist in multiple files without interfering with each other.

當一個符号的綁定屬性是STB_LOCAL時,則表明該符号的連結屬性是internal的,對其他目标檔案來說是不可見的,即不可通路。是以不同的目标檔案中的本地符号可以同名,它們彼此不會幹擾對方。

* STB_GLOBAL 全局符号

Global symbols are visible to all object files being combined. One file's definition of a global symbol will satisfy another file's undefined reference to the same global symbol.

當一個符号的綁定屬性是STB_GLOBAL時,則表明該符号的連結屬性是external的,對其他目标檔案來說是可見的,即可以通路。 

* STB_WEAK 弱符号

Weak symbols resemble global symbols, but their definitions have lower precedence.

當一個符号的綁定屬性是STB_WEAK時,則表明該符号是個弱符号,它和全局符号有類似的地方,即連結屬性也是external的。但是連結器處理弱符号的優先級相對全局符号要低,即當全局符号和弱符号同名時,連結器最後使用全局符号而忽略弱符号。 

* STB_LOOS through STB_HIOS

Values in this inclusive range are reserved for operating system-specific semantics.

* STB_LOPROC through STB_HIPROC

Values in this inclusive range are reserved for processor-specific semantics. If meanings are specified, the processor supplement explains them.

Global and weak symbols differ in two major ways.

全局符号和弱符号的差別主要在兩個方面。

When the link editor combines several relocatable object files, it does not allow multiple definitions of STB_GLOBAL symbols with the same name. On the other hand, if a defined global symbol exists, the appearance of a weak symbol with the same name will not cause an error. The link editor honors the global definition and ignores the weak ones. Similarly, if a common symbol exists (that is, a symbol whose st_shndx field holds SHN_COMMON), the appearance of a weak symbol with the same name will not cause an error. The link editor honors the common definition and ignores the weak ones.

當連結器連結若幹可重定位檔案時,它是不允許具有STB_GLOBAL屬性的符号以相同名字進行重複定義的。而如果一個已定義的全局符号存在,則即便另一個具有相同名字的弱符号存在也不會引起錯誤。連結器将認可全局符号的定義而忽略弱符号的定義。與此類似的,如果一個符号被放在COMMON塊(就是說這個符号的st_shndx成員的值為SHN_COMMON),則一個同名的弱符号也不會引起錯誤。連結器同樣認可放在COMMON塊符号的定義而忽略其他的弱符号。

-----------------------------------------------------------------------

>>>     STB_GLOBAL > SHN_COMMON > STB_WEAK

-----------------------------------------------------------------------

When the link editor searches archive libraries [see ``Archive File'' in Chapter 7], it extracts archive members that contain definitions of undefined global symbols. The member's definition may be either a global or a weak symbol. The link editor does not extract archive members to resolve undefined weak symbols. Unresolved weak symbols have a zero value.

在連結靜态庫的情況下:

(1)當連結器遇到一個未定義的全局符号(global symbol)時,連結器會去提取靜态庫,試圖找到這個符号定義。在靜态庫中,這個符号可以是全局符号,也可以是弱符号。

(2)當連結器遇到一個未定義的弱符号(weak symbols)時,連結器是不會去提取靜态庫的,而是直接将該弱符号的值賦為0。(可以參考文章《Fun with weak symbols》。試驗結果發現:如果引用了靜态庫中的非弱符号,那麼即使連結器遇到了一個未定義的弱符号,依然會去靜态庫中解析符号)

NOTE: The behavior of weak symbols in areas not specified by this document is implementation defined. Weak symbols are intended primarily for use in system software. Applications using weak symbols are unreliable since changes in the runtime environment might cause the execution to fail.

注意: 弱符号在上述規則之外地方的行為是實作相關的。弱符号主要用于系統軟體中,不推薦在應用程式中使用弱符号,因為在運作時,弱符号很容易被覆寫掉。

In each symbol table, all symbols with STB_LOCAL binding precede the weak and global symbols. As ``Sections'', above describes, a symbol table section's sh_info section header member holds the symbol table index for the first non-local symbol.

在符号表中,不同綁定屬性的符号所在位置是不同的 -- 所有的本地符号都被安放在符号表的前頭,緊接着的才是全局符号和弱符号。前文提到過,一個符号表節對應的節頭表項的節頭表成員sh_info中的數值代表的是第一個綁定屬性為非STB_LOCAL的符号的符号表索引值(即最後一個綁定屬性為STB_LOCAL符号的符号表索引值加1)。

Symbol Types

A symbol's type provides a general classification for the associated entity.

一個符号的類型為該符号關聯的實體進行分類。

ELF符号表分析Symbol Table

* STT_NOTYPE

The symbol's type is not specified.

當符号類型是STT_NOTYPE時,表明該符号未指定類型或者目前還不知道該符号的類型。

* STT_OBJECT

The symbol is associated with a data object, such as a variable, an array, and so on.

當符号類型是STT_OBJECT時,表明該符号關聯的實體是個資料對象,例如一個變量,數組等。

* STT_FUNC

The symbol is associated with a function or other executable code.

當符号類型是STT_FUNC時,表明該符号關聯的實體是個函數或者其他的可執行代碼。

* STT_SECTION

The symbol is associated with a section. Symbol table entries of this type exist primarily for relocation and normally have STB_LOCAL binding.

當符号類型是STT_SECTION時,表明該符号關聯的實體是個節。一般符号表中的一個符号是這個類型時,主要是用于重定位的目的,并且其綁定屬性一般情況下是STB_LOCAL。

* STT_FILE

Conventionally, the symbol's name gives the name of the source file associated with the object file. A file symbol has STB_LOCAL binding, its section index is SHN_ABS, and it precedes the other STB_LOCAL symbols for the file, if it is present.

通常情況下,當一個符号的類型是STT_FILE時,這個符号的名稱就是該目标檔案相關聯的源檔案的名稱。這種類型的符号的綁定屬性是STB_LOCAL的,與它相關的節的節頭表索引值為SHN_ABS,并且如果在符号表中存在此種符号的話,那麼其位置排在本地符号(STB_LOCAL)的前頭。

* STT_COMMON

The symbol labels an uninitialized common block. See below for details.

當符号類型是STT_COMMON時,表明該符号是個公用塊資料對象,并且這個公用塊在目标檔案中實際是未被配置設定空間的。 

* STT_TLS

The symbol specifies a Thread-Local Storage entity. When defined, it gives the assigned offset for the symbol, not the actual address. Symbols of type STT_TLS can be referenced by only special thread-local storage relocations and thread-local storage relocations can only reference symbols with type STT_TLS. Implementation need not support thread-local storage.

當符号的類型是STT_TLS時,表明該符号對應變量存儲線上程局部存儲内。

* STT_LOOS through STT_HIOS

Values in this inclusive range are reserved for operating system-specific semantics.

* STT_LOPROC through STT_HIPROC

Values in this inclusive range are reserved for processor-specific semantics. If meanings are specified, the processor supplement explains them.

Function symbols (those with type STT_FUNC) in shared object files have special significance. When another object file references a function from a shared object, the link editor automatically creates a procedure linkage table entry for the referenced symbol. Shared object symbols with types other than STT_FUNC will not be referenced automatically through the procedure linkage table.

連結器對那些存在于共享庫中的函數符号(即那些符号類型為STT_FUNC的符号)有特殊的處理。當其它的目标檔案引用了共享庫中的函數時,連結器會自動的為這個引用符号建立一個 程式連接配接表(PLT)表項。而共享庫中符号類型為非STT_FUNC的符号則不是通過 程式連接配接表(PLT)自動通路。例如,可執行程式引用共享庫中的共享對象(即符号類型為STT_OBJECT的符号)時,連結器會建立一個copy reloc來解決。

Symbols with type STT_COMMON label uninitialized common blocks. In relocatable objects, these symbols are not allocated and must have the special section index SHN_COMMON (see below). In shared objects and executables these symbols must be allocated to some section in the defining object.

當一個符号的類型是STT_COMMON時,則表明該符号是個公用塊資料對象,且這個公用塊在目标檔案中實際是未被配置設定空間。在可重定位檔案中,并不會為這些符号配置設定空間,并且與之相關的節的節頭表索引值必須是SHN_COMMON。但是在共享庫和可執行檔案中,這些符号必須在相應的節(bss)中配置設定空間。

In relocatable objects, symbols with type STT_COMMON are treated just as other symbols with index SHN_COMMON. If the link-editor allocates space for the SHN_COMMON symbol in an output section of the object it is producing, it must preserve the type of the output symbol as STT_COMMON.

在可重定位檔案中,對符号類型是STT_COMMON的符号的處理方式跟那些與之相關節的節頭表索引值為SHN_COMMON的符号的處理方式是一樣的。如果連結器在生成可執行檔案或者共享庫時,為這類符号配置設定了空間的話(.bss),那麼必須保留該符号的類型為STT_COMMON.(why? -- 下文給出)

When the dynamic linker encounters a reference to a symbol that resolves to a definition of type STT_COMMON, it may (but is not required to) change its symbol resolution rules as follows: instead of binding the reference to the first symbol found with the given name, the dynamic linker searches for the first symbol with that name with type other than STT_COMMON. If no such symbol is found, it looks for the STT_COMMON definition of that name that has the largest size.

當動态連結器(/lib/ld-linux.so.2)遇到一個符号引用是指向符号類型是STT_COMMON的符号定義時,那麼動态連結器也許(但是并不推薦這樣做)會改變符号解析的法則如下:并不會像其它類型的符号一樣,将該引用綁定到第一個同名的符号定義,而是先搜尋類型為非STT_COMMON的符号定義,如果存在則綁定到該符号定義。如果不存在,那麼動态連結器這才在符号類型為STT_COMMON的定義中搜尋,并且最終選擇占用記憶體空間最大的那個。

Symbol Visibility

A symbol's visibility, although it may be specified in a relocatable object, defines how that symbol may be accessed once it has become part of an executable or shared object.

盡管我們可以在編譯階段(通過在source code中或者編譯器選項指定符号的可見性)和靜态連結階段(通過export list檔案)指定符号的可見性屬性,但其實可見性屬性控制的是一個符号在運作時的解析行為。

ELF符号表分析Symbol Table

* STV_DEFAULT

The visibility of symbols with the STV_DEFAULT attribute is as specified by the symbol's binding type. That is, global and weak symbols are visible outside of their defining component (executable file or shared object). Local symbols are hidden, as described below. Global and weak symbols are also preemptable, that is, they may by preempted by definitions of the same name in another component.

當符号的可見性是STV_DEFAULT時,那麼該符号的可見性由符号的綁定屬性決定。這類情況下,(可執行檔案和共享庫中的)全局符号和弱符号預設是外部可通路的,本地符号預設外部是無法被通路的。但是,可見性是STV_DEFAULT的全局符号和弱符号是可被覆寫的。什麼意思?舉個最典型的例子,共享庫中的可見性值為STV_DEFAULTD的全局符号和弱符号是可被可執行檔案中的同名符号覆寫的。

NOTE: An implementation may restrict the set of global and weak symbols that are externally visible.

注意:一個具體的實作可能會限制對外可通路的全局符号和弱符号的數量。

* STV_PROTECTED

A symbol defined in the current component is protected if it is visible in other components but not preemptable, meaning that any reference to such a symbol from within the defining component must be resolved to the definition in that component, even if there is a definition in another component that would preempt by the default rules. A symbol with STB_LOCAL binding may not have STV_PROTECTED visibility. If a symbol definition with STV_PROTECTED visibility from a shared object is taken as resolving a reference from an executable or another shared object, the SHN_UNDEF symbol table entry created has STV_DEFAULT visibility.

當符号的可見性是STV_PROTECTED時,它是外部可見的,這點跟可見性是STV_DEFAULT的一樣,但不同的是它是不可覆寫的。這樣的符号在共享庫中比較常見。不可覆寫意味着如果是在該符号所在的共享庫中通路這個符号,那麼就一定是通路的這個符号,盡管可執行檔案中也會存在同樣名字的符号也不會被覆寫掉。

規定綁定屬性為STB_LOCAL的符号的可見性不可以是STV_PROTECTED。

NOTE: The presence of the 

STV_PROTECTED

 flag on a symbol in a given load module does not affect the symbol resolution rules for references to that symbol from outside the containing load module.

* STV_HIDDEN

A symbol defined in the current component is hidden if its name is not visible to other components. Such a symbol is necessarily protected. This attribute may be used to control the external interface of a component. Note that an object named by such a symbol may still be referenced from another component if its address is passed outside.

A hidden symbol contained in a relocatable object must be either removed or converted to 

STB_LOCAL

 binding by the link-editor when the relocatable object is included in an executable file or shared object.

當符号的可見性是STV_HIDDEN時,證明該符号是外部無法通路的。這個屬性主要用來控制共享庫對外接口的數量。需要注意的是,一個可見性為STV_HIDDEN的資料對象,如果能擷取到該符号的位址,那麼依然是可以通路或者修改該資料對象的。

在可重定位檔案中,如果一個符号的可見性是STV_HIDDEN的話,那麼在連結生成可執行檔案或者共享庫的過程中,該符号要麼被删除,要麼綁定屬性變成STB_LOCAL。

* STV_INTERNAL

The meaning of this visibility attribute may be defined by processor supplements to further constrain hidden symbols. A processor supplement's definition should be such that generic tools can safely treat internal symbols as hidden.

An internal symbol contained in a relocatable object must be either removed or converted to 

STB_LOCAL

 binding by the link-editor when the relocatable object is included in an executable file or shared object.

Symbol Values

Symbol table entries for different object file types have slightly different interpretations for the st_value member.

在不同的目标檔案中,對成員st_value的意義解釋也是不同的。

  • In relocatable files, st_value holds alignment constraints for a symbol whose section index is SHN_COMMON.
在可重定位檔案中,若符号的st_shndx等于SHN_COMMON,則st_value的值代表的是該符号的對齊位元組數。
  • In relocatable files, st_value holds a section offset for a defined symbol. st_value is an offset from the beginning of the section that st_shndx identifies.
在可重定位檔案中,若一個符号是已定義的,那麼st_value的值代表的是該符号其所在的節中的偏移量 -- 當然了,這個節是由st_shndx指定的。
  • In executable and shared object files, st_value holds a virtual address. To make these files' symbols more useful for the dynamic linker, the section offset (file interpretation) gives way to a virtual address (memory interpretation) for which the section number is irrelevant.
在可執行檔案和共行庫中,一個已定義符号的st_value的值不再是一個節偏移量,而是一個虛拟位址,因為動态連結器需要知道符号的記憶體位址。 -- 因為虛拟位址是與節無關的,是以這種情況下,我們不需要關心st_shndx的值。

Although the symbol table values have similar meanings for different object files, the data allows efficient access by the appropriate programs.

綜合以上三點可知,在不同的目标檔案中st_value的值代表的含義不同。這樣設計是有原因的:在靜态連結階段,連結器需要的是符号在檔案中的位置資訊,而在程式運作時,動态連結器需要的是卻符号在記憶體中的位置資訊。

以下程式用于輸出ELF所有符号的資訊(即sh_type為SHT_SYMTAB或者SHT_DYNSYM),模仿readelf的--syms選項:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <link.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <elf.h>
#include <error.h>
#include <errno.h>

#define ELFW(type)	_ELFW (ELF, __ELF_NATIVE_CLASS, type)
#define _ELFW(e,w,t)	_ELFW_1 (e, w, _##t)
#define _ELFW_1(e,w,t)	e##w##t

static const char *st_type(unsigned int stype) 
{
	switch (stype) {
		case STT_NOTYPE: return "NOTYPE";
		case STT_OBJECT: return "OBJECT";
		case STT_FUNC: return "FUNC";
		case STT_SECTION: return "SECTION";
		case STT_FILE: return "FILE";
		case STT_COMMON: return "COMMON";
		case STT_TLS: return "TLS";
		case STT_LOOS ... STT_HIOS: return "OS_SPEC";
		case STT_LOPROC ... STT_HIPROC: return "PROC_SPEC";
		default: return "<unknown symbol type>";
	}
}

static const char *st_bind(unsigned int stbind)
{
	switch (stbind) {
		case STB_LOCAL: return "LOCAL";
		case STB_GLOBAL: return "GLOBAL";
		case STB_WEAK: return "WEAK";
//		case STB_GNU_UNIQUE: return "UNIQUE";
		case STB_LOOS ... STB_HIOS: return "OS";
		case STB_LOPROC ... STB_HIPROC: return "PROC";
		default: return "<unknown symbol bind>";
	}
}

static const char *st_vis(unsigned int svis)
{
	switch (svis) {
		case STV_DEFAULT: return "DEFAULT";
		case STV_INTERNAL: return "INTERNAL";
		case STV_HIDDEN: return "HIDDEN";
		case STV_PROTECTED: return "PROTECTED";
		default: return "<unknown symbol vis>";
	}
}

static const char *st_shndx(unsigned int shndx)
{
	static char s_shndx[32];

	switch (shndx) {
		case SHN_UNDEF: return "UND";	
		case SHN_ABS: return "ABS";
		case SHN_COMMON: return "COMMON";
		case SHN_LOPROC ... SHN_HIPROC: return "PRC";
		case SHN_LOOS ... SHN_HIOS: return "OS";
		default:  
			(void)snprintf(s_shndx, sizeof(s_shndx), "%u", shndx); 
			return (const char *)s_shndx;
	}
}

static void print_syms(ElfW(Shdr) *shdrs, const char *shstrtab,  
		const char *shname, ElfW(Sym) *syms, size_t entries, const char *strtab)
{
	printf("Symbol table '%s' contains %zu entries:\n", shname, entries);
	printf("%7s%9s%14s%5s%8s%6s%9s%5s\n", "Num:", "Value", "Size", "Type",
	    "Bind", "Vis", "Ndx", "Name");

	for (size_t i = 0; i < entries; i++) {
		ElfW(Sym) *sym = &syms[i];
	
		printf("%6zu:", i);
		printf(" %16.16jx", (uintmax_t)sym->st_value);
		printf(" %5ju", (uintmax_t)sym->st_size);
		printf(" %-7s", st_type(ELFW(ST_TYPE)(sym->st_info)));
		printf(" %-6s", st_bind(ELFW(ST_BIND)(sym->st_info)));
		printf(" %-8s", st_vis(ELFW(ST_VISIBILITY)(sym->st_other)));
		printf(" %3s", st_shndx(sym->st_shndx));
		if (strcmp("SECTION", st_type(ELFW(ST_TYPE)(sym->st_info))) == 0) {
			printf(" %s", shstrtab + shdrs[sym->st_shndx].sh_name);	
		} else {
			printf(" %s", strtab + sym->st_name);
		}
		printf("\n");
	}
}
 
int main(int argc, char *argv[])
{
	int fd;
	char *file_mmbase;
	struct stat file_status;
	size_t fsize;
	ElfW(Ehdr) *ehdr;
	ElfW(Shdr) *shdrs;
	size_t shnum, shstrndx;
	const char *shstrtab;

//	[ 6] .dynstr           STRTAB          0000000000000468 000468 0000dd 00   A  0   0  1
//	[32] .strtab           STRTAB          0000000000000000 003d28 000322 00      0   0  1
//	[33] .shstrtab         STRTAB          0000000000000000 00404a 00013e 00      0   0  1
 
	if (argc != 2) {
		error(EXIT_FAILURE, 0, "Usage: %s file-name", argv[0]);
	}
 
	if ((fd = open(argv[1], O_RDONLY)) < 0) {
		error(EXIT_FAILURE, errno, "open %s failed", argv[1]);
	}
 
	if (fstat(fd, &file_status) < 0) {
		error(EXIT_FAILURE, errno, "get file %s info err", argv[1]);
	}
	fsize = (size_t)file_status.st_size;
 
	if ((file_mmbase = mmap(NULL, fsize, PROT_READ, 
				MAP_PRIVATE, fd, (off_t)0)) == MAP_FAILED) {
		error(EXIT_FAILURE, errno, "mmap file %s err", argv[1]);
	}
 
	ehdr = (ElfW(Ehdr) *)file_mmbase;
	shdrs = (ElfW(Shdr) *)(file_mmbase + ehdr->e_shoff);
	shnum = ehdr->e_shnum == 0 ? shdrs[0].sh_size : ehdr->e_shnum;
	shstrndx = ehdr->e_shstrndx == SHN_XINDEX ? shdrs[0].sh_link : ehdr->e_shstrndx;
	shstrtab = file_mmbase + shdrs[shstrndx].sh_offset;

	for (size_t i = 0; i < shnum; i++) {
		ElfW(Shdr) *shdr = &shdrs[i];	

		if (shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM) {
			const char *shname = shstrtab + shdr->sh_name;
			ElfW(Sym) *syms = (ElfW(Sym *))(file_mmbase + shdr->sh_offset); 
			size_t entries = shdr->sh_size / shdr->sh_entsize;
			// sh_info: One greater than the symbol table index of 
			// 			the last local symbol (binding STB_LOCAL).
			// printf("shdr->sh_info = %u\n", shdr->sh_info);
			// sh_link: .strtab or .dynstr (The section header index of 
			// 			the associated string table.)
			const char *strtab = file_mmbase + shdrs[shdr->sh_link].sh_offset;
			print_syms(shdrs, shstrtab, shname, syms, entries, strtab);	
		}
	}

	(void)munmap(file_mmbase, fsize);
	(void)close(fd);
	
	exit(EXIT_SUCCESS);
}
           

程式輸出結果如下:

[00:26:[email protected]:/tmp]$ readelf --syms --wide print_symbol 

Symbol table '.dynsym' contains 18 entries:

   Num:    Value          Size Type    Bind   Vis      Ndx Name

     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 

     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable

     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

    10: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)

    16: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable

    17: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND [email protected]_2.2.5 (2)

Symbol table '.symtab' contains 82 entries:

   Num:    Value          Size Type    Bind   Vis      Ndx Name

     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 

     1: 0000000000000238     0 SECTION LOCAL  DEFAULT    1 

     2: 0000000000000254     0 SECTION LOCAL  DEFAULT    2 

     3: 0000000000000274     0 SECTION LOCAL  DEFAULT    3 

     4: 0000000000000298     0 SECTION LOCAL  DEFAULT    4 

     5: 00000000000002b8     0 SECTION LOCAL  DEFAULT    5 

     6: 0000000000000468     0 SECTION LOCAL  DEFAULT    6 

     7: 000000000000053a     0 SECTION LOCAL  DEFAULT    7 

     8: 0000000000000560     0 SECTION LOCAL  DEFAULT    8 

     9: 0000000000000580     0 SECTION LOCAL  DEFAULT    9 

    10: 0000000000000640     0 SECTION LOCAL  DEFAULT   10 

    11: 0000000000000760     0 SECTION LOCAL  DEFAULT   11 

    12: 0000000000000780     0 SECTION LOCAL  DEFAULT   12 

    13: 0000000000000850     0 SECTION LOCAL  DEFAULT   13 

    14: 0000000000000860     0 SECTION LOCAL  DEFAULT   14 

    15: 0000000000001320     0 SECTION LOCAL  DEFAULT   15 

    16: 0000000000001330     0 SECTION LOCAL  DEFAULT   16 

    17: 0000000000001590     0 SECTION LOCAL  DEFAULT   17 

    18: 0000000000001600     0 SECTION LOCAL  DEFAULT   18 

    19: 0000000000201d60     0 SECTION LOCAL  DEFAULT   19 

    20: 0000000000201d68     0 SECTION LOCAL  DEFAULT   20 

    21: 0000000000201d70     0 SECTION LOCAL  DEFAULT   21 

    22: 0000000000201f60     0 SECTION LOCAL  DEFAULT   22 

    23: 0000000000202000     0 SECTION LOCAL  DEFAULT   23 

    24: 0000000000202020     0 SECTION LOCAL  DEFAULT   24 

    25: 0000000000000000     0 SECTION LOCAL  DEFAULT   25 

    26: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c

    27: 0000000000000890     0 FUNC    LOCAL  DEFAULT   14 deregister_tm_clones

    28: 00000000000008d0     0 FUNC    LOCAL  DEFAULT   14 register_tm_clones

    29: 0000000000000920     0 FUNC    LOCAL  DEFAULT   14 __do_global_dtors_aux

    30: 0000000000202020     1 OBJECT  LOCAL  DEFAULT   24 completed.7696

    31: 0000000000201d68     0 OBJECT  LOCAL  DEFAULT   20 __do_global_dtors_aux_fini_array_entry

    32: 0000000000000960     0 FUNC    LOCAL  DEFAULT   14 frame_dummy

    33: 0000000000201d60     0 OBJECT  LOCAL  DEFAULT   19 __frame_dummy_init_array_entry

    34: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS print_symbol.c

    35: 000000000000096a   139 FUNC    LOCAL  DEFAULT   14 st_type

    36: 00000000000009f5   103 FUNC    LOCAL  DEFAULT   14 st_bind

    37: 0000000000000a5c    77 FUNC    LOCAL  DEFAULT   14 st_vis

    38: 0000000000000aa9   145 FUNC    LOCAL  DEFAULT   14 st_shndx

    39: 0000000000202040    32 OBJECT  LOCAL  DEFAULT   24 s_shndx.4238

    40: 0000000000000b3a   575 FUNC    LOCAL  DEFAULT   14 print_syms

    41: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c

    42: 00000000000017bc     0 OBJECT  LOCAL  DEFAULT   18 __FRAME_END__

    43: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 

    44: 0000000000001310    15 FUNC    LOCAL  DEFAULT   14 fstat

    45: 0000000000201d68     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_end

    46: 0000000000201d70     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC

    47: 0000000000201d60     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_start

    48: 0000000000001590     0 NOTYPE  LOCAL  DEFAULT   17 __GNU_EH_FRAME_HDR

    49: 0000000000201f60     0 OBJECT  LOCAL  DEFAULT   22 _GLOBAL_OFFSET_TABLE_

    50: 0000000000001300     2 FUNC    GLOBAL DEFAULT   14 __libc_csu_fini

    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    53: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable

    54: 0000000000202000     0 NOTYPE  WEAK   DEFAULT   23 data_start

    55: 0000000000202010     0 NOTYPE  GLOBAL DEFAULT   23 _edata

    56: 0000000000001320     0 FUNC    GLOBAL DEFAULT   15 _fini

    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    58: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    59: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    60: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    61: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    62: 0000000000202000     0 NOTYPE  GLOBAL DEFAULT   23 __data_start

    63: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    64: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

    65: 0000000000202008     0 OBJECT  GLOBAL HIDDEN    23 __dso_handle

    66: 0000000000001330     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used

    67: 0000000000001290   101 FUNC    GLOBAL DEFAULT   14 __libc_csu_init

    68: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    69: 0000000000202060     0 NOTYPE  GLOBAL DEFAULT   24 _end

    70: 0000000000000860    43 FUNC    GLOBAL DEFAULT   14 _start

    71: 0000000000202010     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start

    72: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    73: 0000000000000d79  1301 FUNC    GLOBAL DEFAULT   14 main

    74: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    75: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    76: 0000000000001310    15 FUNC    GLOBAL HIDDEN    14 __fstat

    77: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    78: 0000000000202010     0 OBJECT  GLOBAL HIDDEN    23 __TMC_END__

    79: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable

    80: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND [email protected]@GLIBC_2.2.5

    81: 0000000000000760     0 FUNC    GLOBAL DEFAULT   11 _init

[00:26:[email protected]:/tmp]$ ./print_symbol print_symbol 

Symbol table '.dynsym' contains 18 entries:

   Num:    Value          Size Type    Bind   Vis      Ndx Name

     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 

     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND putchar

     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location

     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable

     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mmap

     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf

     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND snprintf

     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND close

     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main

     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcmp

    10: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __fxstat

    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND munmap

    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND error

    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND open

    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit

    16: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable

    17: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize

Symbol table '.symtab' contains 82 entries:

   Num:    Value          Size Type    Bind   Vis      Ndx Name

     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 

     1: 0000000000000238     0 SECTION LOCAL  DEFAULT    1 

     2: 0000000000000254     0 SECTION LOCAL  DEFAULT    2 

     3: 0000000000000274     0 SECTION LOCAL  DEFAULT    3 

     4: 0000000000000298     0 SECTION LOCAL  DEFAULT    4 

     5: 00000000000002b8     0 SECTION LOCAL  DEFAULT    5 

     6: 0000000000000468     0 SECTION LOCAL  DEFAULT    6 

     7: 000000000000053a     0 SECTION LOCAL  DEFAULT    7 

     8: 0000000000000560     0 SECTION LOCAL  DEFAULT    8 

     9: 0000000000000580     0 SECTION LOCAL  DEFAULT    9 

    10: 0000000000000640     0 SECTION LOCAL  DEFAULT   10 

    11: 0000000000000760     0 SECTION LOCAL  DEFAULT   11 

    12: 0000000000000780     0 SECTION LOCAL  DEFAULT   12 

    13: 0000000000000850     0 SECTION LOCAL  DEFAULT   13 

    14: 0000000000000860     0 SECTION LOCAL  DEFAULT   14 

    15: 0000000000001320     0 SECTION LOCAL  DEFAULT   15 

    16: 0000000000001330     0 SECTION LOCAL  DEFAULT   16 

    17: 0000000000001590     0 SECTION LOCAL  DEFAULT   17 

    18: 0000000000001600     0 SECTION LOCAL  DEFAULT   18 

    19: 0000000000201d60     0 SECTION LOCAL  DEFAULT   19 

    20: 0000000000201d68     0 SECTION LOCAL  DEFAULT   20 

    21: 0000000000201d70     0 SECTION LOCAL  DEFAULT   21 

    22: 0000000000201f60     0 SECTION LOCAL  DEFAULT   22 

    23: 0000000000202000     0 SECTION LOCAL  DEFAULT   23 

    24: 0000000000202020     0 SECTION LOCAL  DEFAULT   24 

    25: 0000000000000000     0 SECTION LOCAL  DEFAULT   25 

    26: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c

    27: 0000000000000890     0 FUNC    LOCAL  DEFAULT   14 deregister_tm_clones

    28: 00000000000008d0     0 FUNC    LOCAL  DEFAULT   14 register_tm_clones

    29: 0000000000000920     0 FUNC    LOCAL  DEFAULT   14 __do_global_dtors_aux

    30: 0000000000202020     1 OBJECT  LOCAL  DEFAULT   24 completed.7696

    31: 0000000000201d68     0 OBJECT  LOCAL  DEFAULT   20 __do_global_dtors_aux_fini_array_entry

    32: 0000000000000960     0 FUNC    LOCAL  DEFAULT   14 frame_dummy

    33: 0000000000201d60     0 OBJECT  LOCAL  DEFAULT   19 __frame_dummy_init_array_entry

    34: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS print_symbol.c

    35: 000000000000096a   139 FUNC    LOCAL  DEFAULT   14 st_type

    36: 00000000000009f5   103 FUNC    LOCAL  DEFAULT   14 st_bind

    37: 0000000000000a5c    77 FUNC    LOCAL  DEFAULT   14 st_vis

    38: 0000000000000aa9   145 FUNC    LOCAL  DEFAULT   14 st_shndx

    39: 0000000000202040    32 OBJECT  LOCAL  DEFAULT   24 s_shndx.4238

    40: 0000000000000b3a   575 FUNC    LOCAL  DEFAULT   14 print_syms

    41: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c

    42: 00000000000017bc     0 OBJECT  LOCAL  DEFAULT   18 __FRAME_END__

    43: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 

    44: 0000000000001310    15 FUNC    LOCAL  DEFAULT   14 fstat

    45: 0000000000201d68     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_end

    46: 0000000000201d70     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC

    47: 0000000000201d60     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_start

    48: 0000000000001590     0 NOTYPE  LOCAL  DEFAULT   17 __GNU_EH_FRAME_HDR

    49: 0000000000201f60     0 OBJECT  LOCAL  DEFAULT   22 _GLOBAL_OFFSET_TABLE_

    50: 0000000000001300     2 FUNC    GLOBAL DEFAULT   14 __libc_csu_fini

    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    53: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable

    54: 0000000000202000     0 NOTYPE  WEAK   DEFAULT   23 data_start

    55: 0000000000202010     0 NOTYPE  GLOBAL DEFAULT   23 _edata

    56: 0000000000001320     0 FUNC    GLOBAL DEFAULT   15 _fini

    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    58: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    59: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    60: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    61: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    62: 0000000000202000     0 NOTYPE  GLOBAL DEFAULT   23 __data_start

    63: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    64: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

    65: 0000000000202008     0 OBJECT  GLOBAL HIDDEN    23 __dso_handle

    66: 0000000000001330     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used

    67: 0000000000001290   101 FUNC    GLOBAL DEFAULT   14 __libc_csu_init

    68: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    69: 0000000000202060     0 NOTYPE  GLOBAL DEFAULT   24 _end

    70: 0000000000000860    43 FUNC    GLOBAL DEFAULT   14 _start

    71: 0000000000202010     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start

    72: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    73: 0000000000000d79  1301 FUNC    GLOBAL DEFAULT   14 main

    74: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    75: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    76: 0000000000001310    15 FUNC    GLOBAL HIDDEN    14 __fstat

    77: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]@GLIBC_2.2.5

    78: 0000000000202010     0 OBJECT  GLOBAL HIDDEN    23 __TMC_END__

    79: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable

    80: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND [email protected]@GLIBC_2.2.5

    81: 0000000000000760     0 FUNC    GLOBAL DEFAULT   11 _init

以下是利用libelf庫來輸出符号表的内容:

#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>

static const char *st_type(unsigned int stype) 
{
	switch (stype) {
		case STT_NOTYPE: return "NOTYPE";
		case STT_OBJECT: return "OBJECT";
		case STT_FUNC: return "FUNC";
		case STT_SECTION: return "SECTION";
		case STT_FILE: return "FILE";
		case STT_COMMON: return "COMMON";
		case STT_TLS: return "TLS";
		case STT_LOOS ... STT_HIOS: return "OS_SPEC";
		case STT_LOPROC ... STT_HIPROC: return "PROC_SPEC";
		default: return "<unknown symbol type>";
	}
}

static const char *st_bind(unsigned int stbind)
{
	switch (stbind) {
		case STB_LOCAL: return "LOCAL";
		case STB_GLOBAL: return "GLOBAL";
		case STB_WEAK: return "WEAK";
//		case STB_GNU_UNIQUE: return "UNIQUE";
		case STB_LOOS ... STB_HIOS: return "OS";
		case STB_LOPROC ... STB_HIPROC: return "PROC";
		default: return "<unknown symbol bind>";
	}
}

static const char *st_vis(unsigned int svis)
{
	switch (svis) {
		case STV_DEFAULT: return "DEFAULT";
		case STV_INTERNAL: return "INTERNAL";
		case STV_HIDDEN: return "HIDDEN";
		case STV_PROTECTED: return "PROTECTED";
		default: return "<unknown symbol vis>";
	}
}

static const char *st_shndx(unsigned int shndx)
{
	static char s_shndx[32];

	switch (shndx) {
		case SHN_UNDEF: return "UND";	
		case SHN_ABS: return "ABS";
		case SHN_COMMON: return "COMMON";
		case SHN_LOPROC ... SHN_HIPROC: return "PRC";
		case SHN_LOOS ... SHN_HIOS: return "OS";
		default:  
			(void)snprintf(s_shndx, sizeof(s_shndx), "%u", shndx); 
			return (const char *)s_shndx;
	}
}

static const char *get_symbol_name(Elf *pelf, size_t strtabndx, GElf_Sym *sym)
{
	if (GELF_ST_TYPE(sym->st_info) == STT_SECTION) {
		// get shstrndx
		size_t shstrndx;
		if (elf_getshdrstrndx(pelf, &shstrndx) == -1) {
			errx(EXIT_FAILURE, "getshdrstrndx() failed: %s.", elf_errmsg(-1));
		}
	
		Elf_Scn *destscn;
		GElf_Shdr destshdr;
	   
		if ((destscn = elf_getscn(pelf, sym->st_shndx)) == NULL) {
			errx(EXIT_FAILURE, "elf_getscn() failed: %s.", elf_errmsg(-1));
		}
		if (gelf_getshdr(destscn, &destshdr) != &destshdr) {
			errx(EXIT_FAILURE, "gelf_getshdr() failed: %s.", elf_errmsg(-1));
		}
		return elf_strptr(pelf, shstrndx, destshdr.sh_name);
	}
	
	return  elf_strptr(pelf, strtabndx, sym->st_name);
}

static void print_syms(Elf *pelf, Elf_Scn *symscn, GElf_Shdr *symshdr)
{
	Elf_Data *symdata;

	if ((symdata = elf_getdata(symscn, NULL)) == NULL) {
		errx(EXIT_FAILURE, "elf_getdata() failed: %s.", elf_errmsg(-1));
	}

	if (symdata->d_type != ELF_T_SYM) {
		errx(EXIT_FAILURE, "Elf_Type is not ELF_T_SYM.");	
	}

	if (symdata->d_size <= 0) {
		errx(EXIT_FAILURE, "Section data size is wrong.");	
	}

	assert(symshdr->sh_size == symdata->d_size);

	// get shstrndx
	size_t shstrndx;
	if (elf_getshdrstrndx(pelf, &shstrndx) == -1) {
		errx(EXIT_FAILURE, "getshdrstrndx() failed: %s.", elf_errmsg(-1));	
	}

	const char *symshdrname = elf_strptr(pelf, shstrndx, symshdr->sh_name);
	if (symshdrname == NULL) {
		errx(EXIT_FAILURE, "elf_strptr() failed: %s,", elf_errmsg(-1));	
	}

	size_t entries; 

#if 0
	// 1
	entries = symshdr->sh_size / symshdr->sh_entsize;
#endif

#if 0
	// 2 
	size_t entsize = gelf_fsize(pelf, ELF_T_SYM, 1, EV_CURRENT); 
	entries = symshdr->sh_size / entsize;
#endif
	
#if 1
	// 3
	size_t entsize = gelf_fsize(pelf, ELF_T_SYM, 1, EV_CURRENT); 
	entries = symdata->d_size / entsize;
#endif

	printf("\nSymbol table '%s' contains %zu entries:\n", symshdrname, entries);
	printf("%7s%9s%14s%5s%8s%6s%9s%5s\n", "Num:", "Value", "Size", "Type",
	    "Bind", "Vis", "Ndx", "Name");

	for (size_t i = 0; i < entries; i++) {
		GElf_Sym sym;
		if (gelf_getsym(symdata, i, &sym) != &sym) {
			errx(EXIT_FAILURE, "gelf_getsym() failed: %s.", elf_errmsg(-1));
		}

		printf("%6zu:", i);
		printf(" %16.16jx", (uintmax_t)sym.st_value);
		printf(" %5ju", (uintmax_t)sym.st_size);
		printf(" %-7s", st_type(GELF_ST_TYPE(sym.st_info)));
		printf(" %-6s", st_bind(GELF_ST_BIND(sym.st_info)));
		printf(" %-8s", st_vis(GELF_ST_VISIBILITY(sym.st_other)));
		printf(" %3s", st_shndx(sym.st_shndx));
		// sh_info: One greater than the symbol table index of 
		// 			the last local symbol (binding STB_LOCAL).
		// printf("shdr->sh_info = %u\n", shdr->sh_info);
		// sh_link: .strtab or .dynstr (The section header index of 
		// 			the associated string table.)
		printf(" %s\n", get_symbol_name(pelf, symshdr->sh_link, &sym));	
	}
}

int main(int argc, const char *argv[])
{
	int fd, class;
	Elf *pelf = NULL;
	Elf_Scn *scn = NULL;

	if (argc != 2)
		errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);

	if (elf_version(EV_CURRENT) == EV_NONE)
		errx(EXIT_FAILURE, "ELF library initializztion "
			"failed: %s", elf_errmsg(-1));

	if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
		errx(EXIT_FAILURE, "open \"%s\" failed", argv[1]);

	if (!(pelf = elf_begin(fd, ELF_C_READ, NULL)))
		errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1));

	if (elf_kind(pelf) != ELF_K_ELF)
		errx(EXIT_FAILURE, "\"%s\" is not an ELF object.", argv[1]);

	// get elf class ()
	if ((class = gelf_getclass(pelf)) == ELFCLASSNONE)
		errx(EXIT_FAILURE, "getclass() failed: %s.", elf_errmsg(-1));

	while ((scn = elf_nextscn(pelf, scn)) != NULL) {
		GElf_Shdr shdr;
		if (gelf_getshdr(scn, &shdr) != &shdr) {
			errx(EXIT_FAILURE, "gelf_getshdr() failed: %s.", elf_errmsg(-1));
		}

		if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
			print_syms(pelf, scn, &shdr);
		}
	}	

	(void)elf_end(pelf);
	(void)close(fd);

	exit(EXIT_SUCCESS);
}
           

參考連結:

《Symbol Table》《Symbol Table Section》

《Inside ELF Symbol Tables》

《Fun with weak symbols》

《Profiler adventures: resolving symbol addresses is hard!》

《OS Backtrace With Symbol Names》

《Executable and Linkable Format 101. Part 2: Symbols》

《Linking》

繼續閱讀