#region License /* * Copyright (C) 1999-2023 Pavel Tomin. * * 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.Hll.C; using Reko.Core.Memory; using Reko.Core.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Reko.Scripts.Python { /// /// Decompiling functionality which provided for /// using in user-defined scripts /// public class RekoProgramAPI { private readonly Program program; public RekoProgramAPI(Program program) { this.program = program; } public Address Address(ulong linearAddress) { return program.SegmentMap.MapLinearAddressToAddress(linearAddress); } public Address Address(string sAddr) { if (TryConvertAddress(sAddr, out var addr)) return addr; throw new ArgumentException($"Failed to parse address: {sAddr}."); } public byte ReadByte(Address addr) { return CreateImageReader(addr).ReadByte(); } public IEnumerable ReadBytes(Address startAddr, long length) { return ReadData(rdr => rdr.ReadByte(), startAddr, length); } public short ReadInt16(Address addr) { return CreateImageReader(addr).ReadInt16(); } public IEnumerable ReadInts16(Address startAddr, long length) { return ReadData(rdr => rdr.ReadInt16(), startAddr, length); } public int ReadInt32(Address addr) { return CreateImageReader(addr).ReadInt32(); } public IEnumerable ReadInts32(Address startAddr, long length) { return ReadData(rdr => rdr.ReadInt32(), startAddr, length); } public long ReadInt64(Address addr) { return CreateImageReader(addr).ReadInt64(); } public IEnumerable ReadInts64(Address startAddr, long length) { return ReadData(rdr => rdr.ReadInt64(), startAddr, length); } public string ReadCString(Address addr) { var rdr = CreateImageReader(addr); var c = rdr.ReadCString(PrimitiveType.Char, program.TextEncoding); return c.ToString(); } public void SetUserComment(Address addr, string comment) { program.User.Annotations[addr] = NormalizeLineEndings(comment); } public void SetUserGlobal(Address addr, string decl) { var usb = new UserSignatureBuilder(program); var global = usb.ParseGlobalDeclaration(decl); var name = global?.Name; var dataType = global?.DataType; if (name is null || dataType is null) throw new ArgumentException( $"Failed to parse global variable declaration: '{decl}'."); var arch = program.Architecture; program.ModifyUserGlobal(arch, addr, dataType, name); } public string[] GetProcedureAddresses() { return program.User.Procedures.Keys. Union(program.Procedures.Keys). Select(addr => addr.ToString()).ToArray(); } public bool ContainsProcedureAddress(Address addr) { if (program.User.Procedures.ContainsKey(addr)) return true; if (program.Procedures.ContainsKey(addr)) return true; return false; } public string GetProcedureName(Address addr) { return UserProcedure(addr).Name; } public void SetUserProcedure(Address addr, string decl) { string name; string? CSignature; if (UserSignatureBuilder.IsValidCIdentifier(decl)) { name = decl; CSignature = null; } else { var usb = new UserSignatureBuilder(program); var sProc = usb.ParseFunctionDeclaration(decl); if (sProc is null || sProc.Name is null) throw new ArgumentException( $"Failed to parse procedure declaration: '{decl}'."); name = sProc.Name; CSignature = decl; } program.User.Procedures[addr] = new UserProcedure(addr, name) { CSignature = CSignature, }; if (program.Procedures.TryGetValue(addr, out var proc)) { proc.Name = name; } } public bool GetProcedureDecompileFlag(Address addr) { return UserProcedure(addr).Decompile; } public void SetUserProcedureDecompileFlag( Address addr, bool decompile) { EnsureUserProcedure(addr).Decompile = decompile; } public string? GetProcedureOutputFile(Address addr) { return UserProcedure(addr).OutputFile; } public void SetUserProcedureOutputFile( Address addr, string outputFile) { EnsureUserProcedure(addr).OutputFile = outputFile; } private bool TryConvertAddress( string sAddress, [MaybeNullWhen(false)] out Address addr) { return program.Architecture.TryParseAddress(sAddress, out addr); } private EndianImageReader CreateImageReader(Address addr) { return program.CreateImageReader(program.Architecture, addr); } private IEnumerable ReadData( Func reader, Address startAddr, long length) { var rdr = CreateImageReader(startAddr); var offStart = rdr.Offset; while (rdr.Offset - offStart < length) { yield return reader(rdr); } } private UserProcedure UserProcedure(Address addr) { if (!TryGetUserProcedure(addr, out var userProc)) { if (!TryMakeDefaultUserProcedure(addr, out userProc)) throw new ArgumentException( $"There is no procedure at address: {addr}."); } return userProc; } private UserProcedure EnsureUserProcedure(Address addr) { if (!TryGetUserProcedure(addr, out var userProc)) { if (!TryMakeDefaultUserProcedure(addr, out userProc)) throw new ArgumentException( $"There is no procedure at address: {addr}."); program.User.Procedures[addr] = userProc; } return userProc; } /// /// Gets the user procedure definition at specified address. /// Returns true if there is user procedure definition at specified /// address; otherwise, false /// private bool TryGetUserProcedure( Address addr, [MaybeNullWhen(false)] out UserProcedure userProc) { if (!program.User.Procedures.TryGetValue(addr, out userProc)) { userProc = null; return false; } return true; } /// /// Gets default user procedure based on data discovered by scanner. /// Returns true if there is procedure discovered by scanner at /// specified address; otherwise, false /// private bool TryMakeDefaultUserProcedure( Address addr, [MaybeNullWhen(false)] out UserProcedure userProc) { if (!program.Procedures.TryGetValue(addr, out var proc)) { userProc = null; return false; } userProc = new UserProcedure(addr, proc.Name); return true; } private string NormalizeLineEndings(string s) { // Python line endings differ from C#. // We should normalize them. return s. Replace(Environment.NewLine, "\n"). Replace("\n", Environment.NewLine); } } }