blob: 854bbb290e4b52d30c745e5ff250714f717314b8 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- S Y S T E M . O B J E C T _ R E A D E R --
-- --
-- B o d y --
-- --
-- Copyright (C) 2009-2022, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Ada.Unchecked_Conversion;
with Interfaces.C;
with System.CRTL;
package body System.Object_Reader is
use Interfaces;
use Interfaces.C;
use System.Mmap;
SSU : constant := System.Storage_Unit;
function To_int32 is new Ada.Unchecked_Conversion (uint32, int32);
function Trim_Trailing_Nuls (Str : String) return String;
-- Return a copy of a string with any trailing NUL characters truncated
procedure Check_Read_Offset (S : Mapped_Stream; Size : uint32);
-- Check that the SIZE bytes at the current offset are still in the stream
-------------------------------------
-- ELF object file format handling --
-------------------------------------
generic
type uword is mod <>;
package ELF_Ops is
-- ELF version codes
ELFCLASS32 : constant := 1; -- 32 bit ELF
ELFCLASS64 : constant := 2; -- 64 bit ELF
-- ELF machine codes
EM_NONE : constant := 0; -- No machine
EM_SPARC : constant := 2; -- SUN SPARC
EM_386 : constant := 3; -- Intel 80386
EM_MIPS : constant := 8; -- MIPS RS3000 Big-Endian
EM_MIPS_RS3_LE : constant := 10; -- MIPS RS3000 Little-Endian
EM_SPARC32PLUS : constant := 18; -- Sun SPARC 32+
EM_PPC : constant := 20; -- PowerPC
EM_PPC64 : constant := 21; -- PowerPC 64-bit
EM_ARM : constant := 40; -- ARM
EM_SPARCV9 : constant := 43; -- SPARC v9 64-bit
EM_IA_64 : constant := 50; -- Intel Merced
EM_X86_64 : constant := 62; -- AMD x86-64 architecture
EM_AARCH64 : constant := 183; -- Aarch64
EN_NIDENT : constant := 16;
type E_Ident_Type is array (0 .. EN_NIDENT - 1) of uint8;
type Header is record
E_Ident : E_Ident_Type; -- Magic number and other info
E_Type : uint16; -- Object file type
E_Machine : uint16; -- Architecture
E_Version : uint32; -- Object file version
E_Entry : uword; -- Entry point virtual address
E_Phoff : uword; -- Program header table file offset
E_Shoff : uword; -- Section header table file offset
E_Flags : uint32; -- Processor-specific flags
E_Ehsize : uint16; -- ELF header size in bytes
E_Phentsize : uint16; -- Program header table entry size
E_Phnum : uint16; -- Program header table entry count
E_Shentsize : uint16; -- Section header table entry size
E_Shnum : uint16; -- Section header table entry count
E_Shstrndx : uint16; -- Section header string table index
end record;
type Section_Header is record
Sh_Name : uint32; -- Section name string table index
Sh_Type : uint32; -- Section type
Sh_Flags : uword; -- Section flags
Sh_Addr : uword; -- Section virtual addr at execution
Sh_Offset : uword; -- Section file offset
Sh_Size : uword; -- Section size in bytes
Sh_Link : uint32; -- Link to another section
Sh_Info : uint32; -- Additional section information
Sh_Addralign : uword; -- Section alignment
Sh_Entsize : uword; -- Entry size if section holds table
end record;
SHF_ALLOC : constant := 2;
SHF_EXECINSTR : constant := 4;
type Symtab_Entry32 is record
St_Name : uint32; -- Name (string table index)
St_Value : uint32; -- Value
St_Size : uint32; -- Size in bytes
St_Info : uint8; -- Type and binding attributes
St_Other : uint8; -- Undefined
St_Shndx : uint16; -- Defining section
end record;
type Symtab_Entry64 is record
St_Name : uint32; -- Name (string table index)
St_Info : uint8; -- Type and binding attributes
St_Other : uint8; -- Undefined
St_Shndx : uint16; -- Defining section
St_Value : uint64; -- Value
St_Size : uint64; -- Size in bytes
end record;
function Read_Header (F : in out Mapped_Stream) return Header;
-- Read a header from an ELF format object
function First_Symbol
(Obj : in out ELF_Object_File) return Object_Symbol;
-- Return the first element in the symbol table, or Null_Symbol if the
-- symbol table is empty.
function Read_Symbol
(Obj : in out ELF_Object_File;
Off : Offset) return Object_Symbol;
-- Read a symbol at offset Off
function Name
(Obj : in out ELF_Object_File;
Sym : Object_Symbol) return String_Ptr_Len;
-- Return the name of the symbol
function Name
(Obj : in out ELF_Object_File;
Sec : Object_Section) return String;
-- Return the name of a section
function Get_Section
(Obj : in out ELF_Object_File;
Shnum : uint32) return Object_Section;
-- Fetch a section by index from zero
function Initialize
(F : Mapped_File;
Hdr : Header;
In_Exception : Boolean) return ELF_Object_File;
-- Initialize an object file
end ELF_Ops;
-----------------------------------
-- PECOFF object format handling --
-----------------------------------
package PECOFF_Ops is
-- Constants and data layout are taken from the document "Microsoft
-- Portable Executable and Common Object File Format Specification"
-- Revision 8.1.
Signature_Loc_Offset : constant := 16#3C#;
-- Offset of pointer to the file signature
Size_Of_Standard_Header_Fields : constant := 16#18#;
-- Length in bytes of the standard header record
Function_Symbol_Type : constant := 16#20#;
-- Type field value indicating a symbol refers to a function
Not_Function_Symbol_Type : constant := 16#00#;
-- Type field value indicating a symbol does not refer to a function
type Magic_Array is array (0 .. 3) of uint8;
-- Array of magic numbers from the header
-- Magic numbers for PECOFF variants
VARIANT_PE32 : constant := 16#010B#;
VARIANT_PE32_PLUS : constant := 16#020B#;
-- PECOFF machine codes
IMAGE_FILE_MACHINE_I386 : constant := 16#014C#;
IMAGE_FILE_MACHINE_IA64 : constant := 16#0200#;
IMAGE_FILE_MACHINE_AMD64 : constant := 16#8664#;
-- PECOFF Data layout
type Header is record
Magics : Magic_Array;
Machine : uint16;
NumberOfSections : uint16;
TimeDateStamp : uint32;
PointerToSymbolTable : uint32;
NumberOfSymbols : uint32;
SizeOfOptionalHeader : uint16;
Characteristics : uint16;
Variant : uint16;
end record;
pragma Pack (Header);
type Optional_Header_PE32 is record
Magic : uint16;
MajorLinkerVersion : uint8;
MinorLinkerVersion : uint8;
SizeOfCode : uint32;
SizeOfInitializedData : uint32;
SizeOfUninitializedData : uint32;
AddressOfEntryPoint : uint32;
BaseOfCode : uint32;
BaseOfData : uint32; -- Note: not in PE32+
ImageBase : uint32;
SectionAlignment : uint32;
FileAlignment : uint32;
MajorOperatingSystemVersion : uint16;
MinorOperationSystemVersion : uint16;
MajorImageVersion : uint16;
MinorImageVersion : uint16;
MajorSubsystemVersion : uint16;
MinorSubsystemVersion : uint16;
Win32VersionValue : uint32;
SizeOfImage : uint32;
SizeOfHeaders : uint32;
Checksum : uint32;
Subsystem : uint16;
DllCharacteristics : uint16;
SizeOfStackReserve : uint32;
SizeOfStackCommit : uint32;
SizeOfHeapReserve : uint32;
SizeOfHeapCommit : uint32;
LoaderFlags : uint32;
NumberOfRvaAndSizes : uint32;
end record;
pragma Pack (Optional_Header_PE32);
pragma Assert (Optional_Header_PE32'Size = 96 * SSU);
type Optional_Header_PE64 is record
Magic : uint16;
MajorLinkerVersion : uint8;
MinorLinkerVersion : uint8;
SizeOfCode : uint32;
SizeOfInitializedData : uint32;
SizeOfUninitializedData : uint32;
AddressOfEntryPoint : uint32;
BaseOfCode : uint32;
ImageBase : uint64;
SectionAlignment : uint32;
FileAlignment : uint32;
MajorOperatingSystemVersion : uint16;
MinorOperationSystemVersion : uint16;
MajorImageVersion : uint16;
MinorImageVersion : uint16;
MajorSubsystemVersion : uint16;
MinorSubsystemVersion : uint16;
Win32VersionValue : uint32;
SizeOfImage : uint32;
SizeOfHeaders : uint32;
Checksum : uint32;
Subsystem : uint16;
DllCharacteristics : uint16;
SizeOfStackReserve : uint64;
SizeOfStackCommit : uint64;
SizeOfHeapReserve : uint64;
SizeOfHeapCommit : uint64;
LoaderFlags : uint32;
NumberOfRvaAndSizes : uint32;
end record;
pragma Pack (Optional_Header_PE64);
pragma Assert (Optional_Header_PE64'Size = 112 * SSU);
subtype Name_Str is String (1 .. 8);
type Section_Header is record
Name : Name_Str;
VirtualSize : uint32;
VirtualAddress : uint32;
SizeOfRawData : uint32;
PointerToRawData : uint32;
PointerToRelocations : uint32;
PointerToLinenumbers : uint32;
NumberOfRelocations : uint16;
NumberOfLinenumbers : uint16;
Characteristics : uint32;
end record;
pragma Pack (Section_Header);
IMAGE_SCN_CNT_CODE : constant := 16#0020#;
type Symtab_Entry is record
Name : Name_Str;
Value : uint32;
SectionNumber : int16;
TypeField : uint16;
StorageClass : uint8;
NumberOfAuxSymbols : uint8;
end record;
pragma Pack (Symtab_Entry);
type Auxent_Section is record
Length : uint32;
NumberOfRelocations : uint16;
NumberOfLinenumbers : uint16;
CheckSum : uint32;
Number : uint16;
Selection : uint8;
Unused1 : uint8;
Unused2 : uint8;
Unused3 : uint8;
end record;
for Auxent_Section'Size use 18 * 8;
function Read_Header (F : in out Mapped_Stream) return Header;
-- Read the object file header
function First_Symbol
(Obj : in out PECOFF_Object_File) return Object_Symbol;
-- Return the first element in the symbol table, or Null_Symbol if the
-- symbol table is empty.
function Read_Symbol
(Obj : in out PECOFF_Object_File;
Off : Offset) return Object_Symbol;
-- Read a symbol at offset Off
function Name
(Obj : in out PECOFF_Object_File;
Sym : Object_Symbol) return String_Ptr_Len;
-- Return the name of the symbol
function Name
(Obj : in out PECOFF_Object_File;
Sec : Object_Section) return String;
-- Return the name of a section
function Get_Section
(Obj : in out PECOFF_Object_File;
Index : uint32) return Object_Section;
-- Fetch a section by index from zero
function Initialize
(F : Mapped_File;
Hdr : Header;
In_Exception : Boolean) return PECOFF_Object_File;
-- Initialize an object file
end PECOFF_Ops;
-------------------------------------
-- XCOFF-32 object format handling --
-------------------------------------
package XCOFF32_Ops is
-- XCOFF Data layout
type Header is record
f_magic : uint16;
f_nscns : uint16;
f_timdat : uint32;
f_symptr : uint32;
f_nsyms : uint32;
f_opthdr : uint16;
f_flags : uint16;
end record;
type Auxiliary_Header is record
o_mflag : uint16;
o_vstamp : uint16;
o_tsize : uint32;
o_dsize : uint32;
o_bsize : uint32;
o_entry : uint32;
o_text_start : uint32;
o_data_start : uint32;
o_toc : uint32;
o_snentry : uint16;
o_sntext : uint16;
o_sndata : uint16;
o_sntoc : uint16;
o_snloader : uint16;
o_snbss : uint16;
o_algntext : uint16;
o_algndata : uint16;
o_modtype : uint16;
o_cpuflag : uint8;
o_cputype : uint8;
o_maxstack : uint32;
o_maxdata : uint32;
o_debugger : uint32;
o_flags : uint8;
o_sntdata : uint16;
o_sntbss : uint16;
end record;
pragma Unreferenced (Auxiliary_Header);
-- Not used, but not removed (just in case)
subtype Name_Str is String (1 .. 8);
type Section_Header is record
s_name : Name_Str;
s_paddr : uint32;
s_vaddr : uint32;
s_size : uint32;
s_scnptr : uint32;
s_relptr : uint32;
s_lnnoptr : uint32;
s_nreloc : uint16;
s_nlnno : uint16;
s_flags : uint32;
end record;
pragma Pack (Section_Header);
STYP_TEXT : constant := 16#0020#;
type Symbol_Entry is record
n_name : Name_Str;
n_value : uint32;
n_scnum : uint16;
n_type : uint16;
n_sclass : uint8;
n_numaux : uint8;
end record;
for Symbol_Entry'Size use 18 * 8;
type Aux_Entry is record
x_scnlen : uint32;
x_parmhash : uint32;
x_snhash : uint16;
x_smtyp : uint8;
x_smclass : uint8;
x_stab : uint32;
x_snstab : uint16;
end record;
for Aux_Entry'Size use 18 * 8;
pragma Pack (Aux_Entry);
C_EXT : constant := 2;
C_HIDEXT : constant := 107;
C_WEAKEXT : constant := 111;
XTY_LD : constant := 2;
-- Magic constant should be documented, especially since it's changed???
function Read_Header (F : in out Mapped_Stream) return Header;
-- Read the object file header
function First_Symbol
(Obj : in out XCOFF32_Object_File) return Object_Symbol;
-- Return the first element in the symbol table, or Null_Symbol if the
-- symbol table is empty.
function Read_Symbol
(Obj : in out XCOFF32_Object_File;
Off : Offset) return Object_Symbol;
-- Read a symbol at offset Off
function Name
(Obj : in out XCOFF32_Object_File;
Sym : Object_Symbol) return String_Ptr_Len;
-- Return the name of the symbol
function Name
(Obj : in out XCOFF32_Object_File;
Sec : Object_Section) return String;
-- Return the name of a section
function Initialize
(F : Mapped_File;
Hdr : Header;
In_Exception : Boolean) return XCOFF32_Object_File;
-- Initialize an object file
function Get_Section
(Obj : in out XCOFF32_Object_File;
Index : uint32) return Object_Section;
-- Fetch a section by index from zero
end XCOFF32_Ops;
-------------
-- ELF_Ops --
-------------
package body ELF_Ops is
function Get_String_Table (Obj : in out ELF_Object_File)
return Object_Section;
-- Fetch the section containing the string table
function Get_Symbol_Table (Obj : in out ELF_Object_File)
return Object_Section;
-- Fetch the section containing the symbol table
function Read_Section_Header
(Obj : in out ELF_Object_File;
Shnum : uint32) return Section_Header;
-- Read the header for an ELF format object section indexed from zero
------------------
-- First_Symbol --
------------------
function First_Symbol
(Obj : in out ELF_Object_File) return Object_Symbol
is
begin
if Obj.Symtab_Last = 0 then
return Null_Symbol;
else
return Read_Symbol (Obj, 0);
end if;
end First_Symbol;
-----------------
-- Get_Section --
-----------------
function Get_Section
(Obj : in out ELF_Object_File;
Shnum : uint32) return Object_Section
is
SHdr : constant Section_Header := Read_Section_Header (Obj, Shnum);
begin
return (Shnum,
Offset (SHdr.Sh_Offset),
uint64 (SHdr.Sh_Addr),
uint64 (SHdr.Sh_Size),
(SHdr.Sh_Flags and SHF_EXECINSTR) /= 0);
end Get_Section;
------------------------
-- Get_String_Table --
------------------------
function Get_String_Table
(Obj : in out ELF_Object_File) return Object_Section
is
begin
-- All cases except MIPS IRIX, string table located in .strtab
if Obj.Arch /= MIPS then
return Get_Section (Obj, ".strtab");
-- On IRIX only .dynstr is available
else
return Get_Section (Obj, ".dynstr");
end if;
end Get_String_Table;
------------------------
-- Get_Symbol_Table --
------------------------
function Get_Symbol_Table
(Obj : in out ELF_Object_File) return Object_Section
is
begin
-- All cases except MIPS IRIX, symbol table located in .symtab
if Obj.Arch /= MIPS then
return Get_Section (Obj, ".symtab");
-- On IRIX, symbol table located somewhere other than .symtab
else
return Get_Section (Obj, ".dynsym");
end if;
end Get_Symbol_Table;
----------------
-- Initialize --
----------------
function Initialize
(F : Mapped_File;
Hdr : Header;
In_Exception : Boolean) return ELF_Object_File
is
Res : ELF_Object_File
(Format => (case uword'Size is
when 64 => ELF64,
when 32 => ELF32,
when others => raise Program_Error));
Sec : Object_Section;
begin
Res.MF := F;
Res.In_Exception := In_Exception;
Res.Num_Sections := uint32 (Hdr.E_Shnum);
case Hdr.E_Machine is
when EM_SPARC
| EM_SPARC32PLUS
=>
Res.Arch := SPARC;
when EM_386 =>
Res.Arch := i386;
when EM_MIPS
| EM_MIPS_RS3_LE
=>
Res.Arch := MIPS;
when EM_PPC =>
Res.Arch := PPC;
when EM_PPC64 =>
Res.Arch := PPC64;
when EM_SPARCV9 =>
Res.Arch := SPARC64;
when EM_IA_64 =>
Res.Arch := IA64;
when EM_X86_64 =>
Res.Arch := x86_64;
when EM_ARM =>
Res.Arch := ARM;
when EM_AARCH64 =>
Res.Arch := AARCH64;
when others =>
raise Format_Error with "unrecognized architecture";
end case;
-- Map section table and section string table
Res.Sectab_Stream := Create_Stream
(F, File_Size (Hdr.E_Shoff),
File_Size (Hdr.E_Shnum) * File_Size (Hdr.E_Shentsize));
Sec := Get_Section (Res, uint32 (Hdr.E_Shstrndx));
Res.Secstr_Stream := Create_Stream (Res, Sec);
-- Map symbol and string table
Sec := Get_Symbol_Table (Res);
Res.Symtab_Stream := Create_Stream (Res, Sec);
Res.Symtab_Last := Offset (Sec.Size);
Sec := Get_String_Table (Res);
Res.Symstr_Stream := Create_Stream (Res, Sec);
return Res;
end Initialize;
-----------------
-- Read_Header --
-----------------
function Read_Header (F : in out Mapped_Stream) return Header is
Hdr : Header;
begin
Seek (F, 0);
Read_Raw (F, Hdr'Address, uint32 (Hdr'Size / SSU));
return Hdr;
end Read_Header;
-------------------------
-- Read_Section_Header --
-------------------------
function Read_Section_Header
(Obj : in out ELF_Object_File;
Shnum : uint32) return Section_Header
is
Shdr : Section_Header;
begin
Seek (Obj.Sectab_Stream, Offset (Shnum * Section_Header'Size / SSU));
Read_Raw (Obj.Sectab_Stream, Shdr'Address, Section_Header'Size / SSU);
return Shdr;
end Read_Section_Header;
-----------------
-- Read_Symbol --
-----------------
function Read_Symbol
(Obj : in out ELF_Object_File;
Off : Offset) return Object_Symbol
is
ST_Entry32 : Symtab_Entry32;
ST_Entry64 : Symtab_Entry64;
Res : Object_Symbol;
begin
Seek (Obj.Symtab_Stream, Off);
case uword'Size is
when 32 =>
Read_Raw (Obj.Symtab_Stream, ST_Entry32'Address,
uint32 (ST_Entry32'Size / SSU));
Res := (Off,
Off + ST_Entry32'Size / SSU,
uint64 (ST_Entry32.St_Value),
uint64 (ST_Entry32.St_Size));
when 64 =>
Read_Raw (Obj.Symtab_Stream, ST_Entry64'Address,
uint32 (ST_Entry64'Size / SSU));
Res := (Off,
Off + ST_Entry64'Size / SSU,
ST_Entry64.St_Value,
ST_Entry64.St_Size);
when others =>
raise Program_Error;
end case;
return Res;
end Read_Symbol;
----------
-- Name --
----------
function Name
(Obj : in out ELF_Object_File;
Sec : Object_Section) return String
is
SHdr : Section_Header;
begin
SHdr := Read_Section_Header (Obj, Sec.Num);
return Offset_To_String (Obj.Secstr_Stream, Offset (SHdr.Sh_Name));
end Name;
function Name
(Obj : in out ELF_Object_File;
Sym : Object_Symbol) return String_Ptr_Len
is
ST_Entry32 : Symtab_Entry32;
ST_Entry64 : Symtab_Entry64;
Name_Off : Offset;
begin
-- Test that this symbol is not null
if Sym = Null_Symbol then
return (null, 0);
end if;
-- Read the symbol table entry
Seek (Obj.Symtab_Stream, Sym.Off);
case uword'Size is
when 32 =>
Read_Raw (Obj.Symtab_Stream, ST_Entry32'Address,
uint32 (ST_Entry32'Size / SSU));
Name_Off := Offset (ST_Entry32.St_Name);
when 64 =>
Read_Raw (Obj.Symtab_Stream, ST_Entry64'Address,
uint32 (ST_Entry64'Size / SSU));
Name_Off := Offset (ST_Entry64.St_Name);
when others =>
raise Program_Error;
end case;
-- Fetch the name from the string table
Seek (Obj.Symstr_Stream, Name_Off);
return Read (Obj.Symstr_Stream);
end Name;
end ELF_Ops;
package ELF32_Ops is new ELF_Ops (uint32);
package ELF64_Ops is new ELF_Ops (uint64);
----------------
-- PECOFF_Ops --
----------------
package body PECOFF_Ops is
function Decode_Name
(Obj : in out PECOFF_Object_File;
Raw_Name : String) return String;
-- A section name is an 8 byte field padded on the right with null
-- characters, or a '\' followed by an ASCII decimal string indicating
-- an offset in to the string table. This routine decodes this
function Get_Section_Virtual_Address
(Obj : in out PECOFF_Object_File;
Index : uint32) return uint64;
-- Fetch the address at which a section is loaded
function Read_Section_Header
(Obj : in out PECOFF_Object_File;
Index : uint32) return Section_Header;
-- Read a header from section table
function String_Table
(Obj : in out PECOFF_Object_File;
Index : Offset) return String;
-- Return an entry from the string table
-----------------
-- Decode_Name --
-----------------
function Decode_Name
(Obj : in out PECOFF_Object_File;
Raw_Name : String) return String
is
Name_Or_Ref : constant String := Trim_Trailing_Nuls (Raw_Name);
Off : Offset;
begin
-- We should never find a symbol with a zero length name. If we do it
-- probably means we are not parsing the symbol table correctly. If
-- this happens we raise a fatal error.
if Name_Or_Ref'Length = 0 then
raise Format_Error with
"found zero length symbol in symbol table";
end if;
if Name_Or_Ref (1) /= '/' then
return Name_Or_Ref;
else
Off := Offset'Value (Name_Or_Ref (2 .. Name_Or_Ref'Last));
return String_Table (Obj, Off);
end if;
end Decode_Name;
------------------
-- First_Symbol --
------------------
function First_Symbol
(Obj : in out PECOFF_Object_File) return Object_Symbol
is
begin
-- Return Null_Symbol in the case that the symbol table is empty
if Obj.Symtab_Last = 0 then
return Null_Symbol;
end if;
return Read_Symbol (Obj, 0);
end First_Symbol;
-----------------
-- Get_Section --
-----------------
function Get_Section
(Obj : in out PECOFF_Object_File;
Index : uint32) return Object_Section
is
Sec : constant Section_Header := Read_Section_Header (Obj, Index);
begin
-- Use VirtualSize instead of SizeOfRawData. The latter is rounded to
-- the page size, so it may add garbage to the content. On the other
-- side, the former may be larger than the latter in case of 0
-- padding.
return (Index,
Offset (Sec.PointerToRawData),
uint64 (Sec.VirtualAddress) + Obj.ImageBase,
uint64 (Sec.VirtualSize),
(Sec.Characteristics and IMAGE_SCN_CNT_CODE) /= 0);
end Get_Section;
---------------------------------
-- Get_Section_Virtual_Address --
---------------------------------
function Get_Section_Virtual_Address
(Obj : in out PECOFF_Object_File;
Index : uint32) return uint64
is
Sec : Section_Header;
begin
-- Try cache
if Index = Obj.GSVA_Sec then
return Obj.GSVA_Addr;
end if;
Obj.GSVA_Sec := Index;
Sec := Read_Section_Header (Obj, Index);
Obj.GSVA_Addr := Obj.ImageBase + uint64 (Sec.VirtualAddress);
return Obj.GSVA_Addr;
end Get_Section_Virtual_Address;
----------------
-- Initialize --
----------------
function Initialize
(F : Mapped_File;
Hdr : Header;
In_Exception : Boolean) return PECOFF_Object_File
is
Res : PECOFF_Object_File
(Format => (case Hdr.Variant is
when PECOFF_Ops.VARIANT_PE32 => PECOFF,
when PECOFF_Ops.VARIANT_PE32_PLUS => PECOFF_PLUS,
when others => raise Program_Error
with "unrecognized PECOFF variant"));
Symtab_Size : constant Offset :=
Offset (Hdr.NumberOfSymbols) * (Symtab_Entry'Size / SSU);
Strtab_Size : uint32;
Hdr_Offset : Offset;
Opt_Offset : File_Size;
Opt_Stream : Mapped_Stream;
begin
Res.MF := F;
Res.In_Exception := In_Exception;
case Hdr.Machine is
when PECOFF_Ops.IMAGE_FILE_MACHINE_I386 =>
Res.Arch := i386;
when PECOFF_Ops.IMAGE_FILE_MACHINE_IA64 =>
Res.Arch := IA64;
when PECOFF_Ops.IMAGE_FILE_MACHINE_AMD64 =>
Res.Arch := x86_64;
when others =>
raise Format_Error with "unrecognized architecture";
end case;
Res.Num_Sections := uint32 (Hdr.NumberOfSections);
-- Map symbol table and the first following word (which is the length
-- of the string table).
Res.Symtab_Last := Symtab_Size;
Res.Symtab_Stream := Create_Stream
(F,
File_Size (Hdr.PointerToSymbolTable),
File_Size (Symtab_Size + 4));
-- Map string table. The first 4 bytes are the length of the string
-- table and are part of it.
Seek (Res.Symtab_Stream, Symtab_Size);
Strtab_Size := Read (Res.Symtab_Stream);
Res.Symstr_Stream := Create_Stream
(F,
File_Size (Hdr.PointerToSymbolTable) + File_Size (Symtab_Size),
File_Size (Strtab_Size));
-- Map section table
Opt_Stream := Create_Stream (Res.Mf, Signature_Loc_Offset, 4);
Hdr_Offset := Offset (uint32'(Read (Opt_Stream)));
Close (Opt_Stream);
Res.Sectab_Stream := Create_Stream
(F,
File_Size (Hdr_Offset +
Size_Of_Standard_Header_Fields +
Offset (Hdr.SizeOfOptionalHeader)),
File_Size (Res.Num_Sections)
* File_Size (Section_Header'Size / SSU));
-- Read optional header and extract image base
Opt_Offset := File_Size (Hdr_Offset + Size_Of_Standard_Header_Fields);
if Res.Format = PECOFF then
declare
Opt_32 : Optional_Header_PE32;
begin
Opt_Stream := Create_Stream
(Res.Mf, Opt_Offset, Opt_32'Size / SSU);
Read_Raw
(Opt_Stream, Opt_32'Address, uint32 (Opt_32'Size / SSU));
Res.ImageBase := uint64 (Opt_32.ImageBase);
Close (Opt_Stream);
end;
else
declare
Opt_64 : Optional_Header_PE64;
begin
Opt_Stream := Create_Stream
(Res.Mf, Opt_Offset, Opt_64'Size / SSU);
Read_Raw
(Opt_Stream, Opt_64'Address, uint32 (Opt_64'Size / SSU));
Res.ImageBase := Opt_64.ImageBase;
Close (Opt_Stream);
end;
end if;
return Res;
end Initialize;
-----------------
-- Read_Symbol --
-----------------
function Read_Symbol
(Obj : in out PECOFF_Object_File;
Off : Offset) return Object_Symbol
is
ST_Entry : Symtab_Entry;
ST_Last : Symtab_Entry;
Aux_Entry : Auxent_Section;
Sz : constant Offset := ST_Entry'Size / SSU;
Result : Object_Symbol;
Noff : Offset;
Sym_Off : Offset;
begin
-- Seek to the successor of Prev
Noff := Off;
loop
Sym_Off := Noff;
Seek (Obj.Symtab_Stream, Sym_Off);
Read_Raw (Obj.Symtab_Stream, ST_Entry'Address, uint32 (Sz));
-- Skip AUX entries
Noff := Noff + Offset (1 + ST_Entry.NumberOfAuxSymbols) * Sz;
exit when ST_Entry.TypeField = Function_Symbol_Type
and then ST_Entry.SectionNumber > 0;
if Noff >= Obj.Symtab_Last then
return Null_Symbol;
end if;
end loop;
-- Construct the symbol
Result :=
(Off => Sym_Off,
Next => Noff,
Value => uint64 (ST_Entry.Value),
Size => 0);
-- Set the size as accurately as possible
-- The size of a symbol is not directly available so we try scanning
-- to the next function and assuming the code ends there.
loop
-- Read symbol and AUX entries
Sym_Off := Noff;
Seek (Obj.Symtab_Stream, Sym_Off);
Read_Raw (Obj.Symtab_Stream, ST_Last'Address, uint32 (Sz));
for I in 1 .. ST_Last.NumberOfAuxSymbols loop
Read_Raw (Obj.Symtab_Stream, Aux_Entry'Address, uint32 (Sz));
end loop;
Noff := Noff + Offset (1 + ST_Last.NumberOfAuxSymbols) * Sz;
if ST_Last.TypeField = Function_Symbol_Type then
if ST_Last.SectionNumber = ST_Entry.SectionNumber
and then ST_Last.Value >= ST_Entry.Value
then
-- Symbol is a function past ST_Entry
Result.Size := uint64 (ST_Last.Value - ST_Entry.Value);
else
-- Not correlated function
Result.Next := Sym_Off;
end if;
exit;
elsif ST_Last.SectionNumber = ST_Entry.SectionNumber
and then ST_Last.TypeField = Not_Function_Symbol_Type
and then ST_Last.StorageClass = 3
and then ST_Last.NumberOfAuxSymbols = 1
then
-- Symbol is a section
Result.Size := uint64 (ST_Last.Value + Aux_Entry.Length
- ST_Entry.Value);
Result.Next := Noff;
exit;
end if;
exit when Noff >= Obj.Symtab_Last;
end loop;
-- Relocate the address
Result.Value :=
Result.Value + Get_Section_Virtual_Address
(Obj, uint32 (ST_Entry.SectionNumber - 1));
return Result;
end Read_Symbol;
------------------
-- Read_Header --
------------------
function Read_Header (F : in out Mapped_Stream) return Header is
Hdr : Header;
Off : int32;
begin
-- Skip the MSDOS stub, and seek directly to the file offset
Seek (F, Signature_Loc_Offset);
Off := Read (F);
-- Read the COFF file header
Seek (F, Offset (Off));
Read_Raw (F, Hdr'Address, uint32 (Hdr'Size / SSU));
return Hdr;
end Read_Header;
-------------------------
-- Read_Section_Header --
-------------------------
function Read_Section_Header
(Obj : in out PECOFF_Object_File;
Index : uint32) return Section_Header
is
Sec : Section_Header;
begin
Seek (Obj.Sectab_Stream, Offset (Index * Section_Header'Size / SSU));
Read_Raw (Obj.Sectab_Stream, Sec'Address, Section_Header'Size / SSU);
return Sec;
end Read_Section_Header;
----------
-- Name --
----------
function Name
(Obj : in out PECOFF_Object_File;
Sec : Object_Section) return String
is
Shdr : constant Section_Header := Read_Section_Header (Obj, Sec.Num);
begin
return Decode_Name (Obj, Shdr.Name);
end Name;
-------------------
-- String_Table --
-------------------
function String_Table
(Obj : in out PECOFF_Object_File;
Index : Offset) return String
is
begin
-- An index of zero is used to represent an empty string, as the
-- first word of the string table is specified to contain the length
-- of the table rather than its contents.
if Index = 0 then
return "";
else
return Offset_To_String (Obj.Symstr_Stream, Index);
end if;
end String_Table;
----------
-- Name --
----------
function Name
(Obj : in out PECOFF_Object_File;
Sym : Object_Symbol) return String_Ptr_Len
is
ST_Entry : Symtab_Entry;
begin
Seek (Obj.Symtab_Stream, Sym.Off);
Read_Raw (Obj.Symtab_Stream, ST_Entry'Address, ST_Entry'Size / SSU);
declare
-- Symbol table entries are packed and Table_Entry.Name may not be
-- sufficiently aligned to interpret as a 32 bit word, so it is
-- copied to a temporary
Aligned_Name : Name_Str := ST_Entry.Name;
for Aligned_Name'Alignment use 4;
First_Word : uint32;
pragma Import (Ada, First_Word);
-- Suppress initialization in Normalized_Scalars mode
for First_Word'Address use Aligned_Name (1)'Address;
Second_Word : uint32;
pragma Import (Ada, Second_Word);
-- Suppress initialization in Normalized_Scalars mode
for Second_Word'Address use Aligned_Name (5)'Address;
begin
if First_Word = 0 then
-- Second word is an offset in the symbol table
if Second_Word = 0 then
return (null, 0);
else
Seek (Obj.Symstr_Stream, int64 (Second_Word));
return Read (Obj.Symstr_Stream);
end if;
else
-- Inlined symbol name
Seek (Obj.Symtab_Stream, Sym.Off);
return To_String_Ptr_Len (Read (Obj.Symtab_Stream), 8);
end if;
end;
end Name;
end PECOFF_Ops;
-----------------
-- XCOFF32_Ops --
-----------------
package body XCOFF32_Ops is
function Read_Section_Header
(Obj : in out XCOFF32_Object_File;
Index : uint32) return Section_Header;
-- Read a header from section table
-----------------
-- Read_Symbol --
-----------------
function Read_Symbol
(Obj : in out XCOFF32_Object_File;
Off : Offset) return Object_Symbol
is
Sym : Symbol_Entry;
Sz : constant Offset := Symbol_Entry'Size / SSU;
Aux : Aux_Entry;
Result : Object_Symbol;
Noff : Offset;
Sym_Off : Offset;
procedure Read_LD_Symbol;
-- Read the next LD symbol
--------------------
-- Read_LD_Symbol --
--------------------
procedure Read_LD_Symbol is
begin
loop
Sym_Off := Noff;
Read_Raw (Obj.Symtab_Stream, Sym'Address, uint32 (Sz));
Noff := Noff + Offset (1 + Sym.n_numaux) * Sz;
for J in 1 .. Sym.n_numaux loop
Read_Raw (Obj.Symtab_Stream, Aux'Address, uint32 (Sz));
end loop;
exit when Noff >= Obj.Symtab_Last;
exit when Sym.n_numaux = 1
and then Sym.n_scnum /= 0
and then (Sym.n_sclass = C_EXT
or else Sym.n_sclass = C_HIDEXT
or else Sym.n_sclass = C_WEAKEXT)
and then Aux.x_smtyp = XTY_LD;
end loop;
end Read_LD_Symbol;
-- Start of processing for Read_Symbol
begin
Seek (Obj.Symtab_Stream, Off);
Noff := Off;
Read_LD_Symbol;
if Noff >= Obj.Symtab_Last then
return Null_Symbol;
end if;
-- Construct the symbol
Result := (Off => Sym_Off,
Next => Noff,
Value => uint64 (Sym.n_value),
Size => 0);
-- Look for the next symbol to compute the size
Read_LD_Symbol;
if Noff >= Obj.Symtab_Last then
return Null_Symbol;
end if;
Result.Size := uint64 (Sym.n_value) - Result.Value;
Result.Next := Sym_Off;
return Result;
end Read_Symbol;
------------------
-- First_Symbol --
------------------
function First_Symbol
(Obj : in out XCOFF32_Object_File) return Object_Symbol
is
begin
-- Return Null_Symbol in the case that the symbol table is empty
if Obj.Symtab_Last = 0 then
return Null_Symbol;
end if;
return Read_Symbol (Obj, 0);
end First_Symbol;
----------------
-- Initialize --
----------------
function Initialize
(F : Mapped_File;
Hdr : Header;
In_Exception : Boolean) return XCOFF32_Object_File
is
Res : XCOFF32_Object_File (Format => XCOFF32);
Strtab_Sz : uint32;
begin
Res.Mf := F;
Res.In_Exception := In_Exception;
Res.Arch := PPC;
-- Map sections table
Res.Num_Sections := uint32 (Hdr.f_nscns);
Res.Sectab_Stream := Create_Stream
(F,
File_Size (Header'Size / SSU) + File_Size (Hdr.f_opthdr),
File_Size (Hdr.f_nscns) * (Section_Header'Size / SSU));
-- Map symbols table
Res.Symtab_Last := Offset (Hdr.f_nscns) * (Symbol_Entry'Size / SSU);
Res.Symtab_Stream := Create_Stream
(F,
File_Size (Hdr.f_symptr),
File_Size (Res.Symtab_Last) + 4);
-- Map string table
Seek (Res.Symtab_Stream, Res.Symtab_Last);
Strtab_Sz := Read (Res.Symtab_Stream);
Res.Symstr_Stream := Create_Stream
(F,
File_Size (Res.Symtab_Last) + 4,
File_Size (Strtab_Sz) - 4);
return Res;
end Initialize;
-----------------
-- Get_Section --
-----------------
function Get_Section
(Obj : in out XCOFF32_Object_File;
Index : uint32) return Object_Section
is
Sec : constant Section_Header := Read_Section_Header (Obj, Index);
begin
return (Index, Offset (Sec.s_scnptr),
uint64 (Sec.s_vaddr),
uint64 (Sec.s_size),
(Sec.s_flags and STYP_TEXT) /= 0);
end Get_Section;
-----------------
-- Read_Header --
-----------------
function Read_Header (F : in out Mapped_Stream) return Header is
Hdr : Header;
begin
Seek (F, 0);
Read_Raw (F, Hdr'Address, uint32 (Hdr'Size / SSU));
return Hdr;
end Read_Header;
-------------------------
-- Read_Section_Header --
-------------------------
function Read_Section_Header
(Obj : in out XCOFF32_Object_File;
Index : uint32) return Section_Header
is
Sec : Section_Header;
begin
-- Seek to the end of the object header
Seek (Obj.Sectab_Stream, Offset (Index * Section_Header'Size / SSU));
-- Read the section
Read_Raw (Obj.Sectab_Stream, Sec'Address, Section_Header'Size / SSU);
return Sec;
end Read_Section_Header;
----------
-- Name --
----------
function Name
(Obj : in out XCOFF32_Object_File;
Sec : Object_Section) return String
is
Hdr : Section_Header;
begin
Hdr := Read_Section_Header (Obj, Sec.Num);
return Trim_Trailing_Nuls (Hdr.s_name);
end Name;
----------
-- Name --
----------
function Name
(Obj : in out XCOFF32_Object_File;
Sym : Object_Symbol) return String_Ptr_Len
is
Symbol : Symbol_Entry;
begin
Seek (Obj.Symtab_Stream, Sym.Off);
Read_Raw (Obj.Symtab_Stream, Symbol'Address, Symbol'Size / SSU);
declare
First_Word : uint32;
pragma Import (Ada, First_Word);
-- Suppress initialization in Normalized_Scalars mode
for First_Word'Address use Symbol.n_name (1)'Address;
Second_Word : uint32;
pragma Import (Ada, Second_Word);
-- Suppress initialization in Normalized_Scalars mode
for Second_Word'Address use Symbol.n_name (5)'Address;
begin
if First_Word = 0 then
if Second_Word = 0 then
return (null, 0);
else
Seek (Obj.Symstr_Stream, int64 (Second_Word));
return Read (Obj.Symstr_Stream);
end if;
else
Seek (Obj.Symtab_Stream, Sym.Off);
return To_String_Ptr_Len (Read (Obj.Symstr_Stream), 8);
end if;
end;
end Name;
end XCOFF32_Ops;
----------
-- Arch --
----------
function Arch (Obj : Object_File) return Object_Arch is
begin
return Obj.Arch;
end Arch;
function Create_Stream
(Mf : Mapped_File;
File_Offset : File_Size;
File_Length : File_Size)
return Mapped_Stream
is
Region : Mapped_Region;
begin
Read (Mf, Region, File_Offset, File_Length, False);
return (Region, 0, Offset (File_Length));
end Create_Stream;
function Create_Stream
(Obj : Object_File;
Sec : Object_Section) return Mapped_Stream
is
begin
return Create_Stream (Obj.Mf, File_Size (Sec.Off), File_Size (Sec.Size));
end Create_Stream;
procedure Tell (Obj : in out Mapped_Stream; Off : out Offset) is
begin
Off := Obj.Off;
end Tell;
function Tell (Obj : Mapped_Stream) return Offset is
begin
return Obj.Off;
end Tell;
function Length (Obj : Mapped_Stream) return Offset is
begin
return Obj.Len;
end Length;
-----------
-- Close --
-----------
procedure Close (S : in out Mapped_Stream) is
begin
Free (S.Region);
end Close;
procedure Close (Obj : in out Object_File) is
begin
Close (Obj.Symtab_Stream);
Close (Obj.Symstr_Stream);
Close (Obj.Sectab_Stream);
case Obj.Format is
when ELF =>
Close (Obj.Secstr_Stream);
when Any_PECOFF =>
null;
when XCOFF32 =>
null;
end case;
Close (Obj.Mf);
end Close;
------------------------
-- Strip_Leading_Char --
------------------------
function Strip_Leading_Char
(Obj : in out Object_File;
Sym : String_Ptr_Len) return Positive
is
begin
if (Obj.Format = PECOFF and then Sym.Ptr (1) = '_')
or else
(Obj.Format = XCOFF32 and then Sym.Ptr (1) = '.')
then
return 2;
else
return 1;
end if;
end Strip_Leading_Char;
----------------------
-- Decoded_Ada_Name --
----------------------
function Decoded_Ada_Name
(Obj : in out Object_File;
Sym : String_Ptr_Len) return String
is
procedure gnat_decode
(Coded_Name_Addr : Address;
Ada_Name_Addr : Address;
Verbose : int);
pragma Import (C, gnat_decode, "__gnat_decode");
subtype size_t is Interfaces.C.size_t;
Sym_Name : constant String :=
String (Sym.Ptr (1 .. Sym.Len)) & ASCII.NUL;
Decoded : char_array (0 .. size_t (Sym.Len) * 2 + 60);
Off : Natural;
begin
-- In the PECOFF case most but not all symbol table entries have an
-- extra leading underscore. In this case we trim it.
Off := Strip_Leading_Char (Obj, Sym);
gnat_decode (Sym_Name (Off)'Address, Decoded'Address, 0);
return To_Ada (Decoded);
end Decoded_Ada_Name;
------------------
-- First_Symbol --
------------------
function First_Symbol (Obj : in out Object_File) return Object_Symbol is
begin
case Obj.Format is
when ELF32 => return ELF32_Ops.First_Symbol (Obj);
when ELF64 => return ELF64_Ops.First_Symbol (Obj);
when Any_PECOFF => return PECOFF_Ops.First_Symbol (Obj);
when XCOFF32 => return XCOFF32_Ops.First_Symbol (Obj);
end case;
end First_Symbol;
------------
-- Format --
------------
function Format (Obj : Object_File) return Object_Format is
begin
return Obj.Format;
end Format;
----------------------
-- Get_Load_Address --
----------------------
function Get_Load_Address (Obj : Object_File) return uint64 is
begin
case Obj.Format is
when ELF => return 0;
when Any_PECOFF => return Obj.ImageBase;
when XCOFF32 => raise Format_Error;
end case;
end Get_Load_Address;
-----------------
-- Get_Section --
-----------------
function Get_Section
(Obj : in out Object_File;
Shnum : uint32) return Object_Section
is
begin
case Obj.Format is
when ELF32 => return ELF32_Ops.Get_Section (Obj, Shnum);
when ELF64 => return ELF64_Ops.Get_Section (Obj, Shnum);
when Any_PECOFF => return PECOFF_Ops.Get_Section (Obj, Shnum);
when XCOFF32 => return XCOFF32_Ops.Get_Section (Obj, Shnum);
end case;
end Get_Section;
function Get_Section
(Obj : in out Object_File;
Sec_Name : String) return Object_Section
is
Sec : Object_Section;
begin
for J in 0 .. Obj.Num_Sections - 1 loop
Sec := Get_Section (Obj, J);
if Name (Obj, Sec) = Sec_Name then
return Sec;
end if;
end loop;
if Obj.In_Exception then
return Null_Section;
else
raise Format_Error with "could not find section in object file";
end if;
end Get_Section;
----------------------
-- Get_Xcode_Bounds --
----------------------
procedure Get_Xcode_Bounds
(Obj : in out Object_File;
Low, High : out uint64)
is
Sec : Object_Section;
begin
-- First set as an empty range
Low := uint64'Last;
High := uint64'First;
-- Now find the lowest and highest offsets
-- attached to executable code sections
for Idx in 1 .. Num_Sections (Obj) loop
Sec := Get_Section (Obj, Idx - 1);
if Sec.Flag_Xcode then
if Sec.Addr < Low then
Low := Sec.Addr;
end if;
if Sec.Addr + Sec.Size > High then
High := Sec.Addr + Sec.Size;
end if;
end if;
end loop;
end Get_Xcode_Bounds;
----------
-- Name --
----------
function Name
(Obj : in out Object_File;
Sec : Object_Section) return String
is
begin
case Obj.Format is
when ELF32 => return ELF32_Ops.Name (Obj, Sec);
when ELF64 => return ELF64_Ops.Name (Obj, Sec);
when Any_PECOFF => return PECOFF_Ops.Name (Obj, Sec);
when XCOFF32 => return XCOFF32_Ops.Name (Obj, Sec);
end case;
end Name;
function Name
(Obj : in out Object_File;
Sym : Object_Symbol) return String_Ptr_Len
is
begin
case Obj.Format is
when ELF32 => return ELF32_Ops.Name (Obj, Sym);
when ELF64 => return ELF64_Ops.Name (Obj, Sym);
when Any_PECOFF => return PECOFF_Ops.Name (Obj, Sym);
when XCOFF32 => return XCOFF32_Ops.Name (Obj, Sym);
end case;
end Name;
-----------------
-- Next_Symbol --
-----------------
function Next_Symbol
(Obj : in out Object_File;
Prev : Object_Symbol) return Object_Symbol
is
begin
-- Test whether we've reached the end of the symbol table
if Prev.Next >= Obj.Symtab_Last then
return Null_Symbol;
end if;
return Read_Symbol (Obj, Prev.Next);
end Next_Symbol;
---------
-- Num --
---------
function Num (Sec : Object_Section) return uint32 is
begin
return Sec.Num;
end Num;
------------------
-- Num_Sections --
------------------
function Num_Sections (Obj : Object_File) return uint32 is
begin
return Obj.Num_Sections;
end Num_Sections;
---------
-- Off --
---------
function Off (Sec : Object_Section) return Offset is
begin
return Sec.Off;
end Off;
function Off (Sym : Object_Symbol) return Offset is
begin
return Sym.Off;
end Off;
----------------------
-- Offset_To_String --
----------------------
function Offset_To_String
(S : in out Mapped_Stream;
Off : Offset) return String
is
Buf : Buffer;
begin
Seek (S, Off);
Read_C_String (S, Buf);
return To_String (Buf);
end Offset_To_String;
----------
-- Open --
----------
function Open
(File_Name : String;
In_Exception : Boolean := False) return Object_File_Access
is
F : Mapped_File;
Hdr_Stream : Mapped_Stream;
begin
-- Open the file
F := Open_Read_No_Exception (File_Name);
if F = Invalid_Mapped_File then
if In_Exception then
return null;
else
raise IO_Error with "could not open object file";
end if;
end if;
Hdr_Stream := Create_Stream (F, 0, 4096);
declare
Hdr : constant ELF32_Ops.Header := ELF32_Ops.Read_Header (Hdr_Stream);
begin
-- Look for the magic numbers for the ELF case
if Hdr.E_Ident (0) = 16#7F# and then
Hdr.E_Ident (1) = Character'Pos ('E') and then
Hdr.E_Ident (2) = Character'Pos ('L') and then
Hdr.E_Ident (3) = Character'Pos ('F') and then
Hdr.E_Ident (4) = ELF32_Ops.ELFCLASS32
then
Close (Hdr_Stream);
return new Object_File'
(ELF32_Ops.Initialize (F, Hdr, In_Exception));
end if;
end;
declare
Hdr : constant ELF64_Ops.Header :=
ELF64_Ops.Read_Header (Hdr_Stream);
begin
-- Look for the magic numbers for the ELF case
if Hdr.E_Ident (0) = 16#7F# and then
Hdr.E_Ident (1) = Character'Pos ('E') and then
Hdr.E_Ident (2) = Character'Pos ('L') and then
Hdr.E_Ident (3) = Character'Pos ('F') and then
Hdr.E_Ident (4) = ELF32_Ops.ELFCLASS64
then
Close (Hdr_Stream);
return new Object_File'
(ELF64_Ops.Initialize (F, Hdr, In_Exception));
end if;
end;
declare
Hdr : constant PECOFF_Ops.Header :=
PECOFF_Ops.Read_Header (Hdr_Stream);
begin
-- Test the magic numbers
if Hdr.Magics (0) = Character'Pos ('P') and then
Hdr.Magics (1) = Character'Pos ('E') and then
Hdr.Magics (2) = 0 and then
Hdr.Magics (3) = 0
then
Close (Hdr_Stream);
return new Object_File'
(PECOFF_Ops.Initialize (F, Hdr, In_Exception));
end if;
exception
-- If this is not a PECOFF file then we've done a seek and read to a
-- random address, possibly raising IO_Error
when IO_Error =>
null;
end;
declare
Hdr : constant XCOFF32_Ops.Header :=
XCOFF32_Ops.Read_Header (Hdr_Stream);
begin
-- Test the magic numbers
if Hdr.f_magic = 8#0737# then
Close (Hdr_Stream);
return new Object_File'
(XCOFF32_Ops.Initialize (F, Hdr, In_Exception));
end if;
end;
Close (Hdr_Stream);
if In_Exception then
return null;
else
raise Format_Error with "unrecognized object format";
end if;
end Open;
----------
-- Read --
----------
function Read (S : in out Mapped_Stream) return Mmap.Str_Access is
function To_Str_Access is
new Ada.Unchecked_Conversion (Address, Str_Access);
begin
return To_Str_Access (Data (S.Region) (Natural (S.Off + 1))'Address);
end Read;
function Read (S : in out Mapped_Stream) return String_Ptr_Len is
begin
return To_String_Ptr_Len (Read (S));
end Read;
procedure Check_Read_Offset (S : Mapped_Stream; Size : uint32) is
begin
if S.Off + Offset (Size) > Offset (Last (S.Region)) then
raise IO_Error with "could not read from object file";
end if;
end Check_Read_Offset;
procedure Read_Raw
(S : in out Mapped_Stream;
Addr : Address;
Size : uint32)
is
function To_Str_Access is
new Ada.Unchecked_Conversion (Address, Str_Access);
Sz : constant Offset := Offset (Size);
begin
-- Check size
pragma Debug (Check_Read_Offset (S, Size));
-- Copy data
To_Str_Access (Addr) (1 .. Positive (Sz)) :=
Data (S.Region) (Positive (S.Off + 1) .. Positive (S.Off + Sz));
-- Update offset
S.Off := S.Off + Sz;
end Read_Raw;
function Read (S : in out Mapped_Stream) return uint8 is
Data : uint8;
begin
Read_Raw (S, Data'Address, Data'Size / SSU);
return Data;
end Read;
function Read (S : in out Mapped_Stream) return uint16 is
Data : uint16;
begin
Read_Raw (S, Data'Address, Data'Size / SSU);
return Data;
end Read;
function Read (S : in out Mapped_Stream) return uint32 is
Data : uint32;
begin
Read_Raw (S, Data'Address, Data'Size / SSU);
return Data;
end Read;
function Read (S : in out Mapped_Stream) return uint64 is
Data : uint64;
begin
Read_Raw (S, Data'Address, Data'Size / SSU);
return Data;
end Read;
function Read (S : in out Mapped_Stream) return int8 is
Data : int8;
begin
Read_Raw (S, Data'Address, Data'Size / SSU);
return Data;
end Read;
function Read (S : in out Mapped_Stream) return int16 is
Data : int16;
begin
Read_Raw (S, Data'Address, Data'Size / SSU);
return Data;
end Read;
function Read (S : in out Mapped_Stream) return int32 is
Data : int32;
begin
Read_Raw (S, Data'Address, Data'Size / SSU);
return Data;
end Read;
function Read (S : in out Mapped_Stream) return int64 is
Data : int64;
begin
Read_Raw (S, Data'Address, Data'Size / SSU);
return Data;
end Read;
------------------
-- Read_Address --
------------------
function Read_Address
(Obj : Object_File; S : in out Mapped_Stream) return uint64
is
Address_32 : uint32;
Address_64 : uint64;
begin
case Obj.Arch is
when i386
| MIPS
| PPC
| SPARC
| ARM
=>
Address_32 := Read (S);
return uint64 (Address_32);
when AARCH64
| IA64
| PPC64
| SPARC64
| x86_64
=>
Address_64 := Read (S);
return Address_64;
when Unknown =>
raise Format_Error with "unrecognized machine architecture";
end case;
end Read_Address;
-------------------
-- Read_C_String --
-------------------
procedure Read_C_String (S : in out Mapped_Stream; B : out Buffer) is
J : Integer := 0;
begin
loop
-- Handle overflow case
if J = B'Last then
B (J) := 0;
exit;
end if;
B (J) := Read (S);
exit when B (J) = 0;
J := J + 1;
end loop;
end Read_C_String;
-------------------
-- Read_C_String --
-------------------
function Read_C_String (S : in out Mapped_Stream) return Str_Access is
Res : constant Str_Access := Read (S);
begin
for J in Res'Range loop
if S.Off + Offset (J - 1) > Offset (Last (S.Region)) then
raise IO_Error with "could not read from object file";
end if;
if Res (J) = ASCII.NUL then
S.Off := S.Off + Offset (J);
return Res;
end if;
end loop;
-- Overflow case
raise Constraint_Error;
end Read_C_String;
-----------------
-- Read_LEB128 --
-----------------
function Read_LEB128 (S : in out Mapped_Stream) return uint32 is
B : uint8;
Shift : Integer := 0;
Res : uint32 := 0;
begin
loop
B := Read (S);
Res := Res or Shift_Left (uint32 (B and 16#7f#), Shift);
exit when (B and 16#80#) = 0;
Shift := Shift + 7;
end loop;
return Res;
end Read_LEB128;
function Read_LEB128 (S : in out Mapped_Stream) return int32 is
B : uint8;
Shift : Integer := 0;
Res : uint32 := 0;
begin
loop
B := Read (S);
Res := Res or Shift_Left (uint32 (B and 16#7f#), Shift);
Shift := Shift + 7;
exit when (B and 16#80#) = 0;
end loop;
if Shift < 32 and then (Res and Shift_Left (1, Shift - 1)) /= 0 then
Res := Res or Shift_Left (-1, Shift);
end if;
return To_int32 (Res);
end Read_LEB128;
-----------------
-- Read_Symbol --
-----------------
function Read_Symbol
(Obj : in out Object_File;
Off : Offset) return Object_Symbol
is
begin
case Obj.Format is
when ELF32 => return ELF32_Ops.Read_Symbol (Obj, Off);
when ELF64 => return ELF64_Ops.Read_Symbol (Obj, Off);
when Any_PECOFF => return PECOFF_Ops.Read_Symbol (Obj, Off);
when XCOFF32 => return XCOFF32_Ops.Read_Symbol (Obj, Off);
end case;
end Read_Symbol;
----------
-- Seek --
----------
procedure Seek (S : in out Mapped_Stream; Off : Offset) is
begin
if Off < 0 or else Off > Offset (Last (S.Region)) then
raise IO_Error with "could not seek to offset in object file";
end if;
S.Off := Off;
end Seek;
----------
-- Size --
----------
function Size (Sec : Object_Section) return uint64 is
begin
return Sec.Size;
end Size;
function Size (Sym : Object_Symbol) return uint64 is
begin
return Sym.Size;
end Size;
------------
-- Strlen --
------------
function Strlen (Buf : Buffer) return int32 is
begin
return int32 (CRTL.strlen (Buf'Address));
end Strlen;
-----------
-- Spans --
-----------
function Spans (Sym : Object_Symbol; Addr : uint64) return Boolean is
begin
return Addr >= Sym.Value and then Addr < Sym.Value + Sym.Size;
end Spans;
---------------
-- To_String --
---------------
function To_String (Buf : Buffer) return String is
Result : String (1 .. Integer (CRTL.strlen (Buf'Address)));
for Result'Address use Buf'Address;
pragma Import (Ada, Result);
begin
return Result;
end To_String;
-----------------------
-- To_String_Ptr_Len --
-----------------------
function To_String_Ptr_Len
(Ptr : Mmap.Str_Access;
Max_Len : Natural := Natural'Last) return String_Ptr_Len
is
begin
for I in 1 .. Max_Len loop
if Ptr (I) = ASCII.NUL then
return (Ptr, I - 1);
end if;
end loop;
return (Ptr, Max_Len);
end To_String_Ptr_Len;
------------------------
-- Trim_Trailing_Nuls --
------------------------
function Trim_Trailing_Nuls (Str : String) return String is
begin
for J in Str'Range loop
if Str (J) = ASCII.NUL then
return Str (Str'First .. J - 1);
end if;
end loop;
return Str;
end Trim_Trailing_Nuls;
-----------
-- Value --
-----------
function Value (Sym : Object_Symbol) return uint64 is
begin
return Sym.Value;
end Value;
end System.Object_Reader;