------------------------------------------------------------------------------
--                                                                          --
--                         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-2025, 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_S390        : constant := 22; --  IBM S/390
      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
      EM_RISCV       : constant := 243; --  RISC-V

      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_SPARCV9 =>
               Res.Arch := SPARC64;

            when EM_MIPS
               | EM_MIPS_RS3_LE
            =>
               Res.Arch := MIPS;

            when EM_PPC =>
               Res.Arch := PPC;

            when EM_PPC64 =>
               Res.Arch := PPC64;

            when EM_S390 =>
               Res.Arch := S390;

            when EM_386 =>
               Res.Arch := i386;

            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 EM_RISCV =>
               Res.Arch := RISCV;

            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 RISCV | S390 =>
            case Obj.Format is
               when ELF32 =>
                  Address_32 := Read (S);
                  return uint64 (Address_32);

               when ELF64 =>
                  Address_64 := Read (S);
                  return Address_64;

               when others =>
                  raise Format_Error with "unrecognized object format";
            end case;

         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;
