1 //          Copyright Yazan Dabain 2014.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module elf;
7 
8 public import elf.sections;
9 
10 import elf.low, elf.low32, elf.low64, elf.meta;
11 
12 import std.mmfile;
13 import std.exception;
14 import std.conv : to;
15 import std.typecons : Nullable;
16 
17 alias enforce = enforce!ELFException;
18 
19 abstract class ELF {
20 	MmFile m_file;
21 
22 	private this(MmFile file) {
23 		this.m_file = file;
24 	}
25 
26 	static ELF fromFile(string filepath) {
27 		MmFile file = new MmFile(filepath);
28 		return ELF.fromFile(file);
29 	}
30 
31 	static ELF fromFile(MmFile file) {
32 		enforce(file.length > 16);
33 		enforce(file[0 .. 4] == ['\x7f', 'E', 'L', 'F']);
34 		bool is32Bit = (file[4] == 1);
35 		bool is64Bit = (file[4] == 2);
36 
37 		if (is32Bit) {
38 			return new ELF32(file);
39 		} else if (is64Bit) {
40 			return new ELF64(file);
41 		} else {
42 			throw new ELFException("invalid elf file class");
43 		}
44 	}
45 
46 	@property ELFHeader header();
47 	ELFSection buildSection(ubyte[] section);
48 
49 	@property auto sections() {
50 		struct Sections {
51 			private size_t m_currentIndex = 0;
52 			ELF m_elf;
53 
54 			this(ELF elf) { this.m_elf = elf; }
55 
56 			@property bool empty() { return m_currentIndex >= m_elf.header.numberOfSectionHeaderEntries; }
57 			@property size_t length() { return m_elf.header.numberOfSectionHeaderEntries - m_currentIndex; }
58 
59 			@property ELFSection front() {
60 				enforce(!empty, "out of bounds exception");
61 				return this[m_currentIndex];
62 			}
63 
64 			void popFront() {
65 				enforce(!empty, "out of bounds exception");
66 				this.m_currentIndex++;
67 			}
68 
69 			@property typeof(this) save() {
70 				return this;
71 			}
72 
73 			ELFSection opIndex(size_t index) {
74 				enforce(index < m_elf.header.numberOfSectionHeaderEntries, "out of bounds exception");
75 				auto sectionStart = m_elf.header.sectionHeaderOffset + index * m_elf.header.sizeOfSectionHeaderEntry;
76 				auto section = m_elf.m_file[sectionStart .. sectionStart + m_elf.header.sizeOfSectionHeaderEntry];
77 				return this.m_elf.buildSection(cast(ubyte[]) section);
78 			}
79 		}
80 		return Sections(this);
81 	}
82 
83 	// linear lookup
84 	Nullable!ELFSection getSection(string name) {
85 		foreach (section; this.sections) {
86 			if (section.name == name) return Nullable!ELFSection(section);
87 		}
88 		return Nullable!ELFSection();
89 	}
90 
91 	StringTable getSectionNamesStringTable() {
92 		ELFSection section = this.sections[this.header.sectionHeaderStringTableIndex];
93 		return StringTable(section);
94 	}
95 
96 	Nullable!StringTable getSymbolsStringTable() {
97 		Nullable!ELFSection section = this.getSection(".strtab");
98 		if (section.isNull) return Nullable!StringTable();
99 		else return Nullable!StringTable(StringTable(section.get()));
100 	}
101 }
102 
103 final class ELF64 : ELF {
104 	ELFHeader64 m_header;
105 
106 	this(MmFile file) {
107 		super(file);
108 
109 		ELFHeader64L headerData = *(cast(ELFHeader64L*) file[0 .. ELFHeader64L.sizeof].ptr);
110 		this.m_header = new ELFHeader64(headerData);
111 	}
112 
113 	override @property ELFHeader header() {
114 		return this.m_header;
115 	}
116 
117 	override ELFSection64 buildSection(ubyte[] sectionData) {
118 		enforce(sectionData.length == ELFSection64L.sizeof);
119 		ELFSection64L sectionRep = *(cast(ELFSection64L*) sectionData.ptr);
120 		ELFSection64 section = new ELFSection64(this, sectionRep);
121 		section.m_elf = this;
122 		return section;
123 	}
124 }
125 
126 final class ELF32 : ELF {
127 	ELFHeader32 m_header;
128 
129 	this(MmFile file) {
130 		super(file);
131 
132 		ELFHeader32L headerData = *(cast(ELFHeader32L*) file[0 .. ELFHeader32L.sizeof].ptr);
133 		this.m_header = new ELFHeader32(headerData);
134 	}
135 
136 	override @property ELFHeader header() {
137 		return this.m_header;
138 	}
139 
140 	override ELFSection32 buildSection(ubyte[] sectionData) {
141 		enforce(sectionData.length == ELFSection32L.sizeof);
142 		ELFSection32L sectionRep = *(cast(ELFSection32L*) sectionData.ptr);
143 		ELFSection32 section = new ELFSection32(this, sectionRep);
144 		return section;
145 	}
146 }
147 
148 abstract class ELFHeader {
149 	@property:
150 	@ReadFrom("ident") Identifier identifier();
151 	@ReadFrom("type") ObjectFileType objectFileType();
152 	@ReadFrom("machine") TargetISA machineISA();
153 	@ReadFrom("version_") ELF_Word version_();
154 	@ReadFrom("entry") ELF_Addr entryPoint();
155 	@ReadFrom("phoff") ELF_Off programHeaderOffset();
156 	@ReadFrom("shoff") ELF_Off sectionHeaderOffset();
157 	@ReadFrom("phentsize") ELF_Half sizeOfProgramHeaderEntry();
158 	@ReadFrom("phnum") ELF_Half numberOfProgramHeaderEntries();
159 	@ReadFrom("shentsize") ELF_Half sizeOfSectionHeaderEntry();
160 	@ReadFrom("shnum") ELF_Half numberOfSectionHeaderEntries();
161 	@ReadFrom("shstrndx") ELF_Half sectionHeaderStringTableIndex();
162 }
163 
164 final class ELFHeader32 : ELFHeader {
165 	private ELFHeader32L m_data;
166 	mixin(generateVirtualReads!(ELFHeader, "m_data"));
167 
168 	this(ELFHeader32L data) {
169 		this.m_data = data;
170 	}
171 }
172 
173 final class ELFHeader64 : ELFHeader {
174 	private ELFHeader64L m_data;
175 	mixin(generateVirtualReads!(ELFHeader, "m_data"));
176 
177 	this(ELFHeader64L data) {
178 		this.m_data = data;
179 	}
180 }
181 
182 abstract class ELFSection {
183 	package ELF m_elf;
184 
185 	@property:
186 	@ReadFrom("name") ELF_Word nameIndex();
187 	@ReadFrom("type") SectionType type();
188 	@ReadFrom("flags") SectionFlag flags();
189 	@ReadFrom("address") ELF_Addr address();
190 	@ReadFrom("offset") ELF_Off offset();
191 	@ReadFrom("size") ELF_XWord size();
192 	@ReadFrom("link") ELF_Word link();
193 	@ReadFrom("info") ELF_Word info();
194 	@ReadFrom("addralign") ELF_XWord addrAlign();
195 	@ReadFrom("entsize") ELF_XWord entrySize();
196 
197 	ubyte bits();
198 
199 	auto name() {
200 		return m_elf.getSectionNamesStringTable().getStringAt(this.nameIndex());
201 	}
202 
203 	auto contents() {
204 		return cast(ubyte[]) m_elf.m_file[offset() .. offset() + size()];
205 	}
206 }
207 
208 final class ELFSection32 : ELFSection {
209 	private ELFSection32L m_data;
210 	mixin(generateVirtualReads!(ELFSection, "m_data"));
211 
212 	this(ELF32 elf, ELFSection32L data) {
213 		this.m_elf = elf;
214 		this.m_data = data;
215 	}
216 
217 	override @property ubyte bits() {
218 		return 32;
219 	}
220 }
221 
222 final class ELFSection64 : ELFSection {
223 	private ELFSection64L m_data;
224 	mixin(generateVirtualReads!(ELFSection, "m_data"));
225 
226 	this(ELF64 elf, ELFSection64L data) {
227 		this.m_elf = elf;
228 		this.m_data = data;
229 	}
230 
231 	override @property ubyte bits() {
232 		return 64;
233 	}
234 }
235 
236 class ELFException : Exception {
237 	this(string msg, string file = __FILE__, size_t line = __LINE__) {
238 		super(msg, file, line);
239 	}
240 }