Skip to content

Commit

Permalink
NE: Document relocations, improve dump
Browse files Browse the repository at this point in the history
  • Loading branch information
BinaryMelodies committed Feb 3, 2025
1 parent 2d2cd1a commit 1e4e8ca
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 30 deletions.
74 changes: 56 additions & 18 deletions src/format/neexe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -114,29 +114,30 @@ void NEFormat::Segment::Dump(Dumper::Dumper& dump, unsigned index, bool isos2)
->AddBitField(0, 2, Dumper::ChoiceDisplay::Make(target_descriptions), false)
->AddBitField(2, 1, Dumper::ChoiceDisplay::Make("additive"), true),
offset_t(relocation.flags));
switch(relocation.flags & 3)
switch(relocation.flags & Relocation::TargetTypeMask)
{
case Relocation::Internal:
if(relocation.segment == 0xFF)
if((relocation.module & 0xFF) == 0xFF)
{
// TODO: get name
rel_entry.AddField("Entry", Dumper::HexDisplay::Make(4), offset_t(relocation.target));
rel_entry.AddField("Location", Dumper::SectionedDisplay<offset_t>::Make(Dumper::HexDisplay::Make(4)), offset_t(relocation.actual_segment), offset_t(relocation.actual_offset));
rel_entry.AddOptionalField("Exported name", Dumper::StringDisplay::Make(), relocation.import_name);
}
else
{
rel_entry.AddField("Location", Dumper::SectionedDisplay<offset_t>::Make(Dumper::HexDisplay::Make(4)), offset_t(relocation.segment), offset_t(relocation.target));
rel_entry.AddField("Location", Dumper::SectionedDisplay<offset_t>::Make(Dumper::HexDisplay::Make(4)), offset_t(relocation.module & 0xFF), offset_t(relocation.target));
}
break;
case Relocation::ImportOrdinal:
// TODO: get module name
rel_entry.AddField("Module", Dumper::DecDisplay::Make(), offset_t(relocation.segment));
rel_entry.AddField("Module number", Dumper::DecDisplay::Make(), offset_t(relocation.module));
rel_entry.AddOptionalField("Module", Dumper::StringDisplay::Make(), relocation.module_name);
rel_entry.AddField("Ordinal", Dumper::HexDisplay::Make(4), offset_t(relocation.target));
break;
case Relocation::ImportName:
// TODO: get module name
rel_entry.AddField("Module", Dumper::DecDisplay::Make(), offset_t(relocation.segment));
// TODO: get procedure name
rel_entry.AddField("Module number", Dumper::DecDisplay::Make(), offset_t(relocation.module));
rel_entry.AddOptionalField("Module", Dumper::StringDisplay::Make(), relocation.module_name);
rel_entry.AddField("Name offset", Dumper::HexDisplay::Make(4), offset_t(relocation.target));
rel_entry.AddOptionalField("Name", Dumper::StringDisplay::Make(), relocation.import_name);
break;
case Relocation::OSFixup:
rel_entry.AddField("Fixup type", Dumper::HexDisplay::Make(4), offset_t(relocation.module));
Expand All @@ -152,7 +153,6 @@ void NEFormat::Segment::Dump(Dumper::Dumper& dump, unsigned index, bool isos2)
}
j++;
}
// TODO: finish
i++;
}
}
Expand Down Expand Up @@ -341,9 +341,11 @@ bool NEFormat::IsOS2() const

void NEFormat::ReadFile(Linker::Reader& rd)
{
/* TODO: test reader */
rd.endiantype = ::LittleEndian;
file_offset = rd.Tell();

/* New header */

rd.ReadData(signature);
linker_version.major = rd.ReadUnsigned(1);
linker_version.minor = rd.ReadUnsigned(1);
Expand Down Expand Up @@ -404,6 +406,7 @@ void NEFormat::ReadFile(Linker::Reader& rd)
file_size = rd.Tell();

uint32_t i;

/* Segment table */
rd.Seek(segment_table_offset);
segments.clear();
Expand Down Expand Up @@ -553,6 +556,8 @@ void NEFormat::ReadFile(Linker::Reader& rd)

file_size = std::max(file_size, rd.Tell());

// Load module names for convenience

for(auto& module : module_references)
{
rd.Seek(imported_names_table_offset + module.name_offset);
Expand Down Expand Up @@ -632,9 +637,7 @@ void NEFormat::ReadFile(Linker::Reader& rd)
uint16_t new_offset = segment.image->GetByte(offset) | (segment.image->GetByte(offset + 1) << 8);
if(new_offset == 0xFFFF)
break;
Linker::Debug << "Debug: chained relocation from offset " << std::hex << offset << " to " << new_offset << std::endl;
//relocation.offset = new_offset;
//segment.relocations.emplace_back(relocation);
//Linker::Debug << "Debug: chained relocation from offset " << std::hex << offset << " to " << new_offset << std::endl;
relocation.offsets.push_back(new_offset);
offset = new_offset;
}
Expand Down Expand Up @@ -674,6 +677,8 @@ void NEFormat::ReadFile(Linker::Reader& rd)
}
}

/* Load entry descriptions for readability */

for(auto& name : resident_names)
{
if(0 < name.ordinal && name.ordinal <= entries.size())
Expand All @@ -691,6 +696,39 @@ void NEFormat::ReadFile(Linker::Reader& rd)
entries[name.ordinal - 1].entry_name = name.name;
}
}

/* Load relocation descriptions for readability */
for(auto& segment : segments)
{
for(auto& relocation : segment.relocations)
{
if((relocation.flags & Segment::Relocation::TargetTypeMask) == Segment::Relocation::ImportOrdinal
|| (relocation.flags & Segment::Relocation::TargetTypeMask) == Segment::Relocation::ImportName)
{
if(0 < relocation.module && relocation.module <= module_references.size())
{
relocation.module_name = module_references[relocation.module - 1].name;
}
}

if((relocation.flags & Segment::Relocation::TargetTypeMask) == Segment::Relocation::ImportName)
{
rd.Seek(imported_names_table_offset + relocation.target);
uint8_t length = rd.ReadUnsigned(1);
relocation.import_name = rd.ReadData(length);
}

if((relocation.flags & Segment::Relocation::TargetTypeMask) == Segment::Relocation::Internal && (relocation.module & 0xFF) == 0xFF)
{
if(0 < relocation.target && relocation.target <= entries.size())
{
relocation.actual_segment = entries[relocation.target - 1].segment;
relocation.actual_offset = entries[relocation.target - 1].offset;
relocation.import_name = entries[relocation.target - 1].entry_name;
}
}
}
}
}

offset_t NEFormat::ImageSize()
Expand Down Expand Up @@ -1050,7 +1088,7 @@ void NEFormat::Dump(Dumper::Dumper& dump)
for(auto& entry : entries)
{
Dumper::Entry call_entry("Entry", i + 1, 0 /* TODO */, 8);
call_entry.AddField("Ordinal", Dumper::HexDisplay::Make(4), offset_t(i));
call_entry.AddField("Ordinal", Dumper::HexDisplay::Make(4), offset_t(i + 1));
call_entry.AddField("Type", Dumper::ChoiceDisplay::Make(type_descriptions), offset_t(entry.type));
if(entry.type != Entry::Unused)
{
Expand Down Expand Up @@ -1617,7 +1655,7 @@ void NEFormat::ProcessModule(Linker::Module& module)
Segment::Relocation(type, Segment::Relocation::ImportName,
position.address, FetchModule(library) + 1, FetchImportedName(MakeProcedureName(name)))
);
rel.WriteWord(0xFFFFFFFF);
rel.WriteWord(0xFFFFFFFF); // gets truncated for shorter relocations
/* TODO: if not additive? */
continue;
}
Expand All @@ -1627,7 +1665,7 @@ void NEFormat::ProcessModule(Linker::Module& module)
Segment::Relocation(type, Segment::Relocation::ImportOrdinal,
position.address, FetchModule(library) + 1, ordinal)
);
rel.WriteWord(0xFFFFFFFF);
rel.WriteWord(0xFFFFFFFF); // gets truncated for shorter relocations
/* TODO: if not additive? */
continue;
}
Expand Down Expand Up @@ -1746,7 +1784,7 @@ void NEFormat::CalculateValues()
resource_count += rtype.resources.size();
}

resident_name_table_offset = resource_table_offset + 0; /* TODO: resource table */
resident_name_table_offset = resource_table_offset + 0; /* TODO: store resource table */
current_offset = resident_name_table_offset + 1;
for(Name& name : resident_names)
{
Expand Down
70 changes: 58 additions & 12 deletions src/format/neexe.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,31 +82,52 @@ namespace Microsoft
class Relocation
{
public:
/** @brief The type of the field that needs to be updated in the binary image */
enum source_type
{
/** @brief Lower 8 bits of value */
Offset8 = 0,
/** @brief A 16-bit segment selector value (or paragraph in real mode) */
Selector16 = 2,
/** @brief A 32-bit far pointer, 16-bit segment and 16-bit offset value */
Pointer32 = 3,
/** @brief A 16-bit offset within its preferred segment */
Offset16 = 5,
/** @brief A 48-bit far pointer, 16-bit segment and 32-bit offset value */
Pointer48 = 11,
/** @brief A 32-bit offset within its preferred segment */
Offset32 = 13,
};
/** @brief The type of relocation */
source_type type = source_type(0);
/** @brief Encodes what the type of the target is for this relocation */
enum flag_type
{
/** @brief Mask to get the type of the target */
TargetTypeMask = 3,
/** @brief An internal segment:offset, or if segment is 0xFF, offset is the entry number */
Internal = 0,
/** @brief An imported module:ordinal where module is the module number */
ImportOrdinal = 1,
/** @brief An imported module:offset where module is the module number and offset is an offset to the name */
ImportName = 2,
/** @brief This is actually an 80x87 instruction sequence that needs to be fixed up for 80x87 emulation */
OSFixup = 3,
/** @brief Whether the relocation has an added in the image or it is the first element in a relocation chain
*
* If set, the value stored in the image has to be added to the target value.
* If cleared, the value stored in the image is part of a relocation chain, providing the offset to the following relocation.
* The chain is closed if the value 0xFFFF is present.
* All other properties of the relocation are the same as the first member of the chain.
*/
Additive = 4,
};
/** @brief The type of target */
flag_type flags = flag_type(0);
/** @brief The offset to the relocation, or if the relocation is chained, a list of offsets (this is not possible for Offset8) */
std::vector<uint16_t> offsets;
union
{
uint16_t module;
uint8_t segment;
};
/** @brief This field is a segment (1 based) or module (1 based) or 80x87 instruction reference, depending on the flags */
uint16_t module = 0;
enum
{
FIARQQ = 1, FJARQQ = 1,
Expand All @@ -116,11 +137,19 @@ namespace Microsoft
FIDRQQ = 5,
FIWRQQ = 6,
};
/** @brief This field is an offset or ordinal, depending on the flags */
uint16_t target = 0;
Relocation()
: module(0)
{
}

/** @brief Convenience field that stores the module name */
std::string module_name;
/** @brief Convenience field that stores the imported procedure name, if imported by name, also the name for an exported entry */
std::string import_name;
/** @brief Convenience field that stores the actual segment for an entry */
uint8_t actual_segment = 0;
/** @brief Convenience field that stores the actual offset for an entry */
uint16_t actual_offset = 0;

Relocation() = default;

Relocation(unsigned type, unsigned flags, uint16_t offset, uint16_t module, uint16_t target)
: type(source_type(type)), flags(flag_type(flags)), module(module), target(target)
Expand Down Expand Up @@ -194,41 +223,56 @@ namespace Microsoft
class Entry
{
public:
/** @brief This field represents the type of the entry */
enum entry_type
{
/** @brief The entry is unused, the other fields are meaningless */
Unused,
/** @brief The entry references a fixed segment */
Fixed,
/** @brief The entry references a movable segment */
Movable,
};
/** @brief The type of entry, based on the first byte in an entry bundle */
entry_type type = Unused;
/** @brief The number of the segment, 1 based */
uint8_t segment = 0;
/** @brief Flags present in an entry */
enum flag_type : uint8_t
{
/** @brief Set if the entry is exported */
Exported = 1,
/** @brief Set if the data segment used by the entry is global, used in SINGLEDATA modules (libraries) */
SharedData = 2,
};
flag_type flags = flag_type(0);
/** @brief Offset within the segment */
uint16_t offset = 0;

// informational purposes
enum export_type
{
/** @brief The entry is not exported, the Exported bit is not set */
NotExported,
/** @brief The entry is exported by name, it is referenced in the resident name table */
ExportByName,
/** @brief The entry is exported by ordinal, it is referenced in the nonresident name table */
ExportByOrdinal,
};
/** @brief Whether the entry is exported. This is not actually stored in the entry table and its value is ignored during program generation */
export_type export_state = NotExported;
/** @brief The name of an exported entry. This is not actually stored in the entry table and its value is ignored during program generation */
std::string entry_name;

enum
{
WordCountShift = 3,

/** @brief Byte code for interrupt call that must be placed in the entry field */
INT_3Fh = 0x3FCD,
};

Entry()
{
}
Entry() = default;

Entry(unsigned type, uint8_t segment, unsigned flags, uint16_t offset)
: type(entry_type(type)), segment(segment), flags(flag_type(flags)), offset(offset)
Expand All @@ -246,7 +290,9 @@ namespace Microsoft
class ModuleReference
{
public:
/** @brief Offset to the module name within the imported names table */
uint16_t name_offset = 0;
/** @brief The name of the module, not actually used during program generation */
std::string name;

ModuleReference(uint16_t name_offset, std::string name = "")
Expand Down

0 comments on commit 1e4e8ca

Please sign in to comment.