------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                 S Y S T E M . O B J E C T _ R E A D E R                  --
--                                                                          --
--                                 S p e c                                  --
--                                                                          --
--          Copyright (C) 2009-2020, 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.      --
--                                                                          --
------------------------------------------------------------------------------

--  This package implements a simple, minimal overhead reader for object files
--  composed of sections of untyped heterogeneous binary data.

with Interfaces;
with System.Mmap;

package System.Object_Reader is

   --------------
   --  Limits  --
   --------------

   BUFFER_SIZE : constant := 8 * 1024;

   ---------------------
   -- Object sections --
   ----------------------

   type Object_Section is private;

   Null_Section : constant Object_Section;

   --------------------
   -- Object symbols --
   --------------------

   type Object_Symbol is private;

   ------------------------
   -- Object format type --
   ------------------------

   type Object_Format is
     (ELF32,
      --  Object format is 32-bit ELF

      ELF64,
      --  Object format is 64-bit ELF

      PECOFF,
      --  Object format is Microsoft PECOFF

      PECOFF_PLUS,
      --  Object format is Microsoft PECOFF+

      XCOFF32);
      --  Object format is AIX 32-bit XCOFF

   --  PECOFF | PECOFF_PLUS appears so often as a case choice, would
   --  seem a good idea to have a subtype name covering these two choices ???

   ------------------
   -- Object files --
   ------------------

   type Object_File (Format : Object_Format) is private;

   type Object_File_Access is access Object_File;

   ------------------------------
   -- Object architecture type --
   ------------------------------

   type Object_Arch is
     (Unknown,
      --  The target architecture has not yet been determined

      SPARC,
      --  32-bit SPARC

      SPARC64,
      --  64-bit SPARC

      i386,
      --  Intel IA32

      MIPS,
      --  MIPS Technologies MIPS

      x86_64,
      --  x86-64 (64-bit AMD/Intel)

      IA64,
      --  Intel IA64

      PPC,
      --  32-bit PowerPC

      PPC64,
      --  64-bit PowerPC

      ARM);
      --  32-bit ARM

   ------------------
   -- Target types --
   ------------------

   subtype Offset is Interfaces.Integer_64;

   subtype uint8  is Interfaces.Unsigned_8;
   subtype uint16 is Interfaces.Unsigned_16;
   subtype uint32 is Interfaces.Unsigned_32;
   subtype uint64 is Interfaces.Unsigned_64;

   subtype int8  is Interfaces.Integer_8;
   subtype int16 is Interfaces.Integer_16;
   subtype int32 is Interfaces.Integer_32;
   subtype int64 is Interfaces.Integer_64;

   type Buffer is array (0 .. BUFFER_SIZE - 1) of uint8;

   type String_Ptr_Len is record
      Ptr : Mmap.Str_Access;
      Len : Natural;
   end record;
   --  A string made from a pointer and a length. Not all strings for name
   --  are C strings: COFF inlined symbol names have a max length of 8.

   -------------------------------------------
   -- Operations on buffers of untyped data --
   -------------------------------------------

   function To_String (Buf : Buffer) return String;
   --  Construct string from C style null-terminated string stored in a buffer

   function To_String_Ptr_Len
     (Ptr : Mmap.Str_Access;
      Max_Len : Natural := Natural'Last) return String_Ptr_Len;
   --  Convert PTR to a String_Ptr_Len.

   function Strlen (Buf : Buffer) return int32;
   --  Return the length of a C style null-terminated string

   -------------------------
   -- Opening and closing --
   -------------------------

   function Open
     (File_Name    : String;
      In_Exception : Boolean := False) return Object_File_Access;
   --  Open the object file and initialize the reader. In_Exception is true
   --  when the parsing is done as part of an exception handler decorator. In
   --  this mode we do not want to raise an exception.

   procedure Close (Obj : in out Object_File);
   --  Close the object file

   -----------------------
   -- Sequential access --
   -----------------------

   type Mapped_Stream is private;
   --  Provide an abstraction of a stream on a memory mapped file

   function Create_Stream (Mf : System.Mmap.Mapped_File;
                           File_Offset : System.Mmap.File_Size;
                           File_Length : System.Mmap.File_Size)
                          return Mapped_Stream;
   --  Create a stream from Mf

   procedure Close (S : in out Mapped_Stream);
   --  Close the stream (deallocate memory)

   procedure Read_Raw
     (S   : in out Mapped_Stream;
      Addr  : Address;
      Size  : uint32);
   pragma Inline (Read_Raw);
   --  Read a number of fixed sized records

   procedure Seek (S : in out Mapped_Stream; Off : Offset);
   --  Seek to an absolute offset in bytes

   procedure Tell (Obj : in out Mapped_Stream; Off : out Offset)
     with Inline;
   function Tell (Obj : Mapped_Stream) return Offset
     with Inline;
   --  Fetch the current offset

   function Length (Obj : Mapped_Stream) return Offset
     with Inline;
   --  Length of the stream

   function Read (S : in out Mapped_Stream) return Mmap.Str_Access;
   --  Provide a pointer in memory at the current offset

   function Read (S : in out Mapped_Stream) return String_Ptr_Len;
   --  Provide a pointer in memory at the current offset

   function Read (S : in out Mapped_Stream) return uint8;
   function Read (S : in out Mapped_Stream) return uint16;
   function Read (S : in out Mapped_Stream) return uint32;
   function Read (S : in out Mapped_Stream) return uint64;
   function Read (S : in out Mapped_Stream) return int8;
   function Read (S : in out Mapped_Stream) return int16;
   function Read (S : in out Mapped_Stream) return int32;
   function Read (S : in out Mapped_Stream) return int64;
   --  Read a scalar

   function Read_Address
     (Obj : Object_File; S : in out Mapped_Stream) return uint64;
   --  Read either a 64 or 32 bit address from the file stream depending on the
   --  address size of the target architecture and promote it to a 64 bit type.

   function Read_LEB128 (S : in out Mapped_Stream) return uint32;
   function Read_LEB128 (S : in out Mapped_Stream) return int32;
   --  Read a value encoding in Little-Endian Base 128 format

   procedure Read_C_String (S : in out Mapped_Stream; B : out Buffer);
   function Read_C_String (S : in out Mapped_Stream) return Mmap.Str_Access;
   --  Read a C style NULL terminated string

   function Offset_To_String
     (S : in out Mapped_Stream;
      Off : Offset) return String;
   --  Construct a string from a C style NULL terminated string located at an
   --  offset into the object file.

   ------------------------
   -- Object information --
   ------------------------

   function Arch (Obj : Object_File) return Object_Arch;
   --  Return the object architecture

   function Format (Obj : Object_File) return Object_Format;
   --  Return the object file format

   function Get_Load_Address (Obj : Object_File) return uint64;
   --  Return the load address defined in Obj. May raise Format_Error if not
   --  implemented

   function Num_Sections (Obj : Object_File) return uint32;
   --  Return the number of sections composing the object file

   function Get_Section
     (Obj   : in out Object_File;
      Shnum : uint32) return Object_Section;
   --  Return the Nth section (numbered from zero)

   function Get_Section
     (Obj      : in out Object_File;
      Sec_Name : String) return Object_Section;
   --  Return a section by name

   function Create_Stream
     (Obj : Object_File;
      Sec : Object_Section) return Mapped_Stream;
   --  Create a stream for section Sec

   procedure Get_Xcode_Bounds
     (Obj   : in out Object_File;
      Low, High : out uint64);
   --  Return the low and high addresses of the code for the object file. Can
   --  be used to check if an address in within this object file. This
   --  procedure is not efficient and the result should be saved to avoid
   --  recomputation.

   -------------------------
   -- Section information --
   -------------------------

   function Name
     (Obj : in out Object_File;
      Sec : Object_Section) return String;
   --  Return the name of a section as a string

   function Size (Sec : Object_Section) return uint64;
   --  Return the size of a section in bytes

   function Num (Sec : Object_Section) return uint32;
   --  Return the index of a section from zero

   function Off (Sec : Object_Section) return Offset;
   --  Return the byte offset of the section within the object

   ------------------------------
   -- Symbol table information --
   ------------------------------

   Null_Symbol : constant Object_Symbol;
   --  An empty symbol table entry.

   function First_Symbol (Obj : in out Object_File) return Object_Symbol;
   --  Return the first element in the symbol table or Null_Symbol if the
   --  symbol table is empty.

   function Next_Symbol
     (Obj  : in out Object_File;
      Prev : Object_Symbol) return Object_Symbol;
   --  Return the element following Prev in the symbol table, or Null_Symbol if
   --  Prev is the last symbol in the table.

   function Read_Symbol
     (Obj : in out Object_File;
      Off : Offset) return Object_Symbol;
   --  Read symbol at Off

   function Name
     (Obj : in out Object_File;
      Sym : Object_Symbol) return String_Ptr_Len;
   --  Return the name of the symbol

   function Decoded_Ada_Name
     (Obj : in out Object_File;
      Sym : String_Ptr_Len) return String;
   --  Return the decoded name of a symbol encoded as per exp_dbug.ads

   function Strip_Leading_Char
     (Obj : in out Object_File;
      Sym : String_Ptr_Len) return Positive;
   --  Return the index of the first character to decode the name. This can
   --  strip one character for ABI with a prefix (like x86 for PECOFF).

   function Value (Sym : Object_Symbol) return uint64;
   --  Return the name of the symbol

   function Size (Sym : Object_Symbol) return uint64;
   --  Return the size of the symbol in bytes

   function Spans (Sym : Object_Symbol; Addr : uint64) return Boolean;
   --  Determine whether a particular address corresponds to the range
   --  referenced by this symbol.

   function Off (Sym : Object_Symbol) return Offset;
   --  Return the offset of the symbol.

   ----------------
   -- Exceptions --
   ----------------

   IO_Error : exception;
   --  Input/Output error reading file

   Format_Error : exception;
   --  Encountered a problem parsing the object

private
   type Mapped_Stream is record
      Region : System.Mmap.Mapped_Region;
      Off    : Offset;
      Len    : Offset;
   end record;

   subtype ELF is Object_Format range ELF32 .. ELF64;
   subtype Any_PECOFF is Object_Format range PECOFF .. PECOFF_PLUS;

   type Object_File (Format : Object_Format) is record
      Mf           : System.Mmap.Mapped_File :=
                        System.Mmap.Invalid_Mapped_File;
      Arch         : Object_Arch := Unknown;

      Num_Sections : uint32 := 0;
      --  Number of sections

      Symtab_Last : Offset;       --  Last offset of symbol table

      In_Exception : Boolean := False;
      --  True if the parsing is done as part of an exception handler

      Sectab_Stream : Mapped_Stream;
      --  Section table

      Symtab_Stream : Mapped_Stream;
      --  Symbol table

      Symstr_Stream : Mapped_Stream;
      --  Symbol strings

      case Format is
         when ELF =>
            Secstr_Stream : Mapped_Stream;
            --  Section strings
         when Any_PECOFF =>
            ImageBase   : uint64;       --  ImageBase value from header

            --  Cache for latest result of Get_Section_Virtual_Address

            GSVA_Sec  : uint32 := uint32'Last;
            GSVA_Addr : uint64;
         when XCOFF32 =>
            null;
      end case;
   end record;

   subtype ELF_Object_File is Object_File; -- with
   --  Predicate => ELF_Object_File.Format in ELF;
   subtype PECOFF_Object_File is Object_File; -- with
   --  Predicate => PECOFF_Object_File.Format in Any_PECOFF;
   subtype XCOFF32_Object_File is Object_File; -- with
   --  Predicate => XCOFF32_Object_File.Format in XCOFF32;
   --  ???Above predicates cause the compiler to crash when instantiating
   --  ELF64_Ops (see package body).

   type Object_Section is record
      Num        : uint32 := 0;
      --  Section index in the section table

      Off        : Offset := 0;
      --  First byte of the section in the object file

      Addr       : uint64 := 0;
      --  Load address of the section. Valid only when Flag_Alloc is true.

      Size       : uint64 := 0;
      --  Length of the section in bytes

      Flag_Xcode : Boolean := False;
      --  True if the section is advertised to contain executable code
   end record;

   Null_Section : constant Object_Section := (0, 0, 0, 0, False);

   type Object_Symbol is record
      Off   : Offset := 0;  --  Offset of underlying symbol on disk
      Next  : Offset := 0;  --  Offset of the following symbol
      Value : uint64 := 0;  --  Value associated with this symbol
      Size  : uint64 := 0;  --  Size of the referenced entity
   end record;

   Null_Symbol : constant Object_Symbol := (0, 0, 0, 0);
end System.Object_Reader;
