The GNU Binutils provides support for reading and writing program files in various formats such as ELF, COFF. This support is known as the BFD, the Binary File Descriptor library (or the Big F*cking Deal). This article illustrates how an Ada application can use the BFD library to have access to a program symbol table.
Declarations
The Ada BFD library provides a set of Ada bindings that give access to the BFD library. A binary file such as an object file, an executable or an archive is represented by the Bfd.Files.File_Type
limited type. The symbol table is represented by the Bfd.Symbols.Symbol_Table
limited type. These two types hold internal data used and managed by the BFD library.
with Bfd.Files;
with Bfd.Sections;
with Bfd.Symbols;
...
File : Bfd.Files.File_Type;
Symbols : Bfd.Symbols.Symbol_Table;
Opening the BFD file
The Open
procedure must be called to read the object or executable file whose path is given as argument. The File_Type
parameter will be initialized to get access to the binary file information. The Check_Format
function must then be called to let the BFD library gather the file format information and verify that it is an object file or an executable.
Bfd.Files.Open (File, Path, "");
if Bfd.Files.Check_Format (File, Bfd.Files.OBJECT) then
...
end if;
Loading the symbol table
The symbol table is loaded by using the Read_Symbols
procedure.
Bfd.Symbols.Read_Symbols (File, Symbols);
The resources used by the symbol table will be freed when the symbol table instance is finalized.
Looking for a symbol
Once the symbol table is loaded, we can use the Get_Symbol
function to find a symbol knowing its name. If the symbol is not found, a Null_Symbol
is returned.
Sym : Bfd.Symbols.Symbol := Bfd.Symbols.Get_Symbol (Symbols, "_main");
...
if Sym /= Bfd.Symbols.Null_Symbol then
-- Symbol found
end if;
Each symbol has the following set of information:
- A name (it may not be unique),
- A set of flags that describe the symbol (global, local, weak, constructor, TLS, ...),
- A symbol value,
- A section to which the symbol belongs.
Sec : constant Bfd.Sections.Section := Bfd.Symbols.Get_Section (Sym);
Flags : constant Bfd.Symbol_Flags := Bfd.Symbols.Get_Flags (Sym);
Value : constant Bfd.Symbol_Value := Bfd.Symbols.Get_Value (Sym);
Before interpreting and using the symbol value returned by Get_Value
, you must look at the section to check for an undefined symbol. Indeed, undefined symbols being not yet resolved by the linker they have no value and no section. You can check that by using the Is_Undefined_Section
function:
if Bfd.Sections.Is_Undefined_Section (Sec) then
Ada.Text_IO.Put_Line ("undefined symbol");
end if;
When the symbol is defined, you must look at the flags and also the section information to know more about it.
if (Flags and Bfd.Symbols.BSF_GLOBAL) /= 0 then
Ada.Text_IO.Put_Line ("global symbol in section "
& Bfd.Sections.Get_Name (Sec)
& " := " & Bfd.Symbol_Value'Image (Value));
elsif (Flags and Bfd.Symbols.BSF_LOCAL) /= 0 then
Ada.Text_IO.Put_Line ("local symbol in section "
& Bfd.Sections.Get_Name (Sec)
& " := " & Bfd.Symbol_Value'Image (Value));
else
Ada.Text_IO.Put_Line ("other symbol in section "
& Bfd.Sections.Get_Name (Sec)
& " := " & Bfd.Symbol_Value'Image (Value));
end if;
Conclusion and references
Reading an executable symbol table has been made fairly simple with the use of the Ada BFD library. Furthermore, the library allows to scan the sections, read their content and even use the BFD disassembler.
Add a comment
To add a comment, you must be connected. Login