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