// Apply relocations to a segment. bool ApplyRelocations(ImageReader rdr, int cRelocations, NeSegment seg) { string module = ""; Address address = null; NeRelocationEntry rep = null; for (int i = 0; i < cRelocations; i++) { rep = new NeRelocationEntry { address_type = rdr.ReadByte(), relocation_type = rdr.ReadByte(), offset = rdr.ReadLeUInt16(), target1 = rdr.ReadLeUInt16(), target2 = rdr.ReadLeUInt16(), }; // Get the target address corresponding to this entry. // If additive, there is no target chain list. Instead, add source // and target. bool additive = (rep.relocation_type & NE_RELFLAG_ADDITIVE) != 0; Tuple <Address, ImportReference> impRef; uint lp; switch (rep.relocation_type & 3) { case NE_RELTYPE_ORDINAL: module = moduleNames[rep.target1 - 1]; // Synthesize an import lp = ((uint)rep.target1 << 16) | rep.target2; if (importStubs.TryGetValue(lp, out impRef)) { address = impRef.Item1; } else { address = addrImportStubs; importStubs.Add(lp, new Tuple <Address, ImportReference>( address, new OrdinalImportReference(address, module, rep.target2))); addrImportStubs += 8; } break; case NE_RELTYPE_NAME: module = moduleNames[rep.target1 - 1]; uint offName = lfaNew + this.offImportedNamesTable + rep.target2; var nameRdr = new LeImageReader(RawImage, offName); byte fnNameLength = nameRdr.ReadByte(); var abFnName = nameRdr.ReadBytes(fnNameLength); lp = ((uint)rep.target1 << 16) | rep.target2; if (importStubs.TryGetValue(lp, out impRef)) { address = impRef.Item1; } else { address = addrImportStubs; string fnName = Encoding.ASCII.GetString(abFnName); importStubs.Add(lp, new Tuple <Address, ImportReference>( address, new NamedImportReference(address, module, fnName))); } break; case NE_RELTYPE_INTERNAL: if ((rep.target1 & 0xff) == 0xff) { throw new NotImplementedException(); } else { address = segments[rep.target1 - 1].Address + rep.target2; } Debug.Print("{0}: {1:X4}:{2:X4} {3}", i + 1, address.Selector.Value, address.Selector.Value, ""); break; case NE_RELTYPE_OSFIXUP: /* Relocation type 7: * * These appear to be used as fixups for the Windows * floating point emulator. Let's just ignore them and * try to use the hardware floating point. Linux should * successfully emulate the coprocessor if it doesn't * exist. */ /* * TRACE("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n", * i + 1, rep->relocation_type, rep->offset, * rep->target1, rep->target2, * NE_GetRelocAddrName( rep->address_type, additive ) ); */ continue; } ushort offset = rep.offset; // Apparently, high bit of address_type is sometimes set; // we ignore it for now. if (rep.address_type > NE_RADDR_OFFSET32) { diags.Error( string.Format( "Module {0}: unknown relocation address type {1:X2}. Please report", module, rep.address_type)); return(false); } if (additive) { var sp = seg.Address + offset; Debug.Print(" {0:X4}:{0:X4}", offset, offset); byte b; ushort w; switch (rep.address_type & 0x7f) { case NE_RADDR_LOWBYTE: b = mem.ReadByte(sp); mem.WriteByte(sp, (byte)(b + address.Offset)); break; case NE_RADDR_OFFSET16: w = mem.ReadLeUInt16(sp); mem.WriteLeUInt16(sp, (ushort)(w + address.Offset)); break; case NE_RADDR_POINTER32: w = mem.ReadLeUInt16(sp); mem.WriteLeUInt16(sp, (ushort)(w + address.Offset)); mem.WriteLeUInt16(sp + 2, address.Selector.Value); break; case NE_RADDR_SELECTOR: // Borland creates additive records with offset zero. Strange, but OK. w = mem.ReadLeUInt16(sp); if (w != 0) { diags.Error(string.Format("Additive selector to {0:X4}. Please report.", w)); } else { mem.WriteLeUInt16(sp, address.Selector.Value); } break; default: goto unknown; } } else { // Non-additive fixup. do { var sp = seg.Address + offset; ushort next_offset = mem.ReadLeUInt16(sp); Debug.Print(" {0:X4}:{0:X4}", offset, next_offset); switch (rep.address_type & 0x7f) { case NE_RADDR_LOWBYTE: mem.WriteByte(sp, (byte)address.Offset); break; case NE_RADDR_OFFSET16: mem.WriteLeUInt16(sp, (ushort)address.Offset); break; case NE_RADDR_POINTER32: mem.WriteLeUInt16(sp, (ushort)address.Offset); mem.WriteLeUInt16(sp + 2, address.Selector.Value); break; case NE_RADDR_SELECTOR: mem.WriteLeUInt16(sp, address.Selector.Value); break; default: goto unknown; } if (next_offset == offset) { break; // avoid infinite loop } if (next_offset >= seg.Alloc) { break; } offset = next_offset; } while (offset != 0xffff); } } return(true); unknown: var svc = Services.RequireService <IDiagnosticsService>(); svc.Warn("{0}: unknown ADDR TYPE {1}, " + "TYPE {2}, OFFSET {3:X4}, TARGET {4:X4} {5:X4}", seg.Address.Selector, rep.address_type, rep.relocation_type, rep.offset, rep.target1, rep.target2); return(false); }