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