1 {
2  This unit contains the types needed for reading Elf images.
3 
4  This file was ported from DUBY. See svn log for details
5 
6  ---------------------------------------------------------------------------
7 
8   ***************************************************************************
9  *                                                                         *
10  *   This source is free software; you can redistribute it and/or modify   *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  *   This code is distributed in the hope that it will be useful, but      *
16  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
18  *   General Public License for more details.                              *
19  *                                                                         *
20  *   A copy of the GNU General Public License is available on the World    *
21  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
22  *   obtain it by writing to the Free Software Foundation,                 *
23  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
24  *                                                                         *
25  ***************************************************************************
26 }
27 unit FpImgReaderElf;
28 
29 {$mode objfpc}{$H+}
30 
31 interface
32 
33 uses
34   Classes, SysUtils,
35   LazUTF8, {$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif},
36   DbgIntfBaseTypes,
37   // FpDebug
38   FpImgReaderBase, fpDbgSymTable, FpImgReaderElfTypes, FpDbgCommon;
39 
40 type
41   TElfSection = packed record
42     name      : AnsiString;
43     FileOfs   : QWord;
44     Address   : QWord;
45     Size      : QWord;
46   end;
47   PElfSection = ^TElfSection;
48 
49   { TElfFile }
50 
51   TElfFile = class(TObject)
52   private
53     FTargetInfo: TTargetDescriptor;
FElfToMachineTypenull54     function FElfToMachineType(machinetype: word): TMachineType;
55   protected
Load32BitFilenull56     function Load32BitFile(ALoader: TDbgFileLoader): Boolean;
Load64BitFilenull57     function Load64BitFile(ALoader: TDbgFileLoader): Boolean;
58     procedure AddSection(const name: AnsiString; FileOffset, Address, Size: Qword);
59   public
60     sections  : array of TElfSection;
61     seccount  : Integer;
LoadFromFilenull62     function LoadFromFile(ALoader: TDbgFileLoader): Boolean;
FindSectionnull63     function FindSection(const Name: String): Integer;
64   end;
65 
66   { TElfDbgSource }
67 
68   TElfDbgSource = class(TDbgImageReader) // executable parser
69   private
70     FSections: TStringListUTF8Fast;
71     FFileLoader     : TDbgFileLoader;
72     fOwnSource  : Boolean;
73     fElfFile    : TElfFile;
74   protected
GetSectionnull75     function GetSection(const AName: String): PDbgImageSection; override;
76     procedure LoadSections;
77     procedure ClearSections;
78   public
isValidnull79     class function isValid(ASource: TDbgFileLoader): Boolean; override;
UserNamenull80     class function UserName: AnsiString; override;
81     constructor Create(ASource: TDbgFileLoader; ADebugMap: TObject; OwnSource: Boolean); override;
82     destructor Destroy; override;
83     procedure ParseSymbolTable(AFpSymbolInfo: TfpSymbolList); override;
84 
GetSectionInfonull85     //function GetSectionInfo(const SectionName: AnsiString; var Size: int64): Boolean; override;
86     //function GetSectionData(const SectionName: AnsiString; Offset, Size: Int64; var Buf: array of byte): Int64; override;
87   end;
88 
89 implementation
90 
91 type
92   TElf32symbol=record
93     st_name  : longword;
94     st_value : longword;
95     st_size  : longword;
96     st_info  : byte; { bit 0-3: type, 4-7: bind }
97     st_other : byte;
98     st_shndx : word;
99   end;
100   PElf32symbolArray = ^TElf32symbolArray;
101   TElf32symbolArray = array[0..maxSmallint] of TElf32symbol;
102 
103   TElf64symbol=record
104     st_name  : longword;
105     st_info  : byte; { bit 0-3: type, 4-7: bind }
106     st_other : byte;
107     st_shndx : word;
108     st_value : qword;
109     st_size  : qword;
110   end;
111   PElf64symbolArray = ^TElf64symbolArray;
112   TElf64symbolArray = array[0..maxSmallint] of TElf64symbol;
113 
114 
115 
116 const
117   // Symbol-map section name
118   _symbol        = '.symtab';
119   _symbolstrings = '.strtab';
120 
121 { TElfFile }
122 
TElfFile.FElfToMachineTypenull123 function TElfFile.FElfToMachineType(machinetype: word): TMachineType;
124 begin
125   case machinetype of
126     EM_386:       result := mt386;
127     EM_68K:       result := mt68K;
128     EM_PPC:       result := mtPPC;
129     EM_PPC64:     result := mtPPC64;
130     EM_ARM:       result := mtARM;
131     EM_OLD_ALPHA: result := mtOLD_ALPHA;
132     EM_IA_64:     result := mtIA_64;
133     EM_X86_64:    result := mtX86_64;
134     EM_AVR:       result := mtAVR8;
135     EM_ALPHA:     result := mtALPHA;
136   else
137     result := mtNone;
138   end;
139 
140   // If OS is not encoded in header, take some guess based on machine type
141   if FTargetInfo.OS = osNone then
142   begin
143     if result = mtAVR8 then
144       FTargetInfo.OS := osEmbedded
145     else
146       // Default to the same as host...
147       FTargetInfo.OS := {$if defined(Linux)}osLinux
148                     {$elseif defined(Darwin)}osDarwin
149                     {$else}osWindows{$endif};
150   end;
151 end;
152 
Load32BitFilenull153 function TElfFile.Load32BitFile(ALoader: TDbgFileLoader): Boolean;
154 var
155   hdr   : Elf32_Ehdr;
156   sect  : array of Elf32_shdr;
157   i, j  : integer;
158   nm    : string;
159   sz    : LongWord;
160   strs  : array of byte;
161 begin
162   Result := ALoader.Read(0, sizeof(hdr), @hdr) = sizeof(hdr);
163   if not Result then Exit;
164 
165   FTargetInfo.machineType := FElfToMachineType(hdr.e_machine);
166 
167   SetLength(sect, hdr.e_shnum);
168   //ALoader.Position := hdr.e_shoff;
169 
170   sz := hdr.e_shetsize * hdr.e_shnum;
171   if sz > LongWord(length(sect)*sizeof(Elf32_shdr)) then begin
172     debugln(['TElfFile.Load32BitFile Size of SectHdrs is ', sz, ' expected ', LongWord(length(sect)*sizeof(Elf32_shdr))]);
173     sz := LongWord(length(sect)*sizeof(Elf32_shdr));
174   end;
175   //ALoader.Read(sect[0], sz);
176   ALoader.Read(hdr.e_shoff, sz, @sect[0]);
177 
178   i := sect[hdr.e_shstrndx].sh_offset;
179   j := sect[hdr.e_shstrndx].sh_size;
180   SetLength(strs, j);
181   //ALoader.Position:=i;
182   //ALoader.Read(strs[0], j);
183   ALoader.Read(i, j, @strs[0]);
184 
185   for i := 0 to hdr.e_shnum - 1 do
186     with sect[i] do begin
187       nm := PChar( @strs[sh_name] );
188       AddSection(nm, sh_offset, sh_addr, sh_size );
189     end;
190 end;
191 
Load64BitFilenull192 function TElfFile.Load64BitFile(ALoader: TDbgFileLoader): Boolean;
193 var
194   hdr   : Elf64_Ehdr;
195   sect  : array of Elf64_shdr;
196   i, j  : integer;
197   nm    : string;
198   sz    : LongWord;
199   strs  : array of byte;
200 begin
201   Result := ALoader.Read(0, sizeof(hdr), @hdr) = sizeof(hdr);
202   if not Result then Exit;
203 
204   FTargetInfo.machineType := FElfToMachineType(hdr.e_machine);
205 
206   SetLength(sect, hdr.e_shnum);
207   //ALoader.Position := hdr.e_shoff;
208 
209   sz := hdr.e_shentsize * hdr.e_shnum;
210   if sz > LongWord(length(sect)*sizeof(Elf64_shdr)) then begin
211     debugln(['TElfFile.Load64BitFile Size of SectHdrs is ', sz, ' expected ', LongWord(length(sect)*sizeof(Elf64_shdr))]);
212     sz := LongWord(length(sect)*sizeof(Elf64_shdr));
213   end;
214   //ALoader.Read(sect[0], sz);
215   ALoader.Read(hdr.e_shoff, sz, @sect[0]);
216 
217   i := sect[hdr.e_shstrndx].sh_offset;
218   j := sect[hdr.e_shstrndx].sh_size;
219   SetLength(strs, j);
220   //ALoader.Position:=i;
221   //ALoader.Read(strs[0], j);
222   ALoader.Read(i, j, @strs[0]);
223 
224   for i := 0 to hdr.e_shnum - 1 do
225     with sect[i] do begin
226       nm := PChar( @strs[sh_name] );
227       AddSection(nm, sh_offset, sh_address, sh_size );
228     end;
229 end;
230 
231 procedure TElfFile.AddSection(const name: AnsiString; FileOffset, Address,
232   Size: Qword);
233 begin
234   if seccount=Length(sections) then begin
235     if seccount = 0 then SetLength(sections, 4)
236     else SetLength(sections, seccount*2);
237   end;
238   sections[seccount].Address:= Address;
239   sections[seccount].name:=name;
240   sections[seccount].FileOfs:=FileOffset;
241   sections[seccount].Size:=Size;
242   inc(seccount);
243 end;
244 
LoadFromFilenull245 function TElfFile.LoadFromFile(ALoader: TDbgFileLoader): Boolean;
246 var
247   ident : array [0..EINDENT-1] of byte;
248 begin
249   try
250     Result :=  ALoader.Read(0, sizeof(ident), @ident[0]) = sizeof(ident);
251     if not Result then Exit;
252 
253     Result := (ident[EI_MAG0] = $7f) and
254               (ident[EI_MAG1] = byte('E')) and
255               (ident[EI_MAG2] = byte('L')) and
256               (ident[EI_MAG3] = byte('F'));
257     if not Result then Exit;
258 
259     Result := False;
260     case ident[EI_DATA] of
261       ELFDATA2LSB: FTargetInfo.ByteOrder := boLSB;
262       ELFDATA2MSB: FTargetInfo.ByteOrder := boMSB;
263     else
264       FTargetInfo.byteOrder := boNone;
265     end;
266 
267     case ident[EI_OSABI] of
268       ELFOSABI_LINUX: FTargetInfo.OS := osLinux;
269       ELFOSABI_STANDALONE: FTargetInfo.OS := osEmbedded;
270     else
271       FTargetInfo.OS := osNone;  // Will take a guess after machine type is available
272     end;
273 
274     if ident[EI_CLASS] = ELFCLASS32 then begin
275       FTargetInfo.bitness := b32;
276       Result := Load32BitFile(ALoader);
277       exit;
278     end;
279 
280     if ident[EI_CLASS] = ELFCLASS64 then begin
281       FTargetInfo.bitness := b64;
282       Result := Load64BitFile(ALoader);
283       exit;
284     end;
285 
286   except
287     Result := false;
288   end;
289 end;
290 
FindSectionnull291 function TElfFile.FindSection(const Name: String): Integer;
292 var
293   i : Integer;
294 begin
295   Result := -1;
296   for i := 0 to seccount - 1 do
297     if sections[i].name = Name then begin
298       Result := i;
299       Exit;
300     end;
301 end;
302 
303 { TElfDbgSource }
304 
GetSectionnull305 function TElfDbgSource.GetSection(const AName: String): PDbgImageSection;
306 var
307   i: Integer;
308   ex: PDbgImageSectionEx;
309 begin
310   Result := nil;
311   i := FSections.IndexOf(AName);
312   if i < 0 then
313     exit;
314   ex := PDbgImageSectionEx(FSections.Objects[i]);
315   Result := @ex^.Sect;
316   if ex^.Loaded then
317     exit;
318   ex^.Loaded  := True;
319   FFileLoader.LoadMemory(ex^.Offs, Result^.Size, Result^.RawData);
320 end;
321 
322 procedure TElfDbgSource.LoadSections;
323 var
324   p: PDbgImageSectionEx;
325   idx: integer;
326   i: Integer;
327   fs: TElfSection;
328 begin
329   for i := 0 to fElfFile.seccount - 1 do begin
330     fs := fElfFile.sections[i];
331     idx := FSections.AddObject(fs.name, nil);
332     New(p);
333     P^.Offs := fs.FileOfs;
334     p^.Sect.Size := fs.Size;
335     p^.Sect.VirtualAddress := 0; // Todo? fs.Address - ImageBase
336     p^.Loaded := False;
337     FSections.Objects[idx] := TObject(p);
338   end;
339 end;
340 
341 procedure TElfDbgSource.ClearSections;
342 var
343   i: Integer;
344 begin
345   for i := 0 to FSections.Count-1 do
346     Freemem(FSections.Objects[i]);
347   FSections.Clear;
348 end;
349 
TElfDbgSource.isValidnull350 class function TElfDbgSource.isValid(ASource: TDbgFileLoader): Boolean;
351 var
352   buf : array [0..3+sizeof(Elf32_EHdr)] of byte;
353 begin
354   try
355     Result := Assigned(ASource) and
356       (ASource.Read(0, sizeof(Elf32_EHdr), @buf[0]) = sizeof(Elf32_EHdr));
357 
358     if not Result then Exit;
359 
360     Result := (buf[EI_MAG0] = $7f) and (buf[EI_MAG1] = byte('E')) and
361               (buf[EI_MAG2] = byte('L')) and (buf[EI_MAG3] = byte('F'));
362   except
363     Result := false;
364   end;
365 end;
366 
TElfDbgSource.UserNamenull367 class function TElfDbgSource.UserName: AnsiString;
368 begin
369   Result := 'ELF executable';
370 end;
371 
372 constructor TElfDbgSource.Create(ASource: TDbgFileLoader; ADebugMap: TObject; OwnSource: Boolean);
373 var
374   DbgFileName, SourceFileName: String;
375   crc: Cardinal;
376   NewFileLoader: TDbgFileLoader;
377 begin
378   FSections := TStringListUTF8Fast.Create;
379   FSections.Sorted := True;
380   //FSections.Duplicates := dupError;
381   FSections.CaseSensitive := False;
382 
383   FFileLoader := ASource;
384   fOwnSource := OwnSource;
385   fElfFile := TElfFile.Create;
386   fElfFile.LoadFromFile(FFileLoader);
387 
388   LoadSections;
389   // check external debug file
390   if ReadGnuDebugLinkSection(DbgFileName, crc) then
391   begin
392     SourceFileName := ASource.FileName;
393     if SourceFileName<>'' then
394       SourceFileName := ExtractFilePath(SourceFileName);
395     NewFileLoader := LoadGnuDebugLink(SourceFileName, DbgFileName, crc);
396     if NewFileLoader <> nil then begin
397       if fOwnSource then
398         FFileLoader.Free;
399 
400       FFileLoader := NewFileLoader;
401       fOwnSource := True;
402 
403       fElfFile.Free;
404       fElfFile := TElfFile.Create;
405       fElfFile.LoadFromFile(FFileLoader);
406 
407       ClearSections;
408       LoadSections;
409     end;
410   end;
411 
412   FTargetInfo := fElfFile.FTargetInfo;
413 
414   inherited Create(ASource, ADebugMap, OwnSource);
415 end;
416 
417 destructor TElfDbgSource.Destroy;
418 begin
419   if fOwnSource then FFileLoader.Free;
420   fElfFile.Free;
421   ClearSections;
422   FreeAndNil(FSections);
423   inherited Destroy;
424 end;
425 
426 procedure TElfDbgSource.ParseSymbolTable(AFpSymbolInfo: TfpSymbolList);
427 var
428   p: PDbgImageSection;
429   ps: PDbgImageSection;
430   SymbolArr32: PElf32symbolArray;
431   SymbolArr64: PElf64symbolArray;
432   SymbolStr: pointer;
433   i: integer;
434   SymbolCount: integer;
435   SymbolName: AnsiString;
436   SectIdx: Word;
437   Sect: PElfSection;
438 begin
439   AfpSymbolInfo.SetAddressBounds(1, High(AFpSymbolInfo.HighAddr)); // always search / TODO: iterate all sections for bounds
440   p := Section[_symbol];
441   ps := Section[_symbolstrings];
442   if assigned(p) and assigned(ps) then
443   begin
444     SymbolStr:=PDbgImageSectionEx(ps)^.Sect.RawData;
445     if FTargetInfo.Bitness = b64 then
446     begin
447       SymbolArr64:=PDbgImageSectionEx(p)^.Sect.RawData;
448       SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(TElf64symbol);
449       for i := 0 to SymbolCount-1 do
450       begin
451         begin
452           {$push}
453           {$R-}
454           if SymbolArr64^[i].st_name<>0 then
455             begin
456             SectIdx := SymbolArr64^[i].st_shndx;
457             if (SectIdx < 0) or (SectIdx >= fElfFile.seccount) then
458               continue;
459             Sect := @fElfFile.sections[SectIdx];
460             if Sect^.Address = 0 then
461               continue; // not loaded, symbol not in memory
462 
463             SymbolName:=pchar(SymbolStr+SymbolArr64^[i].st_name);
464             AfpSymbolInfo.Add(SymbolName, TDbgPtr(SymbolArr64^[i].st_value+ImageBase),
465               Sect^.Address + Sect^.Size);
466             end;
467           {$pop}
468         end
469       end;
470     end
471     else
472     begin
473       SymbolArr32:=PDbgImageSectionEx(p)^.Sect.RawData;
474       SymbolCount := PDbgImageSectionEx(p)^.Sect.Size div sizeof(TElf32symbol);
475       for i := 0 to SymbolCount-1 do
476       begin
477         begin
478           if SymbolArr32^[i].st_name<>0 then
479             begin
480             SectIdx := SymbolArr32^[i].st_shndx;
481             if (SectIdx < 0) or (SectIdx >= fElfFile.seccount) then
482               continue;
483             Sect := @fElfFile.sections[SectIdx];
484             if Sect^.Address = 0 then
485               continue; // not loaded, symbol not in memory
486 
487             SymbolName:=pchar(SymbolStr+SymbolArr32^[i].st_name);
488             AfpSymbolInfo.Add(SymbolName, TDBGPtr(SymbolArr32^[i].st_value+ImageBase),
489               Sect^.Address + Sect^.Size);
490             end;
491         end
492       end;
493     end;
494   end;
495 end;
496 
497 initialization
498   RegisterImageReaderClass( TElfDbgSource );
499 
500 end.
501 
502