From 95bea696ca37a3fa550eef97135d86b1826d1b09 Mon Sep 17 00:00:00 2001
From: svenbarth <pascaldragon@googlemail.com>
Date: Fri, 3 Feb 2017 22:36:58 +0000
Subject: [PATCH] + add ability to read Big Obj COFF files as generated by MSVC
 with /bigobj and GNU AS with -mbig-obj (starting from version 2.25); this
 format allows 2^31 sections per COFF file instead of 2^16, thus solving the
 problem of compiling packages\odata\src\sharepoint.pp Note: the field names
 of tcoffbigobjheader and coffbigobjsymbol are inspired from the field names
 of the structs declared in LLVM

git-svn-id: trunk@35379 -
---
 compiler/ogcoff.pas | 186 ++++++++++++++++++++++++++++++++++++--------
 1 file changed, 153 insertions(+), 33 deletions(-)

diff --git a/compiler/ogcoff.pas b/compiler/ogcoff.pas
index b4e6a02055..629b461c57 100644
--- a/compiler/ogcoff.pas
+++ b/compiler/ogcoff.pas
@@ -53,6 +53,18 @@ interface
          opthdr : word;
          flag   : word;
        end;
+       tcoffbigobjheader = packed record
+         Sig1 : word;
+         Sig2 : word;
+         Version : word;
+         Machine : word;
+         TimeDateStame : longword;
+         UUID : array[0..15] of byte;
+         unused : array[0..3] of longword;
+         NumberOfSections : longword;
+         PointerToSymbolTable : longword;
+         NumberOfSymbols : longword;
+       end;
        tcoffpeoptheader = packed record
          Magic : word;
          MajorLinkerVersion : byte;
@@ -171,6 +183,7 @@ interface
          FSecCount : Longint;
          FSecTbl   : ^TObjSectionArray;
          win32     : boolean;
+         bigobj    : boolean;
          function  GetSection(secidx:longint):TObjSection;
          function  Read_str(strpos:longword):string;
          procedure read_relocs(s:TCoffObjSection);
@@ -270,6 +283,8 @@ interface
        COFF_OPT_MAGIC   = $20b;
        TLSDIR_SIZE      = $28;
 {$endif x86_64}
+       COFF_BIG_OBJ_MAGIC: array[0..15] of byte = ($C7, $A1, $BA, $D1, $EE, $BA, $A9, $4B, $AF, $20, $FA, $F6, $6A, $A4, $DC, $B8);
+       COFF_BIG_OBJ_VERSION = 2;
     function ReadDLLImports(const dllname:string;readdllproc:Treaddllproc):boolean;
 
 implementation
@@ -474,6 +489,10 @@ implementation
          sym      : longword;
          reloctype : word;
        end;
+       strtableoffset=packed record
+         Zeroes : longword;
+         Offset : longword;
+       end;
        coffsymbol=packed record
          name    : array[0..3] of char; { real is [0..7], which overlaps the strpos ! }
          strpos  : longword;
@@ -483,6 +502,18 @@ implementation
          typ     : byte;
          aux     : byte;
        end;
+       coffbigobjsymbol=packed record
+         Name               : record
+                                case boolean of
+                                  True: (ShortName : array[0..7] of char);
+                                  False: (Offset : strtableoffset)
+                              end;
+         Value              : longword;
+         SectionNumber      : longword;
+         _Type              : word;
+         StorageClass       : byte;
+         NumberOfAuxSymbols : byte;
+       end;
 
        { This is defined in rtl/win/sysos.inc source }
        tlsdirectory=packed record
@@ -1561,6 +1592,7 @@ const pemagic : array[0..3] of byte = (
       begin
         inherited create;
         win32:=awin32;
+        bigobj:=false;
         FSymTbl:=nil;
       end;
 
@@ -1696,22 +1728,31 @@ const pemagic : array[0..3] of byte = (
         symidx    : aint;
         i         : longint;
         sym       : coffsymbol;
+        bosym     : coffbigobjsymbol;
         objsym    : TObjSymbol;
         bind      : Tasmsymbind;
         strname   : string;
-        auxrec    : array[0..17] of byte;
+        auxrec    : array[0..sizeof(coffsymbol)-1] of byte;
+        boauxrec  : array[0..sizeof(coffbigobjsymbol)-1] of byte;
         objsec    : TObjSection;
+        secidx    : longint;
+        symvalue  : longword;
+        auxcount  : byte;
+        symcls    : byte;
 
         { keeps string manipulations out of main routine }
         procedure UnsupportedSymbolType;
           begin
-            Comment(V_Fatal,'Unsupported COFF symbol type '+tostr(sym.typ)+' at index '+tostr(symidx)+' while reading '+InputFileName);
+            Comment(V_Fatal,'Unsupported COFF symbol type '+tostr(symcls)+' at index '+tostr(symidx)+' while reading '+InputFileName);
           end;
 
       begin
         with TCoffObjData(objdata) do
          begin
-           nsyms:=FCoffSyms.Size div sizeof(CoffSymbol);
+           if bigobj then
+             nsyms:=FCoffSyms.Size div sizeof(coffbigobjsymbol)
+           else
+             nsyms:=FCoffSyms.Size div sizeof(CoffSymbol);
            { Allocate memory for symidx -> TObjSymbol table }
            FSymTbl:=AllocMem(nsyms*sizeof(TObjSymbol));
            { Load the Symbols }
@@ -1719,28 +1760,53 @@ const pemagic : array[0..3] of byte = (
            symidx:=0;
            while (symidx<nsyms) do
             begin
-              FCoffSyms.Read(sym,sizeof(sym));
-              if plongint(@sym.name)^<>0 then
+              if bigobj then
                 begin
-                  move(sym.name,strname[1],8);
-                  strname[9]:=#0;
-                  strname[0]:=chr(strlen(@strname[1]));
-                  if strname='' then
-                    Internalerror(200205171);
+                  FCoffSyms.Read(bosym,sizeof(bosym));
+                  if bosym.Name.Offset.Zeroes<>0 then
+                    begin
+                      move(bosym.Name.ShortName,strname[1],8);
+                      strname[9]:=#0;
+                      strname[0]:=chr(strlen(@strname[1]));
+                      if strname='' then
+                        internalerror(2017020301);
+                    end
+                  else
+                    strname:=Read_str(bosym.Name.Offset.Offset);
+                  secidx:=longint(bosym.SectionNumber);
+                  symvalue:=bosym.Value;
+                  auxcount:=bosym.NumberOfAuxSymbols;
+                  symcls:=bosym.StorageClass;
                 end
               else
-                strname:=Read_str(sym.strpos);
+                begin
+                  FCoffSyms.Read(sym,sizeof(sym));
+                  if plongint(@sym.name)^<>0 then
+                    begin
+                      move(sym.name,strname[1],8);
+                      strname[9]:=#0;
+                      strname[0]:=chr(strlen(@strname[1]));
+                      if strname='' then
+                        Internalerror(200205171);
+                    end
+                  else
+                    strname:=Read_str(sym.strpos);
+                  secidx:=sym.section;
+                  symvalue:=sym.value;
+                  auxcount:=sym.aux;
+                  symcls:=sym.typ;
+                end;
               bind:=AB_EXTERNAL;
               size:=0;
               address:=0;
               objsym:=nil;
               objsec:=nil;
-              case sym.typ of
+              case symcls of
                 COFF_SYM_GLOBAL :
                   begin
-                    if sym.section=0 then
+                    if secidx=0 then
                      begin
-                       if sym.value=0 then
+                       if symvalue=0 then
                         bind:=AB_EXTERNAL
                        else
                         begin
@@ -1751,9 +1817,9 @@ const pemagic : array[0..3] of byte = (
                     else
                      begin
                        bind:=AB_GLOBAL;
-                       objsec:=GetSection(word(sym.section));
-                       if sym.value>=objsec.mempos then
-                         address:=sym.value-objsec.mempos;
+                       objsec:=GetSection(secidx);
+                       if symvalue>=objsec.mempos then
+                         address:=symvalue-objsec.mempos;
                      end;
                     objsym:=CreateSymbol(strname);
                     objsym.bind:=bind;
@@ -1766,11 +1832,11 @@ const pemagic : array[0..3] of byte = (
                 COFF_SYM_LOCAL :
                   begin
                     { do not add constants (section=-1) }
-                    if sym.section<>-1 then
+                    if secidx<>-1 then
                      begin
-                       objsec:=GetSection(word(sym.section));
-                       if sym.value>=objsec.mempos then
-                         address:=sym.value-objsec.mempos;
+                       objsec:=GetSection(secidx);
+                       if symvalue>=objsec.mempos then
+                         address:=symvalue-objsec.mempos;
                        objsym:=CreateSymbol(strname);
                        objsym.bind:=AB_LOCAL;
                        objsym.typ:=AT_FUNCTION;
@@ -1782,11 +1848,11 @@ const pemagic : array[0..3] of byte = (
                 COFF_SYM_SECTION :
                   begin
                     { GetSection checks that index is in range }
-                    objsec:=GetSection(word(sym.section));
+                    objsec:=GetSection(secidx);
                     if assigned(objsec) then
                       begin
-                        if sym.value>=objsec.mempos then
-                          address:=sym.value-objsec.mempos;
+                        if symvalue>=objsec.mempos then
+                          address:=symvalue-objsec.mempos;
                         objsym:=CreateSymbol(strname);
                         objsym.bind:=AB_LOCAL;
                         objsym.typ:=AT_FUNCTION;
@@ -1803,9 +1869,12 @@ const pemagic : array[0..3] of byte = (
               end;
               FSymTbl^[symidx]:=objsym;
               { read aux records }
-              for i:=1 to sym.aux do
+              for i:=1 to auxcount do
                begin
-                 FCoffSyms.Read(auxrec,sizeof(auxrec));
+                 if bigobj then
+                   FCoffSyms.Read(boauxrec,sizeof(boauxrec))
+                 else
+                   FCoffSyms.Read(auxrec,sizeof(auxrec));
                  inc(symidx);
                end;
               inc(symidx);
@@ -1836,12 +1905,17 @@ const pemagic : array[0..3] of byte = (
     function  TCoffObjInput.ReadObjData(AReader:TObjectreader;out objdata:TObjData):boolean;
       var
         secalign : shortint;
+        secofs,
         strpos,
         i        : longint;
+        sympos,
+        symcount,
+        symsize,
         code     : longint;
         objsec   : TCoffObjSection;
         secoptions : TObjSectionOptions;
         header   : tcoffheader;
+        boheader : tcoffbigobjheader;
         sechdr   : tcoffsechdr;
         secname  : string;
         secnamebuf : array[0..15] of char;
@@ -1859,7 +1933,34 @@ const pemagic : array[0..3] of byte = (
                InputError('Can''t read COFF Header');
                exit;
              end;
-           if header.mach<>COFF_MAGIC then
+           if (header.mach=0) and (header.nsects=$ffff) then
+             begin
+               { either a library or big obj COFF }
+               AReader.seek(0);
+               if not AReader.read(boheader,sizeof(boheader)) then
+                 begin
+                   InputError('Can''t read Big Obj COFF Header');
+                   exit;
+                 end;
+               if CompareByte(boheader.UUID,COFF_BIG_OBJ_MAGIC,length(boheader.uuid))<>0 then
+                 begin
+                   { ToDo: this should be treated as a library }
+                   InputError('Illegal Big Obj COFF Magic');
+                   exit;
+                 end;
+               if boheader.Version<>COFF_BIG_OBJ_VERSION then
+                 begin
+                   InputError('Illegal Big Obj COFF Version');
+                   exit;
+                 end;
+               if boheader.Machine<>COFF_MAGIC then
+                 begin
+                   InputError('Illegal COFF Machine type');
+                   exit;
+                 end;
+               bigobj:=true;
+             end
+           else if header.mach<>COFF_MAGIC then
              begin
                InputError('Illegal COFF Magic');
                exit;
@@ -1868,8 +1969,20 @@ const pemagic : array[0..3] of byte = (
            eVCobj:=header.flag=$100;
 {$endif arm}
            { ObjSymbols }
-           AReader.Seek(header.sympos);
-           if not AReader.ReadArray(FCoffSyms,header.syms*sizeof(CoffSymbol)) then
+           if bigobj then
+             begin
+               sympos:=longint(boheader.PointerToSymbolTable);
+               symcount:=longint(boheader.NumberOfSymbols);
+               symsize:=sizeof(CoffBigObjSymbol);
+             end
+           else
+             begin
+               sympos:=longint(header.sympos);
+               symcount:=longint(header.syms);
+               symsize:=sizeof(CoffSymbol);
+             end;
+           AReader.Seek(sympos);
+           if not AReader.ReadArray(FCoffSyms,symcount*symsize) then
              begin
                InputError('Error reading coff symbol table');
                exit;
@@ -1895,10 +2008,17 @@ const pemagic : array[0..3] of byte = (
              end;
            { Section headers }
            { Allocate SecIdx -> TObjSection table, secidx is 1-based }
-           FSecCount:=header.nsects;
-           FSecTbl:=AllocMem((header.nsects+1)*sizeof(TObjSection));
-           AReader.Seek(sizeof(tcoffheader)+header.opthdr);
-           for i:=1 to header.nsects do
+           if bigobj then
+             FSecCount:=longint(boheader.NumberOfSections)
+           else
+             FSecCount:=header.nsects;
+           FSecTbl:=AllocMem((FSecCount+1)*sizeof(TObjSection));
+           if bigobj then
+             secofs:=sizeof(tcoffbigobjheader)
+           else
+             secofs:=sizeof(tcoffheader)+header.opthdr;
+           AReader.Seek(secofs);
+           for i:=1 to FSecCount do
              begin
                if not AReader.read(sechdr,sizeof(sechdr)) then
                 begin