Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ELF refactoring with breaking changes #42

Merged
merged 17 commits into from
Oct 15, 2024
Merged
Prev Previous commit
Next Next commit
Improve roundtrip
xoofx committed Oct 14, 2024
commit a406b84d2f018ac559e30479b256191b73493447
142 changes: 86 additions & 56 deletions src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs
Original file line number Diff line number Diff line change
@@ -21,34 +21,65 @@ public class ElfSimpleTests : ElfTestBase
[DynamicData(nameof(GetLinuxBins), DynamicDataSourceType.Method)]
public void TestLinuxFile(string file)
{
if (!OperatingSystem.IsLinux())
if (string.IsNullOrEmpty(file))
{
Assert.Inconclusive("This test can only run on Linux");
Assert.Inconclusive("This test can only run on Linux or on Windows with WSL");
return;
}

using var stream = File.OpenRead(file);
if (!ElfFile.IsElf(stream)) return;

var elf = ElfFile.Read(stream);
stream.Position = 0;
var elfOriginal = ElfFile.Read(stream); // elfOriginal is not written back, so it's layout is not updated

// TODO: check for errors
var originalBuffer = File.ReadAllBytes(file);

//var writer = new StringWriter();
//writer.WriteLine("---------------------------------------------------------------------------------------");
//writer.WriteLine($"{file}");
//elf.Print(writer);
//writer.WriteLine();
var copyStream = new MemoryStream();
elf.Write(copyStream);

for (var i = 0; i < elfOriginal.Content.Count; i++)
{
var content = elf.Content[i];
var contentOriginal = elfOriginal.Content[i];
Assert.AreEqual(contentOriginal.Position, content.Position, $"Invalid position for content {content}");
Assert.AreEqual(contentOriginal.Size, content.Size, $"Invalid size for content {content}");
}

for (var i = 0; i < elfOriginal.Segments.Count; i++)
{
var segment = elf.Segments[i];
var segmentOriginal = elfOriginal.Segments[i];
Assert.AreEqual(segmentOriginal.Position, segment.Position, $"Invalid position for segment {segment}");
Assert.AreEqual(segmentOriginal.Size, segment.Size, $"Invalid size for segment {segment}");
}

ByteArrayAssert.AreEqual(originalBuffer, copyStream.ToArray());
}

public static IEnumerable<object[]> GetLinuxBins()
{
var wslDirectory = @"\\wsl$\Ubuntu\usr\bin";
if (OperatingSystem.IsLinux())
{
foreach (var file in Directory.EnumerateFiles(@"/usr/bin"))
{
yield return new object[] { file };
}
}
else if (OperatingSystem.IsWindows() && Directory.Exists(wslDirectory))
{
foreach (var file in Directory.EnumerateFiles(wslDirectory))
{
var fileInfo = new FileInfo(file);
// Skip symbolic links as loading them will fail
if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == 0)
{
yield return new object[] { file };
}
}
}
else
{
yield return new object[] { string.Empty };
@@ -96,7 +127,7 @@ public void TryReadFailed()
public void SimpleEmptyWithDefaultSections()
{
var elf = new ElfFile(ElfArch.X86_64);
elf.Content.Add(new ElfSectionHeaderTable());
elf.Add(new ElfSectionHeaderTable());
AssertReadElf(elf, "empty_default.elf");
}

@@ -121,9 +152,9 @@ public void SimpleCodeSection()
codeStream.Position = 0;

var codeSection = new ElfStreamSection(ElfSectionSpecialType.Text, codeStream);
elf.Content.Add(codeSection);
elf.Content.Add(new ElfSectionHeaderStringTable());
elf.Content.Add(new ElfSectionHeaderTable());
elf.Add(codeSection);
elf.Add(new ElfSectionHeaderStringTable());
elf.Add(new ElfSectionHeaderTable());

AssertReadElf(elf, "test.elf");
}
@@ -136,16 +167,15 @@ public void TestBss()
var stream = new MemoryStream();
stream.Write(new byte[] { 1, 2, 3, 4 });
stream.Position = 0;
var codeSection = new ElfStreamSection(ElfSectionSpecialType.Text, stream);
elf.Content.Add(codeSection);
var bssSection = new ElfStreamSection(ElfSectionSpecialType.Bss)

var codeSection = elf.Add(new ElfStreamSection(ElfSectionSpecialType.Text, stream));
var bssSection = elf.Add(new ElfStreamSection(ElfSectionSpecialType.Bss)
{
Alignment = 1024
};
elf.Content.Add(bssSection);
FileAlignment = 1024
});

elf.Content.Add(new ElfSectionHeaderStringTable());
elf.Content.Add(new ElfSectionHeaderTable());
elf.Add(new ElfSectionHeaderStringTable());
elf.Add(new ElfSectionHeaderTable());

var diagnostics = new DiagnosticBag();
elf.UpdateLayout(diagnostics);
@@ -166,10 +196,10 @@ public void SimpleCodeSectionAndSymbolSection()
codeStream.Position = 0;

var codeSection = new ElfStreamSection(ElfSectionSpecialType.Text, codeStream);
elf.Content.Add(codeSection);
elf.Add(codeSection);

var stringSection = new ElfStringTable();
elf.Content.Add(stringSection);
elf.Add(stringSection);

var symbolSection = new ElfSymbolTable()
{
@@ -198,9 +228,9 @@ public void SimpleCodeSectionAndSymbolSection()
}
}
};
elf.Content.Add(symbolSection);
elf.Content.Add(new ElfSectionHeaderStringTable());
elf.Content.Add(new ElfSectionHeaderTable());
elf.Add(symbolSection);
elf.Add(new ElfSectionHeaderStringTable());
elf.Add(new ElfSectionHeaderTable());

AssertReadElf(elf, "test2.elf");
}
@@ -216,22 +246,22 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSection()
var codeSection = new ElfStreamSection(ElfSectionSpecialType.Text, codeStream)
{
VirtualAddress = 0x1000,
Alignment = 4096
VirtualAddressAlignment = 4096
};
elf.Content.Add(codeSection);
elf.Add(codeSection);

var dataStream = new MemoryStream();
dataStream.Write(new byte[1024]);

var dataSection = new ElfStreamSection(ElfSectionSpecialType.ReadOnlyData, dataStream)
{
VirtualAddress = 0x2000,
Alignment = 4096
VirtualAddressAlignment = 4096
};
elf.Content.Add(dataSection);
elf.Add(dataSection);

var stringSection = new ElfStringTable();
elf.Content.Add(stringSection);
elf.Add(stringSection);

var symbolSection = new ElfSymbolTable()
{
@@ -260,10 +290,10 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSection()
}
}
};
elf.Content.Add(symbolSection);
elf.Add(symbolSection);

elf.Content.Add(new ElfSectionHeaderStringTable());
elf.Content.Add(new ElfSectionHeaderTable());
elf.Add(new ElfSectionHeaderStringTable());
elf.Add(new ElfSectionHeaderTable());

elf.Segments.Add(new ElfSegment()
{
@@ -274,7 +304,7 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSection()
Flags = ElfSegmentFlagsCore.Readable|ElfSegmentFlagsCore.Executable,
Size = 4096,
SizeInMemory = 4096,
Alignment = 4096,
VirtualAddressAlignment = 4096,
});

elf.Segments.Add(new ElfSegment()
@@ -286,7 +316,7 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSection()
Flags = ElfSegmentFlagsCore.Readable | ElfSegmentFlagsCore.Writable,
Size = 1024,
SizeInMemory = 1024,
Alignment = 4096,
VirtualAddressAlignment = 4096,
});

AssertReadElf(elf, "test3.elf");
@@ -304,22 +334,22 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation()
var codeSection = new ElfStreamSection(ElfSectionSpecialType.Text, codeStream)
{
VirtualAddress = 0x1000,
Alignment = 4096
VirtualAddressAlignment = 4096
};
elf.Content.Add(codeSection);
elf.Add(codeSection);

var dataStream = new MemoryStream();
dataStream.Write(new byte[1024]);

var dataSection = new ElfStreamSection(ElfSectionSpecialType.ReadOnlyData, dataStream)
{
VirtualAddress = 0x2000,
Alignment = 4096
VirtualAddressAlignment = 4096
};
elf.Content.Add(dataSection);
elf.Add(dataSection);

var stringSection = new ElfStringTable();
elf.Content.Add(stringSection);
elf.Add(stringSection);

var symbolSection = new ElfSymbolTable()
{
@@ -348,7 +378,7 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation()
}
}
};
elf.Content.Add(symbolSection);
elf.Add(symbolSection);

elf.Segments.Add(
new ElfSegment()
@@ -360,7 +390,7 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation()
Flags = ElfSegmentFlagsCore.Readable | ElfSegmentFlagsCore.Executable,
Size = 4096,
SizeInMemory = 4096,
Alignment = 4096,
VirtualAddressAlignment = 4096,
}
);

@@ -374,7 +404,7 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation()
Flags = ElfSegmentFlagsCore.Readable | ElfSegmentFlagsCore.Writable,
Size = 1024,
SizeInMemory = 1024,
Alignment = 4096,
VirtualAddressAlignment = 4096,
}
);

@@ -399,10 +429,10 @@ public void SimpleProgramHeaderAndCodeSectionAndSymbolSectionAndRelocation()
}
}
};
elf.Content.Add(relocTable);
elf.Add(relocTable);

elf.Content.Add(new ElfSectionHeaderStringTable());
elf.Content.Add(new ElfSectionHeaderTable());
elf.Add(new ElfSectionHeaderStringTable());
elf.Add(new ElfSectionHeaderTable());

AssertReadElf(elf, "test4.elf");
}
@@ -454,12 +484,12 @@ public void TestAlignedSection()

var codeSection = new ElfStreamSection(ElfSectionSpecialType.Text, codeStream)
{
Alignment = 0x1000,
FileAlignment = 0x1000,
};
elf.Content.Add(codeSection);
elf.Add(codeSection);

elf.Content.Add(new ElfSectionHeaderStringTable());
elf.Content.Add(new ElfSectionHeaderTable());
elf.Add(new ElfSectionHeaderStringTable());
elf.Add(new ElfSectionHeaderTable());

var diagnostics = elf.Verify();
Assert.IsFalse(diagnostics.HasErrors);
@@ -482,20 +512,20 @@ public void TestManySections()
for (int i = 0; i < ushort.MaxValue; i++)
{
var section = new ElfStreamSection(ElfSectionSpecialType.Data) { Name = $".section{i}" };
elf.Content.Add(section);
elf.Add(section);
symbolTable.Entries.Add(new ElfSymbol { Type = ElfSymbolType.Section, SectionLink = section });
}

elf.Content.Add(stringTable);
elf.Content.Add(symbolTable);
elf.Content.Add(new ElfSectionHeaderStringTable());
elf.Content.Add(new ElfSectionHeaderTable());
elf.Add(stringTable);
elf.Add(symbolTable);
elf.Add(new ElfSectionHeaderStringTable());
elf.Add(new ElfSectionHeaderTable());

var diagnostics = elf.Verify();
Assert.IsTrue(diagnostics.HasErrors);
Assert.AreEqual(DiagnosticId.ELF_ERR_MissingSectionHeaderIndices, diagnostics.Messages[0].Id);

elf.Content.Add(new ElfSymbolTableSectionHeaderIndices { Link = symbolTable });
elf.Add(new ElfSymbolTableSectionHeaderIndices { Link = symbolTable });
diagnostics = elf.Verify();
Assert.IsFalse(diagnostics.HasErrors);

3 changes: 2 additions & 1 deletion src/LibObjectFile/Diagnostics/DiagnosticId.cs
Original file line number Diff line number Diff line change
@@ -78,7 +78,8 @@ public enum DiagnosticId
ELF_ERR_SectionHeaderStringTableNotFound = 165,
ELF_ERR_InvalidSectionEntrySize = 166,
ELF_ERR_MissingSectionHeaderTable = 167,

ELF_ERR_MissingSectionHeaderTableSection = 168,

AR_ERR_InvalidMagicLength = 1000,
AR_ERR_MagicNotFound = 1001,
AR_ERR_ExpectingNewLineCharacter = 1002,
4 changes: 2 additions & 2 deletions src/LibObjectFile/Dwarf/DwarfElfContext.cs
Original file line number Diff line number Diff line change
@@ -303,7 +303,7 @@ private ElfStreamSection GetOrCreateDebugSection(string name, bool createSymbol,
var newSection = new ElfStreamSection(ElfSectionType.ProgBits)
{
Name = name,
Alignment = 1,
VirtualAddressAlignment = 1,
Stream = new MemoryStream(),
};

@@ -328,7 +328,7 @@ private ElfRelocationTable GetOrCreateRelocationTable(ElfStreamSection section)
var newSection = new ElfRelocationTable(true)
{
Name = $".rela{section.Name}",
Alignment = (ulong)AddressSize,
VirtualAddressAlignment = (ulong)AddressSize,
Flags = ElfSectionFlags.InfoLink,
Info = section,
Link = _symbolTable,
15 changes: 6 additions & 9 deletions src/LibObjectFile/Elf/ElfContent.cs
Original file line number Diff line number Diff line change
@@ -18,9 +18,14 @@ public abstract class ElfContent : ElfObject
{
protected ElfContent()
{
Alignment = 0;
FileAlignment = 1;
}

/// <summary>
/// Gets or sets the alignment requirement of this content in the file.
/// </summary>
public uint FileAlignment { get; set; }

protected override void ValidateParent(ObjectElement parent)
{
if (!(parent is ElfFile))
@@ -42,14 +47,6 @@ protected void ValidateParent(ObjectElement parent, ElfFileClass fileClass)
}
}

/// <summary>
/// Gets or sets the alignment requirement of this section.
/// </summary>
/// <remarks>
/// An alignment of zero or 1 means that the section or segment has no alignment constraints.
/// </remarks>
public ulong Alignment { get; set; }

/// <summary>
/// Gets the containing <see cref="ElfFile"/>. Might be null if this section or segment
/// does not belong to an existing <see cref="ElfFile"/>.
Loading