Skip to content

Commit

Permalink
Feature: OmfParser class handles many more OMF record types (#1331)
Browse files Browse the repository at this point in the history
Introduces OmfObjectLoader, which can load an object file as if it were a complete program.
  • Loading branch information
uxmal committed Feb 20, 2024
1 parent 14d30e7 commit ac45b10
Show file tree
Hide file tree
Showing 10 changed files with 894 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/Drivers/reko.config
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
<Loader MagicNumber="CEED6666CC0D000B03730083000C000D" Offset="0x104" Type="Reko.Environments.Gameboy.Loader,Reko.Environments.Gameboy" Description="Nintendo Gameboy ROM" />
<Loader MagicNumber="7F4D4F53" Type="Reko.Environments.MorphOS.MorphosElfImageLoader,Reko.Environments.MorphOS" Description="MorphOS executable" />
<Loader MagicNumber="601A" Type="Reko.Environments.AtariTOS.PrgLoader,Reko.Environments.AtariTOS" Description="Atari TOS .PRG file" />
<Loader MagicNumber="80" Type="Reko.ImageLoaders.Omf.OmfLoader,Reko.ImageLoaders.Omf" Description="Intel OMF object file" />
<Loader MagicNumber="80" Type="Reko.ImageLoaders.Omf.OmfObjectLoader,Reko.ImageLoaders.Omf" Description="Intel OMF object file" />
<Loader MagicNumber="F0" Type="Reko.ImageLoaders.Omf.OmfLoader,Reko.ImageLoaders.Omf" Description="Intel OMF library file" />
<Loader MagicNumber="0061736D" Type="Reko.ImageLoaders.WebAssembly.WasmLoader,Reko.ImageLoaders.WebAssembly" /> <!-- Web assembly -->
<Loader MagicNumber="4E534F30" Type="Reko.Environments.Switch.NSO0Loader,Reko.Environments.Switch" Description="Nintendo NSO0 file" />
Expand Down
2 changes: 1 addition & 1 deletion src/ImageLoaders/Omf/OmfLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public override TypeLibrary Load(IPlatform platform, TypeLibrary dstLib)
}
break;
case RecordType.MODEND:
// Modend's seem to be followed by padding to a 16-byte boundary.
// Modend'Name seem to be followed by padding to a 16-byte boundary.
while ((rdr.Offset & 0xF) != 0 && rdr.TryReadByte(out _))
;
break;
Expand Down
154 changes: 154 additions & 0 deletions src/ImageLoaders/Omf/OmfObjectLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#region License
/*
* Copyright (C) 1999-2024 John Källén.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#endregion

using Reko.Core;
using Reko.Core.Collections;
using Reko.Core.Configuration;
using Reko.Core.Loading;
using Reko.Core.Memory;
using Reko.Core.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Reko.ImageLoaders.Omf
{
/// <summary>
/// Loads an Intel OMF object file.
/// </summary>
public class OmfObjectLoader : ProgramImageLoader
{
public OmfObjectLoader(IServiceProvider services, ImageLocation imageLocation, byte[] imgRaw) : base(services, imageLocation, imgRaw)
{
}

public override Address PreferredBaseAddress
{
get => Address.SegPtr(0x800, 0);
set => throw new NotSupportedException();
}

public override Program LoadProgram(Address? address)
{
var configSvc = Services.RequireService<IConfigurationService>();
var arch = configSvc.GetArchitecture("x86-real-16");
if (arch is null)
throw new InvalidOperationException("Expected X86 real mode architecture to be available.");
var platform = configSvc.GetEnvironment("msdos").Load(Services, arch);
if (platform is null)
throw new InvalidOperationException("Expected MS-DOS environment to be available.");

return LoadProgram(
GetRecordEnumerator(RawImage),
address ?? PreferredBaseAddress,
arch,
platform);
}

private IEnumerable<OmfRecord> GetRecordEnumerator(byte[] rawImage)
{
var parser = new OmfParser(new LeImageReader(RawImage));
while (parser.TryReadRecord(out var record))
yield return record;
}

public Program LoadProgram(
IEnumerable<OmfRecord> omfRecords,
Address address,
IProcessorArchitecture arch,
IPlatform platform)
{
var e = omfRecords.GetEnumerator();
var theader = Expect<TheaderRecord>(e);
//$TODO: name the program after contents in theader?

// Lnames are 1-indexed, so put a dummy in position 0
var labelNames = new List<string> { "" };
// Segments are also 1-indexed, so put a dummy in position 0
var segments = new List<ImageSegment> { null! };
var segmems = new List<byte[]> { null! };
var symbols = new List<ImageSymbol>();
bool modendSeen = false;
while (e.MoveNext())
{
if (modendSeen)
throw new BadImageFormatException("Unexpected data after MODEND OMF record.");
switch (e.Current)
{
case UnknownRecord _:
break;
case LnamesRecord lnames:
labelNames.AddRange(lnames.Names);
break;
case SegdefRecord segdef:
if (segdef.NameIndex == 0 || segdef.NameIndex >= labelNames.Count)
throw new BadImageFormatException("Invalid segment name index.");
var bytes = new byte[segdef.Length];
segmems.Add(bytes);
var memarea = new ByteMemoryArea(address, bytes);
var segment = new ImageSegment(labelNames[segdef.NameIndex], memarea, AccessMode.ReadWriteExecute);
segments.Add(segment);
break;
case PubdefRecord pubdef:
if (pubdef.SegmentIndex == 0 || pubdef.SegmentIndex >= segments.Count)
throw new BadImageFormatException("Invalid segment index.");
segment = segments[pubdef.SegmentIndex]!;
foreach (var name in pubdef.Names)
{
var addrSym = segment.Address + name.Offset;
var symbol = ImageSymbol.Create(SymbolType.Unknown, arch, addrSym, name.Name);
symbols.Add(symbol);
}
break;
case DataRecord data:
if (data.SegmentIndex == 0 || data.SegmentIndex >= segments.Count)
throw new BadImageFormatException("Invalid segment index.");
segment = segments[data.SegmentIndex];
Array.Copy(data.Data, 0, segmems[data.SegmentIndex]!, data.Offset, data.Data.Length);
break;
case ModendRecord:
modendSeen = true;
break;
}
}
var segmentMap = new SegmentMap(segments.Skip(1).ToArray()!);
var program = new Program(
new ProgramMemory(segmentMap),
arch,
platform,
symbols.ToSortedList(s => s.Address),
new());
return program;
}

private T Expect<T>(IEnumerator<OmfRecord> records)
where T : OmfRecord
{
if (!records.MoveNext())
throw new BadImageFormatException($"Expected an OMF record of type {typeof(T).Name}");
if (records.Current is not T result)
throw new BadImageFormatException($"Expected an OMF record of type {typeof(T).Name}," +
$"but read a record of type {records.Current.GetType().Name}.");
return result;
}
}
}
Loading

0 comments on commit ac45b10

Please sign in to comment.