metasm 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. metadata +255 -0
@@ -0,0 +1,1375 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+
7
+ require 'metasm/encode'
8
+ require 'metasm/exe_format/elf' unless defined? Metasm::ELF
9
+
10
+ module Metasm
11
+ class ELF
12
+ class Header
13
+ def set_default_values elf
14
+ @magic ||= "\x7fELF"
15
+ @e_class ||= elf.bitsize.to_s
16
+ @data ||= (elf.endianness == :big ? 'MSB' : 'LSB')
17
+ @version ||= 'CURRENT'
18
+ @i_version ||= @version
19
+ @entry ||= 0
20
+ @phoff ||= elf.segments.empty? ? 0 : elf.new_label('phdr')
21
+ @shoff ||= elf.sections.length <= 1 ? 0 : elf.new_label('shdr')
22
+ @flags ||= []
23
+ @ehsize ||= Header.size(elf)
24
+ @phentsize ||= Segment.size(elf)
25
+ @phnum ||= elf.segments.length
26
+ @shentsize ||= Section.size(elf)
27
+ @shnum ||= elf.sections.length
28
+
29
+ super(elf)
30
+ end
31
+ end
32
+
33
+ class Section
34
+ def set_default_values elf
35
+ make_name_p elf if name and @name != ''
36
+ @flags ||= []
37
+ @addr ||= (encoded and @flags.include?('ALLOC')) ? elf.label_at(@encoded, 0) : 0
38
+ @offset ||= encoded ? elf.new_label('section_offset') : 0
39
+ @size ||= encoded ? @encoded.length : 0
40
+ @addralign ||= entsize || 0
41
+ @entsize ||= @addralign
42
+ @link = elf.sections.index(@link) if link.kind_of? Section
43
+ @info = elf.sections.index(@info) if info.kind_of? Section
44
+ super(elf)
45
+ end
46
+
47
+ # defines the @name_p field from @name and elf.section[elf.header.shstrndx]
48
+ # creates .shstrtab if needed
49
+ def make_name_p elf
50
+ return 0 if not name or @name == ''
51
+ if elf.header.shstrndx.to_i == 0
52
+ sn = Section.new
53
+ sn.name = '.shstrtab'
54
+ sn.type = 'STRTAB'
55
+ sn.flags = []
56
+ sn.addralign = 1
57
+ sn.encoded = EncodedData.new << 0
58
+ elf.header.shstrndx = elf.sections.length
59
+ elf.sections << sn
60
+ end
61
+ sne = elf.sections[elf.header.shstrndx].encoded
62
+ return if name_p and sne.data[@name_p, @name.length+1] == @name+0.chr
63
+ return if @name_p = sne.data.index(@name+0.chr)
64
+ @name_p = sne.virtsize
65
+ sne << @name << 0
66
+ end
67
+ end
68
+
69
+ class Segment
70
+ def set_default_values elf
71
+ if encoded
72
+ @offset ||= elf.new_label('segment_offset')
73
+ @vaddr ||= elf.label_at(@encoded, 0)
74
+ @filesz ||= @encoded.rawsize
75
+ @memsz ||= @encoded.virtsize
76
+ end
77
+ @paddr ||= @vaddr if vaddr
78
+
79
+ super(elf)
80
+ end
81
+ end
82
+
83
+ class Symbol
84
+ def set_default_values(elf, strtab)
85
+ make_name_p elf, strtab if strtab and name and @name != ''
86
+ super(elf)
87
+ end
88
+
89
+ # sets the value of @name_p, appends @name to strtab if needed
90
+ def make_name_p(elf, strtab)
91
+ s = strtab.kind_of?(EncodedData) ? strtab.data : strtab
92
+ return if name_p and s[@name_p, @name.length+1] == @name+0.chr
93
+ return if @name_p = s.index(@name+0.chr)
94
+ @name_p = strtab.length
95
+ strtab << @name << 0
96
+ end
97
+ end
98
+
99
+
100
+ def encode_byte(w) Expression[w].encode(:u8, @endianness, (caller if $DEBUG)) end
101
+ def encode_half(w) Expression[w].encode(:u16, @endianness, (caller if $DEBUG)) end
102
+ def encode_word(w) Expression[w].encode(:u32, @endianness, (caller if $DEBUG)) end
103
+ def encode_sword(w) Expression[w].encode(:i32, @endianness, (caller if $DEBUG)) end
104
+ def encode_xword(w) Expression[w].encode((@bitsize == 32 ? :u32 : :u64), @endianness, (caller if $DEBUG)) end
105
+ def encode_sxword(w) Expression[w].encode((@bitsize == 32 ? :i32 : :i64), @endianness, (caller if $DEBUG)) end
106
+ alias encode_addr encode_xword
107
+ alias encode_off encode_xword
108
+
109
+ # checks a section's data has not grown beyond s.size, if so undefs addr/offset
110
+ def encode_check_section_size(s)
111
+ if s.size and s.encoded.virtsize < s.size
112
+ puts "W: Elf: preexisting section #{s} has grown, relocating" if $VERBOSE
113
+ s.addr = s.offset = nil
114
+ s.size = s.encoded.virtsize
115
+ end
116
+ end
117
+
118
+ # reorders self.symbols according to their gnu_hash
119
+ def encode_reorder_symbols
120
+ gnu_hash_bucket_length = 42 # TODO
121
+ @symbols[1..-1] = @symbols[1..-1].sort_by { |s|
122
+ if s.bind != 'GLOBAL'
123
+ -2
124
+ elsif s.shndx == 'UNDEF' or not s.name
125
+ -1
126
+ else
127
+ ELF.gnu_hash_symbol_name(s.name) % gnu_hash_bucket_length
128
+ end
129
+ }
130
+ end
131
+
132
+ # sorted insert of a new section to self.sections according to its permission (for segment merging)
133
+ def encode_add_section s
134
+ # order: r rx rw noalloc
135
+ rank = lambda { |sec|
136
+ f = sec.flags
137
+ sec.type == 'NULL' ? -2 : sec.addr ? -1 :
138
+ f.include?('ALLOC') ? !f.include?('WRITE') ? !f.include?('EXECINSTR') ? 0 : 1 : 2 : 3
139
+ }
140
+ srank = rank[s]
141
+ nexts = @sections.find { |sec| rank[sec] > srank } # find section with rank superior
142
+ nexts = nexts ? @sections.index(nexts) : -1 # if none, last
143
+ @sections.insert(nexts, s) # insert section
144
+ end
145
+
146
+ # encodes the GNU_HASH table
147
+ # TODO
148
+ def encode_gnu_hash
149
+ return if true
150
+
151
+ sortedsyms = @symbols.find_all { |s| s.bind == 'GLOBAL' and s.shndx != 'UNDEF' and s.name }
152
+ bucket = Array.new(42)
153
+
154
+ if not gnu_hash = @sections.find { |s| s.type == 'GNU_HASH' }
155
+ gnu_hash = Section.new
156
+ gnu_hash.name = '.gnu.hash'
157
+ gnu_hash.type = 'GNU_HASH'
158
+ gnu_hash.flags = ['ALLOC']
159
+ gnu_hash.entsize = gnu_hash.addralign = 4
160
+ encode_add_section gnu_hash
161
+ end
162
+ gnu_hash.encoded = EncodedData.new
163
+
164
+ # "bloomfilter[N] has bit B cleared if there is no M (M > symndx) which satisfies (C = @header.class)
165
+ # ((gnu_hash(sym[M].name) / C) % maskwords) == N &&
166
+ # ((gnu_hash(sym[M].name) % C) == B ||
167
+ # ((gnu_hash(sym[M].name) >> shift2) % C) == B"
168
+ # bloomfilter may be [~0]
169
+ bloomfilter = []
170
+
171
+ # bucket[N] contains the lowest M for which
172
+ # gnu_hash(sym[M]) % nbuckets == N
173
+ # or 0 if none
174
+ bucket = []
175
+
176
+ gnu_hash.encoded <<
177
+ encode_word(bucket.length) <<
178
+ encode_word(@symbols.length - sortedsyms.length) <<
179
+ encode_word(bloomfilter.length) <<
180
+ encode_word(shift2)
181
+ bloomfilter.each { |bf| gnu_hash.encoded << encode_xword(bf) }
182
+ bucket.each { |bk| gnu_hash.encoded << encode_word(bk) }
183
+ sortedsyms.each { |s|
184
+ # (gnu_hash(sym[N].name) & ~1) | (N == dynsymcount-1 || (gnu_hash(sym[N].name) % nbucket) != (gnu_hash(sym[N+1].name) % nbucket))
185
+ # that's the hash, with its lower bit replaced by the bool [1 if i am the last sym having my hash as hash]
186
+ val = 28
187
+ gnu_hash.encoded << encode_word(val)
188
+ }
189
+
190
+ @tag['GNU_HASH'] = label_at(gnu_hash.encoded, 0)
191
+
192
+ encode_check_section_size gnu_hash
193
+
194
+ gnu_hash
195
+ end
196
+
197
+ # encodes the symbol dynamic hash table in the .hash section, updates the HASH tag
198
+ def encode_hash
199
+ if not hash = @sections.find { |s| s.type == 'HASH' }
200
+ hash = Section.new
201
+ hash.name = '.hash'
202
+ hash.type = 'HASH'
203
+ hash.flags = ['ALLOC']
204
+ hash.entsize = hash.addralign = 4
205
+ encode_add_section hash
206
+ end
207
+ hash.encoded = EncodedData.new
208
+
209
+ # to find a symbol from its name :
210
+ # 1: idx = hash(name)
211
+ # 2: idx = bucket[idx % bucket.size]
212
+ # 3: if idx == 0: return notfound
213
+ # 4: if dynsym[idx].name == name: return found
214
+ # 5: idx = chain[idx] ; goto 3
215
+ bucket = Array.new(@symbols.length/4+1, 0)
216
+ chain = Array.new(@symbols.length, 0)
217
+ @symbols.each_with_index { |s, i|
218
+ next if s.bind == 'LOCAL' or not s.name or s.shndx == 'UNDEF'
219
+ hash_mod = ELF.hash_symbol_name(s.name) % bucket.length
220
+ chain[i] = bucket[hash_mod]
221
+ bucket[hash_mod] = i
222
+ }
223
+
224
+ hash.encoded << encode_word(bucket.length) << encode_word(chain.length)
225
+
226
+ bucket.each { |b| hash.encoded << encode_word(b) }
227
+ chain.each { |c| hash.encoded << encode_word(c) }
228
+
229
+ @tag['HASH'] = label_at(hash.encoded, 0)
230
+
231
+ encode_check_section_size hash
232
+
233
+ hash
234
+ end
235
+
236
+ # encodes the symbol table
237
+ # should have a stable self.sections array (only append allowed after this step)
238
+ def encode_segments_symbols(strtab)
239
+ if not dynsym = @sections.find { |s| s.type == 'DYNSYM' }
240
+ dynsym = Section.new
241
+ dynsym.name = '.dynsym'
242
+ dynsym.type = 'DYNSYM'
243
+ dynsym.entsize = Symbol.size(self)
244
+ dynsym.addralign = 4
245
+ dynsym.flags = ['ALLOC']
246
+ dynsym.info = @symbols[1..-1].find_all { |s| s.bind == 'LOCAL' }.length + 1
247
+ dynsym.link = strtab
248
+ encode_add_section dynsym
249
+ end
250
+ dynsym.encoded = EncodedData.new
251
+ @symbols.each { |s| dynsym.encoded << s.encode(self, strtab.encoded) } # needs all section indexes, as will be in the final section header
252
+
253
+ @tag['SYMTAB'] = label_at(dynsym.encoded, 0)
254
+ @tag['SYMENT'] = Symbol.size(self)
255
+
256
+ encode_check_section_size dynsym
257
+
258
+ dynsym
259
+ end
260
+
261
+ # encodes the relocation tables
262
+ # needs a complete self.symbols array
263
+ def encode_segments_relocs
264
+ return if not @relocations
265
+
266
+ arch_preencode_reloc_func = "arch_#{@header.machine.downcase}_preencode_reloc"
267
+ send arch_preencode_reloc_func if respond_to? arch_preencode_reloc_func
268
+
269
+ list = @relocations.find_all { |r| r.type == 'JMP_SLOT' }
270
+ if not list.empty? or @relocations.empty?
271
+ if list.find { |r| r.addend }
272
+ stype = 'RELA'
273
+ sname = '.rela.plt'
274
+ else
275
+ stype = 'REL'
276
+ sname = '.rel.plt'
277
+ end
278
+
279
+ if not relplt = @sections.find { |s| s.type == stype and s.name == sname }
280
+ relplt = Section.new
281
+ relplt.name = sname
282
+ relplt.flags = ['ALLOC']
283
+ encode_add_section relplt
284
+ end
285
+ relplt.encoded = EncodedData.new('', :export => {'_REL_PLT' => 0})
286
+ list.each { |r| relplt.encoded << r.encode(self) }
287
+ @tag['JMPREL'] = label_at(relplt.encoded, 0)
288
+ @tag['PLTRELSZ'] = relplt.encoded.virtsize
289
+ @tag['PLTREL'] = relplt.type = stype
290
+ @tag[stype + 'ENT'] = relplt.entsize = relplt.addralign = (stype == 'REL' ? Relocation.size(self) : RelocationAddend.size(self))
291
+ encode_check_section_size relplt
292
+ end
293
+
294
+ list = @relocations.find_all { |r| r.type != 'JMP_SLOT' and not r.addend }
295
+ if not list.empty?
296
+ if not @tag['TEXTREL'] and @sections.find { |s_|
297
+ s_.encoded and e = s_.encoded.inv_export[0] and not s_.flags.include? 'WRITE' and
298
+ list.find { |r| Expression[r.offset, :-, e].reduce.kind_of? ::Integer }
299
+ # TODO need to check with r.offset.bind(elf_binding)
300
+ }
301
+ @tag['TEXTREL'] = 0
302
+ end
303
+ if not rel = @sections.find { |s_| s_.type == 'REL' and s_.name == '.rel.dyn' }
304
+ rel = Section.new
305
+ rel.name = '.rel.dyn'
306
+ rel.type = 'REL'
307
+ rel.flags = ['ALLOC']
308
+ rel.entsize = rel.addralign = Relocation.size(self)
309
+ encode_add_section rel
310
+ end
311
+ rel.encoded = EncodedData.new
312
+ list.each { |r| rel.encoded << r.encode(self) }
313
+ @tag['REL'] = label_at(rel.encoded, 0)
314
+ @tag['RELENT'] = Relocation.size(self)
315
+ @tag['RELSZ'] = rel.encoded.virtsize
316
+ encode_check_section_size rel
317
+ end
318
+
319
+ list = @relocations.find_all { |r| r.type != 'JMP_SLOT' and r.addend }
320
+ if not list.empty?
321
+ if not rela = @sections.find { |s_| s_.type == 'RELA' and s_.name == '.rela.dyn' }
322
+ rela = Section.new
323
+ rela.name = '.rela.dyn'
324
+ rela.type = 'RELA'
325
+ rela.flags = ['ALLOC']
326
+ rela.entsize = rela.addralign = RelocationAddend.size(self)
327
+ encode_add_section rela
328
+ end
329
+ rela.encoded = EncodedData.new
330
+ list.each { |r| rela.encoded << r.encode(self) }
331
+ @tag['RELA'] = label_at(rela.encoded, 0)
332
+ @tag['RELAENT'] = RelocationAddend.size(self)
333
+ @tag['RELASZ'] = rela.encoded.virtsize
334
+ encode_check_section_size rela
335
+ end
336
+ end
337
+
338
+ # creates the .plt/.got from the @relocations
339
+ def arch_386_preencode_reloc
340
+ # if .got.plt does not exist, the dynamic loader segfaults
341
+ if not gotplt = @sections.find { |s| s.type == 'PROGBITS' and s.name == '.got.plt' }
342
+ gotplt = Section.new
343
+ gotplt.name = '.got.plt'
344
+ gotplt.type = 'PROGBITS'
345
+ gotplt.flags = %w[ALLOC WRITE]
346
+ gotplt.addralign = @bitsize/8
347
+ # _DYNAMIC is not base-relocated at runtime
348
+ encode_add_section gotplt
349
+ end
350
+ gotplt.encoded ||= (EncodedData.new('', :export => {'_PLT_GOT' => 0}) << encode_xword('_DYNAMIC') << encode_xword(0) << encode_xword(0))
351
+ @tag['PLTGOT'] = label_at(gotplt.encoded, 0)
352
+ plt = nil
353
+
354
+ shellcode = lambda { |c| Shellcode.new(@cpu).share_namespace(self).assemble(c).encoded }
355
+
356
+ @relocations.dup.each { |r|
357
+ case r.type
358
+ when 'PC32'
359
+ next if not r.symbol
360
+
361
+ if r.symbol.type != 'FUNC'
362
+ # external data xref: generate a GOT entry
363
+ # XXX reuse .got.plt ?
364
+ if not got ||= @sections.find { |s| s.type == 'PROGBITS' and s.name == '.got' }
365
+ got = Section.new
366
+ got.name = '.got'
367
+ got.type = 'PROGBITS'
368
+ got.flags = %w[ALLOC WRITE]
369
+ got.addralign = @bitsize/8
370
+ got.encoded = EncodedData.new
371
+ encode_add_section got
372
+ end
373
+
374
+ prevoffset = r.offset
375
+ gotlabel = r.symbol.name + '_got_entry'
376
+ if not got.encoded.export[gotlabel]
377
+ # create the got thunk
378
+ got.encoded.add_export(gotlabel, got.encoded.length)
379
+ got.encoded << encode_xword(0)
380
+
381
+ # transform the reloc PC32 => GLOB_DAT
382
+ r.type = 'GLOB_DAT'
383
+ r.offset = Expression[gotlabel]
384
+ r.addend = 0 if @bitsize == 64
385
+ else
386
+ @relocations.delete r
387
+ end
388
+
389
+ # prevoffset is label_section_start + int_section_offset
390
+ target_s = @sections.find { |s| s.encoded and s.encoded.export[prevoffset.lexpr] == 0 }
391
+ rel = target_s.encoded.reloc[prevoffset.rexpr]
392
+ # [foo] => [foo - reloc_addr + gotlabel]
393
+
394
+ rel.target = Expression[[rel.target, :-, prevoffset], :+, gotlabel]
395
+ next
396
+ end
397
+
398
+ # convert to .plt entry
399
+ #
400
+ # [.plt header]
401
+ # plt_start: # caller set ebx = gotplt if generate_PIC
402
+ # push [gotplt+4]
403
+ # jmp [gotplt+8]
404
+ #
405
+ # [.plt thunk]
406
+ # some_func_thunk:
407
+ # jmp [gotplt+func_got_offset]
408
+ # some_func_got_default:
409
+ # push some_func_jmpslot_offset_in_.rel.plt
410
+ # jmp plt_start
411
+ #
412
+ # [.got.plt header]
413
+ # dd _DYNAMIC
414
+ # dd 0 # rewritten to GOTPLT? by ld-linux
415
+ # dd 0 # rewritten to dlresolve_inplace by ld-linux
416
+ #
417
+ # [.got.plt + func_got_offset]
418
+ # dd some_func_got_default # lazily rewritten to the real addr of some_func by jmp dlresolve_inplace
419
+ # # base_relocated ?
420
+
421
+ # in the PIC case, _dlresolve imposes us to use the ebx register (which may not be saved by the calling function..)
422
+ # also geteip trashes eax, which may interfere with regparm(3)
423
+ base = @cpu.generate_PIC ? @bitsize == 32 ? 'ebx' : 'rip-$_+_PLT_GOT' : '_PLT_GOT'
424
+ if not plt ||= @sections.find { |s| s.type == 'PROGBITS' and s.name == '.plt' }
425
+ plt = Section.new
426
+ plt.name = '.plt'
427
+ plt.type = 'PROGBITS'
428
+ plt.flags = %w[ALLOC EXECINSTR]
429
+ plt.addralign = @bitsize/8
430
+ plt.encoded = EncodedData.new
431
+ sz = @bitsize/8
432
+ ptqual = @bitsize == 32 ? 'dword' : 'qword'
433
+ plt.encoded << shellcode["metasm_plt_start:\npush #{ptqual} ptr [#{base}+#{sz}]\njmp #{ptqual} ptr [#{base}+#{2*sz}]"]
434
+ if @cpu.generate_PIC and @bitsize == 32 and not @sections.find { |s| s.encoded and s.encoded.export['metasm_intern_geteip'] }
435
+ plt.encoded << shellcode["metasm_intern_geteip:\ncall 42f\n42: pop eax\nsub eax, 42b-metasm_intern_geteip\nret"]
436
+ end
437
+ encode_add_section plt
438
+ end
439
+
440
+ prevoffset = r.offset
441
+ pltlabel = r.symbol.name + '_plt_thunk'
442
+ if not plt.encoded.export[pltlabel]
443
+ # create the plt thunk
444
+ plt.encoded.add_export pltlabel, plt.encoded.length
445
+ if @cpu.generate_PIC and @bitsize == 32
446
+ plt.encoded << shellcode["call metasm_intern_geteip\nlea #{base}, [eax+_PLT_GOT-metasm_intern_geteip]"]
447
+ end
448
+ plt.encoded << shellcode["jmp [#{base} + #{gotplt.encoded.length}]"]
449
+ plt.encoded.add_export r.symbol.name+'_plt_default', plt.encoded.length
450
+ reloffset = @relocations.find_all { |rr| rr.type == 'JMP_SLOT' }.length
451
+ reloffset *= Relocation.size(self) if @bitsize == 32
452
+ plt.encoded << shellcode["push #{reloffset}\njmp metasm_plt_start"]
453
+
454
+ # transform the reloc PC32 => JMP_SLOT
455
+ r.type = 'JMP_SLOT'
456
+ r.offset = Expression['_PLT_GOT', :+, gotplt.encoded.length]
457
+ r.addend = 0 if @bitsize == 64
458
+
459
+ gotplt.encoded << encode_xword(r.symbol.name + '_plt_default')
460
+ else
461
+ @relocations.delete r
462
+ end
463
+
464
+ # mutate the original relocation
465
+ # XXX relies on the exact form of r.target from arch_create_reloc
466
+ target_s = @sections.find { |s| s.encoded and s.encoded.export[prevoffset.lexpr] == 0 }
467
+ rel = target_s.encoded.reloc[prevoffset.rexpr]
468
+ rel.target = Expression[[[rel.target, :-, prevoffset.rexpr], :-, label_at(target_s.encoded, 0)], :+, pltlabel]
469
+
470
+ # when 'GOTOFF', 'GOTPC'
471
+ end
472
+ }
473
+ encode_check_section_size gotplt
474
+ encode_check_section_size plt if plt
475
+ #encode_check_section_size got if got
476
+ end
477
+ alias arch_x86_64_preencode_reloc arch_386_preencode_reloc
478
+
479
+ # encodes the .dynamic section, creates .hash/.gnu.hash/.rel/.rela/.dynsym/.strtab/.init,*_array as needed
480
+ def encode_segments_dynamic
481
+ if not strtab = @sections.find { |s| s.type == 'STRTAB' and s.flags.include? 'ALLOC' }
482
+ strtab = Section.new
483
+ strtab.name = '.dynstr'
484
+ strtab.addralign = 1
485
+ strtab.type = 'STRTAB'
486
+ strtab.flags = ['ALLOC']
487
+ encode_add_section strtab
488
+ end
489
+ strtab.encoded = EncodedData.new << 0
490
+ @tag['STRTAB'] = label_at(strtab.encoded, 0)
491
+
492
+ if not dynamic = @sections.find { |s| s.type == 'DYNAMIC' }
493
+ dynamic = Section.new
494
+ dynamic.name = '.dynamic'
495
+ dynamic.type = 'DYNAMIC'
496
+ dynamic.flags = %w[WRITE ALLOC] # XXX why write ?
497
+ dynamic.addralign = dynamic.entsize = @bitsize / 8 * 2
498
+ dynamic.link = strtab
499
+ encode_add_section dynamic
500
+ end
501
+ dynamic.encoded = EncodedData.new('', :export => {'_DYNAMIC' => 0})
502
+
503
+ encode_tag = lambda { |k, v|
504
+ dynamic.encoded <<
505
+ encode_sxword(int_from_hash(k, DYNAMIC_TAG)) <<
506
+ encode_xword(v)
507
+ }
508
+
509
+ # find or create string in strtab
510
+ add_str = lambda { |n|
511
+ if n and n != '' and not ret = strtab.encoded.data.index(n + 0.chr)
512
+ ret = strtab.encoded.virtsize
513
+ strtab.encoded << n << 0
514
+ end
515
+ ret || 0
516
+ }
517
+ @tag.keys.each { |k|
518
+ case k
519
+ when 'NEEDED'; @tag[k].each { |n| encode_tag[k, add_str[n]] }
520
+ when 'SONAME', 'RPATH', 'RUNPATH'; encode_tag[k, add_str[@tag[k]]]
521
+ when 'INIT_ARRAY', 'FINI_ARRAY', 'PREINIT_ARRAY' # build section containing the array
522
+ if not ar = @sections.find { |s| s.name == '.' + k.downcase }
523
+ ar = Section.new
524
+ ar.name = '.' + k.downcase
525
+ ar.type = k
526
+ ar.addralign = ar.entsize = @bitsize/8
527
+ ar.flags = %w[WRITE ALLOC]
528
+ ar.encoded = EncodedData.new
529
+ encode_add_section ar # insert before encoding syms/relocs (which need section indexes)
530
+ end
531
+
532
+ # fill these later, but create the base relocs now
533
+ arch_create_reloc_func = "arch_#{@header.machine.downcase}_create_reloc"
534
+ next if not respond_to?(arch_create_reloc_func)
535
+ curaddr = label_at(@encoded, 0, 'elf_start')
536
+ fkbind = {}
537
+ @sections.each { |s|
538
+ next if not s.encoded
539
+ fkbind.update s.encoded.binding(Expression[curaddr, :+, 1])
540
+ }
541
+ @relocations ||= []
542
+ off = ar.encoded.length
543
+ @tag[k].each { |a|
544
+ rel = Metasm::Relocation.new(Expression[a], "u#@bitsize".to_sym, @endianness)
545
+ send(arch_create_reloc_func, ar, off, fkbind, rel)
546
+ off += @bitsize/8
547
+ }
548
+ end
549
+ }
550
+
551
+ encode_reorder_symbols
552
+ encode_gnu_hash
553
+ encode_hash
554
+ encode_segments_relocs
555
+ dynsym = encode_segments_symbols(strtab)
556
+ @sections.find_all { |s| %w[HASH GNU_HASH REL RELA].include? s.type }.each { |s| s.link = dynsym }
557
+
558
+ encode_check_section_size strtab
559
+
560
+ # XXX any order needed ?
561
+ @tag.keys.each { |k|
562
+ case k
563
+ when Integer # unknown tags = array of values
564
+ @tag[k].each { |n| encode_tag[k, n] }
565
+ when 'PLTREL'; encode_tag[k, int_from_hash(@tag[k], DYNAMIC_TAG)]
566
+ when 'FLAGS'; encode_tag[k, bits_from_hash(@tag[k], DYNAMIC_FLAGS)]
567
+ when 'FLAGS_1'; encode_tag[k, bits_from_hash(@tag[k], DYNAMIC_FLAGS_1)]
568
+ when 'FEATURES_1'; encode_tag[k, bits_from_hash(@tag[k], DYNAMIC_FEATURES_1)]
569
+ when 'NULL' # keep last
570
+ when 'STRTAB'
571
+ encode_tag[k, @tag[k]]
572
+ encode_tag['STRSZ', strtab.encoded.size]
573
+ when 'INIT_ARRAY', 'FINI_ARRAY', 'PREINIT_ARRAY' # build section containing the array
574
+ ar = @sections.find { |s| s.name == '.' + k.downcase }
575
+ @tag[k].each { |p| ar.encoded << encode_addr(p) }
576
+ encode_check_section_size ar
577
+ encode_tag[k, label_at(ar.encoded, 0)]
578
+ encode_tag[k + 'SZ', ar.encoded.virtsize]
579
+ when 'NEEDED', 'SONAME', 'RPATH', 'RUNPATH' # already handled
580
+ else
581
+ encode_tag[k, @tag[k]]
582
+ end
583
+ }
584
+ encode_tag['NULL', @tag['NULL'] || 0]
585
+
586
+ encode_check_section_size dynamic
587
+ end
588
+
589
+ # creates the undef symbol list from the section.encoded.reloc and a list of known exported symbols (e.g. from libc)
590
+ # also populates @tag['NEEDED']
591
+ def automagic_symbols
592
+ GNUExports rescue return # autorequire
593
+ autoexports = GNUExports::EXPORT.dup
594
+ @sections.each { |s|
595
+ next if not s.encoded
596
+ s.encoded.export.keys.each { |e| autoexports.delete e }
597
+ }
598
+ @sections.each { |s|
599
+ next if not s.encoded
600
+ s.encoded.reloc.each_value { |r|
601
+ t = Expression[r.target.reduce]
602
+ if t.op == :+ and t.rexpr.kind_of? Expression and t.rexpr.op == :- and not t.rexpr.lexpr and
603
+ t.rexpr.rexpr.kind_of?(::String) and t.lexpr.kind_of?(::String)
604
+ symname = t.lexpr
605
+ else
606
+ symname = t.reduce_rec
607
+ end
608
+ next if not dll = autoexports[symname]
609
+ if not @symbols.find { |sym| sym.name == symname }
610
+ @tag['NEEDED'] ||= []
611
+ @tag['NEEDED'] |= [dll]
612
+ sym = Symbol.new
613
+ sym.shndx = 'UNDEF'
614
+ sym.type = 'FUNC'
615
+ sym.name = symname
616
+ sym.bind = 'GLOBAL'
617
+ @symbols << sym
618
+ end
619
+ }
620
+ }
621
+ end
622
+
623
+ # reads the existing segment/sections.encoded and populate @relocations from the encoded.reloc hash
624
+ def create_relocations
625
+ @relocations = []
626
+
627
+ arch_create_reloc_func = "arch_#{@header.machine.downcase}_create_reloc"
628
+ if not respond_to? arch_create_reloc_func
629
+ puts "Elf: create_reloc: unhandled architecture #{@header.machine}" if $VERBOSE
630
+ return
631
+ end
632
+
633
+ # create a fake binding with all our own symbols
634
+ # not foolproof, should work in most cases
635
+ curaddr = label_at(@encoded, 0, 'elf_start')
636
+ binding = {'_DYNAMIC' => 0, '_GOT' => 0} # XXX
637
+ @sections.each { |s|
638
+ next if not s.encoded
639
+ binding.update s.encoded.binding(curaddr)
640
+ curaddr = Expression[curaddr, :+, s.encoded.virtsize]
641
+ }
642
+
643
+ @sections.each { |s|
644
+ next if not s.encoded
645
+ s.encoded.reloc.each { |off, rel|
646
+ t = rel.target.bind(binding).reduce
647
+ next if not t.kind_of? Expression # XXX segment_encode only
648
+ send(arch_create_reloc_func, s, off, binding)
649
+ }
650
+ }
651
+ end
652
+
653
+ # references to FUNC symbols are transformed to JMPSLOT relocations (aka call to .plt)
654
+ # TODO ET_REL support
655
+ def arch_386_create_reloc(section, off, binding, rel=nil)
656
+ rel ||= section.encoded.reloc[off]
657
+ if rel.endianness != @endianness or not [:u32, :i32, :a32].include? rel.type
658
+ puts "ELF: 386_create_reloc: ignoring reloc #{rel.target} in #{section.name}: bad reloc type" if $VERBOSE
659
+ return
660
+ end
661
+ startaddr = label_at(@encoded, 0)
662
+ r = Relocation.new
663
+ r.offset = Expression[label_at(section.encoded, 0, 'sect_start'), :+, off]
664
+ if Expression[rel.target, :-, startaddr].bind(binding).reduce.kind_of?(::Integer)
665
+ # this location is relative to the base load address of the ELF
666
+ r.type = 'RELATIVE'
667
+ else
668
+ et = rel.target.externals
669
+ extern = et.find_all { |name| not binding[name] }
670
+ if extern.length != 1
671
+ puts "ELF: 386_create_reloc: ignoring reloc #{rel.target} in #{section.name}: #{extern.inspect} unknown" if $VERBOSE
672
+ return
673
+ end
674
+ if not sym = @symbols.find { |s| s.name == extern.first }
675
+ puts "ELF: 386_create_reloc: ignoring reloc #{rel.target} in #{section.name}: undefined symbol #{extern.first}" if $VERBOSE
676
+ return
677
+ end
678
+ r.symbol = sym
679
+ rel.target = Expression[rel.target, :-, sym.name]
680
+ if rel.target.bind(binding).reduce.kind_of? ::Integer
681
+ r.type = '32'
682
+ elsif Expression[rel.target, :+, label_at(section.encoded, 0)].bind(section.encoded.binding).reduce.kind_of? ::Integer
683
+ rel.target = Expression[[rel.target, :+, label_at(section.encoded, 0)], :+, off]
684
+ r.type = 'PC32'
685
+ # TODO tls ?
686
+ else
687
+ puts "ELF: 386_create_reloc: ignoring reloc #{sym.name} + #{rel.target}: cannot find matching standard reloc type" if $VERBOSE
688
+ return
689
+ end
690
+ end
691
+ @relocations << r
692
+ end
693
+
694
+ def arch_x86_64_create_reloc(section, off, binding, rel=nil)
695
+ rel ||= section.encoded.reloc[off]
696
+ if rel.endianness != @endianness or not rel.type.to_s =~ /^[aiu](32|64)$/
697
+ puts "ELF: x86_64_create_reloc: ignoring reloc #{rel.target} in #{section.name}: bad reloc type" if $VERBOSE
698
+ return
699
+ end
700
+ startaddr = label_at(@encoded, 0)
701
+ r = RelocationAddend.new
702
+ r.addend = 0
703
+ r.offset = Expression[label_at(section.encoded, 0, 'sect_start'), :+, off]
704
+ if Expression[rel.target, :-, startaddr].bind(binding).reduce.kind_of?(::Integer)
705
+ # this location is relative to the base load address of the ELF
706
+ if rel.length != 8
707
+ puts "ELF: x86_64_create_reloc: ignoring reloc #{rel.target} in #{section.name}: relative non-x64" if $VERBOSE
708
+ return
709
+ end
710
+ r.type = 'RELATIVE'
711
+ else
712
+ et = rel.target.externals
713
+ extern = et.find_all { |name| not binding[name] }
714
+ if extern.length != 1
715
+ puts "ELF: x86_64_create_reloc: ignoring reloc #{rel.target} in #{section.name}: #{extern.inspect} unknown" if $VERBOSE
716
+ return
717
+ end
718
+ if not sym = @symbols.find { |s| s.name == extern.first }
719
+ puts "ELF: x86_64_create_reloc: ignoring reloc #{rel.target} in #{section.name}: undefined symbol #{extern.first}" if $VERBOSE
720
+ return
721
+ end
722
+ r.symbol = sym
723
+ rel.target = Expression[rel.target, :-, sym.name]
724
+ if rel.target.bind(binding).reduce.kind_of? ::Integer
725
+ r.type = '64' # XXX check that
726
+ elsif Expression[rel.target, :+, label_at(section.encoded, 0)].bind(section.encoded.binding).reduce.kind_of? ::Integer
727
+ rel.target = Expression[[rel.target, :+, label_at(section.encoded, 0)], :+, off]
728
+ r.type = 'PC32' # XXX
729
+ # TODO tls ?
730
+ else
731
+ puts "ELF: x86_64_create_reloc: ignoring reloc #{sym.name} + #{rel.target}: cannot find matching standard reloc type" if $VERBOSE
732
+ return
733
+ end
734
+ end
735
+ r.addend = Expression[rel.target]
736
+ #section.encoded.reloc.delete off
737
+ @relocations << r
738
+ end
739
+
740
+ # resets the fields of the elf headers that should be recalculated, eg phdr offset
741
+ def invalidate_header
742
+ @header.shoff = @header.shnum = nil
743
+ @header.phoff = @header.phnum = nil
744
+ @header.shstrndx = nil
745
+ @sections.to_a.each { |s|
746
+ s.name_p = nil
747
+ s.offset = nil
748
+ }
749
+ @segments.to_a.each { |s|
750
+ s.offset = nil
751
+ }
752
+ self
753
+ end
754
+
755
+ # put every ALLOC section in a segment, create segments if needed
756
+ # sections with a good offset within a segment are ignored
757
+ def encode_make_segments_from_sections
758
+ # fixed addresses first
759
+ seclist = @sections.find_all { |sec| sec.addr.kind_of? Integer }.sort_by { |sec| sec.addr } | @sections
760
+ seclist.each { |sec|
761
+ next if not sec.flags.to_a.include? 'ALLOC'
762
+
763
+ # check if we fit in an existing segment
764
+ loadsegs = @segments.find_all { |seg_| seg_.type == 'LOAD' }
765
+
766
+ if sec.addr.kind_of?(::Integer) and seg = loadsegs.find { |seg_|
767
+ seg_.vaddr.kind_of?(::Integer) and seg_.vaddr <= sec.addr and seg_.vaddr + seg_.memsz >= sec.addr + sec.size
768
+ }
769
+ # sections is already inside a segment: we're reencoding an ELF, just patch the section in the segment
770
+ seg.encoded[sec.addr - seg.vaddr, sec.size] = sec.encoded if sec.encoded
771
+ next
772
+ end
773
+
774
+ if not seg = loadsegs.find { |seg_|
775
+ sec.flags.to_a.include?('WRITE') == seg_.flags.to_a.include?('W') and
776
+ #sec.flags.to_a.include?('EXECINSTR') == seg_.flags.to_a.include?('X') and
777
+ not seg_.memsz and
778
+ not loadsegs[loadsegs.index(seg_)+1..-1].find { |sseg|
779
+ # check if another segment would overlap if we add the sec to seg_
780
+ o = Expression[sseg.vaddr, :-, [seg_.vaddr, :+, seg_.encoded.length+sec.encoded.length]].reduce
781
+ o.kind_of? ::Integer and o < 0
782
+ }
783
+ }
784
+ # nope, create a new one
785
+ seg = Segment.new
786
+ seg.type = 'LOAD'
787
+ seg.flags = ['R']
788
+ seg.flags << 'W' if sec.flags.include? 'WRITE'
789
+ seg.align = 0x1000
790
+ seg.encoded = EncodedData.new
791
+ seg.offset = new_label('segment_offset')
792
+ seg.vaddr = sec.addr || new_label('segment_address')
793
+ @segments << seg
794
+ end
795
+ seg.flags |= ['X'] if sec.flags.include? 'EXECINSTR'
796
+ seg.encoded.align sec.addralign if sec.addralign
797
+ sec.addr = Expression[seg.vaddr, :+, seg.encoded.length]
798
+ sec.offset = Expression[seg.offset, :+, seg.encoded.length]
799
+ seg.encoded << sec.encoded
800
+ }
801
+ end
802
+
803
+ # create the relocations from the sections.encoded.reloc
804
+ # create the dynamic sections
805
+ # put sections/phdr in PT_LOAD segments
806
+ # link
807
+ # TODO support mapped PHDR, obey section-specified base address, handle NOBITS
808
+ # encode ET_REL
809
+ def encode(type='DYN')
810
+ @header.type ||= {:bin => 'EXEC', :lib => 'DYN', :obj => 'REL'}.fetch(type, type)
811
+ @header.machine ||= case @cpu.shortname
812
+ when 'x64'; 'X86_64'
813
+ when 'ia32'; '386'
814
+ when 'mips'; 'MIPS'
815
+ when 'powerpc'; 'PPC'
816
+ when 'arm'; 'ARM'
817
+ end
818
+
819
+ if @header.type == 'REL'
820
+ raise 'ET_REL encoding not supported atm, come back later'
821
+ end
822
+
823
+ @encoded = EncodedData.new
824
+ if @header.type != 'EXEC' or @segments.find { |i| i.type == 'INTERP' }
825
+ # create a .dynamic section unless we are an ET_EXEC with .nointerp
826
+ automagic_symbols
827
+ create_relocations
828
+ encode_segments_dynamic
829
+ end
830
+
831
+ @segments.delete_if { |s| s.type == 'INTERP' } if not @header.entry
832
+
833
+ encode_make_segments_from_sections
834
+
835
+ loadsegs = @segments.find_all { |seg_| seg_.type == 'LOAD' }
836
+
837
+ # ensure PT_INTERP is mapped if present
838
+ if interp = @segments.find { |i| i.type == 'INTERP' }
839
+ if not seg = loadsegs.find { |seg_| not seg_.memsz and interp.flags & seg_.flags == interp.flags and
840
+ not loadsegs[loadsegs.index(seg_)+1..-1].find { |sseg|
841
+ o = Expression[sseg.vaddr, :-, [seg_.vaddr, :+, seg_.encoded.length+interp.encoded.length]].reduce
842
+ o.kind_of? ::Integer and o < 0
843
+ }
844
+ }
845
+ seg = Segment.new
846
+ seg.type = 'LOAD'
847
+ seg.flags = interp.flags.dup
848
+ seg.align = 0x1000
849
+ seg.encoded = EncodedData.new
850
+ seg.offset = new_label('segment_offset')
851
+ seg.vaddr = new_label('segment_address')
852
+ loadsegs << seg
853
+ @segments << seg
854
+ end
855
+ interp.vaddr = Expression[seg.vaddr, :+, seg.encoded.length]
856
+ interp.offset = Expression[seg.offset, :+, seg.encoded.length]
857
+ seg.encoded << interp.encoded
858
+ interp.encoded = nil
859
+ end
860
+
861
+ # ensure last PT_LOAD is writeable (used for bss)
862
+ seg = loadsegs.last
863
+ if not seg or not seg.flags.include? 'W'
864
+ seg = Segment.new
865
+ seg.type = 'LOAD'
866
+ seg.flags = ['R', 'W']
867
+ seg.encoded = EncodedData.new
868
+ loadsegs << seg
869
+ @segments << seg
870
+ end
871
+
872
+ # add dynamic segment
873
+ if ds = @sections.find { |sec| sec.type == 'DYNAMIC' }
874
+ ds.set_default_values self
875
+ seg = Segment.new
876
+ seg.type = 'DYNAMIC'
877
+ seg.flags = ['R', 'W']
878
+ seg.offset = ds.offset
879
+ seg.vaddr = ds.addr
880
+ seg.memsz = seg.filesz = ds.size
881
+ @segments << seg
882
+ end
883
+
884
+ # use variables in the first segment descriptor, to allow fixup later
885
+ # (when we'll be able to include the program header)
886
+ if first_seg = loadsegs.first
887
+ first_seg_oaddr = first_seg.vaddr # section's vaddr depend on oaddr
888
+ first_seg_off = first_seg.offset
889
+ first_seg.vaddr = new_label('segvaddr')
890
+ first_seg.offset = new_label('segoff')
891
+ first_seg.memsz = new_label('segmemsz')
892
+ first_seg.filesz = new_label('segfilsz')
893
+ end
894
+
895
+ if first_seg and not @segments.find { |seg_| seg_.type == 'PHDR' }
896
+ phdr = Segment.new
897
+ phdr.type = 'PHDR'
898
+ phdr.flags = first_seg.flags
899
+ phdr.offset = new_label('segoff')
900
+ phdr.vaddr = new_label('segvaddr')
901
+ phdr.filesz = phdr.memsz = new_label('segmemsz')
902
+ @segments.unshift phdr
903
+ end
904
+
905
+ # encode section&program headers
906
+ if @header.shnum != 0
907
+ st = @sections.inject(EncodedData.new) { |edata, s| edata << s.encode(self) }
908
+ else
909
+ @header.shoff = @header.shnum = @header.shstrndx = 0
910
+ end
911
+ pt = @segments.inject(EncodedData.new) { |edata, s| edata << s.encode(self) }
912
+
913
+ binding = {}
914
+ @encoded << @header.encode(self)
915
+ @encoded.align 8
916
+ binding[@header.phoff] = @encoded.length
917
+ if phdr
918
+ binding[phdr.offset] = @encoded.length
919
+ pt.add_export phdr.vaddr, 0
920
+ binding[phdr.memsz] = pt.length
921
+ end
922
+ @encoded << pt
923
+ @encoded.align 8
924
+
925
+ if first_seg
926
+ # put headers into the 1st mmaped segment
927
+ if first_seg_oaddr.kind_of? ::Integer
928
+ # pad headers to align the 1st segment's data
929
+ @encoded.virtsize += (first_seg_oaddr - @encoded.virtsize) & 0xfff
930
+ addr = first_seg_oaddr - @encoded.length
931
+ else
932
+ addr = ((@header.type == 'EXEC') ? 0x08048000 : 0)
933
+ binding[first_seg_oaddr] = addr + @encoded.length
934
+ end
935
+ binding[first_seg_off] = @encoded.length if not first_seg_off.kind_of? ::Integer
936
+ first_seg.encoded = @encoded << first_seg.encoded
937
+ @encoded = EncodedData.new
938
+ binding[first_seg.memsz] = first_seg.encoded.virtsize if not first_seg.memsz.kind_of? ::Integer
939
+ binding[first_seg.filesz] = first_seg.encoded.rawsize if not first_seg.filesz.kind_of? ::Integer
940
+ end
941
+
942
+ @segments.each { |seg_|
943
+ next if not seg_.encoded
944
+ if seg_.vaddr.kind_of? ::Integer
945
+ raise "cannot put segment at address #{Expression[seg_.vaddr]} (now at #{Expression[addr]})" if seg_.vaddr < addr
946
+ addr = seg_.vaddr
947
+ else
948
+ binding[seg_.vaddr] = addr
949
+ end
950
+ # ensure seg_.vaddr & page_size == seg_.offset & page_size
951
+ @encoded.virtsize += (addr - @encoded.virtsize) & 0xfff
952
+ binding.update seg_.encoded.binding(addr)
953
+ binding[seg_.offset] = @encoded.length
954
+ seg_.encoded.align 8
955
+ @encoded << seg_.encoded[0, seg_.encoded.rawsize]
956
+ addr += seg_.encoded.length
957
+
958
+ # page break for memory permission enforcement
959
+ if @segments[@segments.index(seg_)+1..-1].find { |seg__| seg__.encoded and seg__.vaddr.kind_of? ::Integer }
960
+ addr += 0x1000 - (addr & 0xfff) if addr & 0xfff != 0 # minimize memory size
961
+ else
962
+ addr += 0x1000 # minimize file size
963
+ end
964
+ }
965
+
966
+ binding[@header.shoff] = @encoded.length if st
967
+ @encoded << st
968
+ @encoded.align 8
969
+
970
+ @sections.each { |sec|
971
+ next if not sec.encoded or sec.flags.include? 'ALLOC' # already in a segment.encoded
972
+ binding[sec.offset] = @encoded.length
973
+ binding.update sec.encoded.binding
974
+ @encoded << sec.encoded
975
+ @encoded.align 8
976
+ }
977
+
978
+ @encoded.fixup! binding
979
+ @encoded.data
980
+ end
981
+
982
+ def parse_init
983
+ # allow the user to specify a section, falls back to .text if none specified
984
+ if not defined? @cursource or not @cursource
985
+ @cursource = Object.new
986
+ class << @cursource
987
+ attr_accessor :elf
988
+ def <<(*a)
989
+ t = Preprocessor::Token.new(nil)
990
+ t.raw = '.text'
991
+ elf.parse_parser_instruction t
992
+ elf.cursource.send(:<<, *a)
993
+ end
994
+ end
995
+ @cursource.elf = self
996
+ end
997
+
998
+ @segments.delete_if { |s| s.type == 'INTERP' }
999
+ seg = Segment.new
1000
+ seg.type = 'INTERP'
1001
+ seg.encoded = EncodedData.new << (@bitsize == 64 ? DEFAULT_INTERP64 : DEFAULT_INTERP) << 0
1002
+ seg.flags = ['R']
1003
+ seg.memsz = seg.filesz = seg.encoded.length
1004
+ @segments.unshift seg
1005
+
1006
+ @source ||= {}
1007
+ super()
1008
+ end
1009
+
1010
+ # handles elf meta-instructions
1011
+ #
1012
+ # syntax:
1013
+ # .section "<name>" [<perms>] [base=<base>]
1014
+ # change current section (where normal instruction/data are put)
1015
+ # perms = list of 'w' 'x' 'alloc', may be prefixed by 'no'
1016
+ # 'r' ignored
1017
+ # defaults to 'alloc'
1018
+ # shortcuts: .text .data .rodata .bss
1019
+ # base: immediate expression representing the section base address
1020
+ # .entrypoint [<label>]
1021
+ # defines the program entrypoint to the specified label / current location
1022
+ # .global "<name>" [<label>] [<label_end>] [type=<FUNC|OBJECT|...>] [plt=<plt_label_name>] [undef]
1023
+ # .weak ...
1024
+ # .local ...
1025
+ # builds a symbol with specified type/scope/size, type defaults to 'func'
1026
+ # if plt_label_name is specified, the compiler will build an entry in the plt for this symbol, with this label (PIC & on-demand resolution)
1027
+ # XXX plt ignored (automagic)
1028
+ # .symbol [global|weak|local] "<name>" ... see .global/.weak/.local
1029
+ # .needed "<libpath>"
1030
+ # marks the elf as requiring the specified library (DT_NEEDED)
1031
+ # .soname "<soname>"
1032
+ # defines the current elf DT_SONAME (exported library name)
1033
+ # .interp "<interpreter_path>"
1034
+ # .nointerp
1035
+ # defines the required ELF interpreter
1036
+ # defaults to '/lib/ld.so'
1037
+ # 'nil'/'none' remove the interpreter specification
1038
+ # .pt_gnu_stack rw|rwx
1039
+ # defines the PT_GNU_STACK flag (default: unspecified, => rwx)
1040
+ # .init/.fini [<label>]
1041
+ # defines the DT_INIT/DT_FINI dynamic tags, same semantic as .entrypoint
1042
+ # .init_array/.fini_array/.preinit_array <label> [, <label>]*
1043
+ # append to the DT_*_ARRAYs
1044
+ #
1045
+ def parse_parser_instruction(instr)
1046
+ readstr = lambda {
1047
+ @lexer.skip_space
1048
+ t = nil
1049
+ raise instr, "string expected, found #{t.raw.inspect if t}" if not t = @lexer.readtok or (t.type != :string and t.type != :quoted)
1050
+ t.value || t.raw
1051
+ }
1052
+ check_eol = lambda {
1053
+ @lexer.skip_space
1054
+ t = nil
1055
+ raise instr, "eol expected, found #{t.raw.inspect if t}" if t = @lexer.nexttok and t.type != :eol
1056
+ }
1057
+
1058
+ case instr.raw.downcase
1059
+ when '.text', '.data', '.rodata', '.bss'
1060
+ sname = instr.raw.downcase
1061
+ if not @sections.find { |s| s.name == sname }
1062
+ s = Section.new
1063
+ s.name = sname
1064
+ s.type = 'PROGBITS'
1065
+ s.encoded = EncodedData.new
1066
+ s.flags = case sname
1067
+ when '.text'; %w[ALLOC EXECINSTR]
1068
+ when '.data', '.bss'; %w[ALLOC WRITE]
1069
+ when '.rodata'; %w[ALLOC]
1070
+ end
1071
+ s.addralign = 8
1072
+ encode_add_section s
1073
+ end
1074
+ @cursource = @source[sname] ||= []
1075
+ check_eol[] if instr.backtrace # special case for magic @cursource
1076
+
1077
+ when '.section'
1078
+ # .section <section name|"section name"> [(no)wxalloc] [base=<expr>]
1079
+ sname = readstr[]
1080
+ if not s = @sections.find { |s_| s_.name == sname }
1081
+ s = Section.new
1082
+ s.type = 'PROGBITS'
1083
+ s.name = sname
1084
+ s.encoded = EncodedData.new
1085
+ s.flags = ['ALLOC']
1086
+ @sections << s
1087
+ end
1088
+ loop do
1089
+ @lexer.skip_space
1090
+ break if not tok = @lexer.nexttok or tok.type != :string
1091
+ case @lexer.readtok.raw.downcase
1092
+ when /^(no)?r?(w)?(x)?(alloc)?$/
1093
+ ar = []
1094
+ ar << 'WRITE' if $2
1095
+ ar << 'EXECINSTR' if $3
1096
+ ar << 'ALLOC' if $4
1097
+ if $1; s.flags -= ar
1098
+ else s.flags |= ar
1099
+ end
1100
+ when 'base'
1101
+ @lexer.skip_space
1102
+ @lexer.readtok if tok = @lexer.nexttok and tok.type == :punct and tok.raw == '='
1103
+ raise instr, 'bad section base' if not s.addr = Expression.parse(@lexer).reduce or not s.addr.kind_of? ::Integer
1104
+ else raise instr, 'unknown specifier'
1105
+ end
1106
+ end
1107
+ @cursource = @source[sname] ||= []
1108
+ check_eol[]
1109
+
1110
+ when '.entrypoint'
1111
+ # ".entrypoint <somelabel/expression>" or ".entrypoint" (here)
1112
+ @lexer.skip_space
1113
+ if tok = @lexer.nexttok and tok.type == :string
1114
+ raise instr if not entrypoint = Expression.parse(@lexer)
1115
+ else
1116
+ entrypoint = new_label('entrypoint')
1117
+ @cursource << Label.new(entrypoint, instr.backtrace.dup)
1118
+ end
1119
+ @header.entry = entrypoint
1120
+ check_eol[]
1121
+
1122
+ when '.global', '.weak', '.local', '.symbol'
1123
+ if instr.raw == '.symbol'
1124
+ bind = readstr[]
1125
+ else
1126
+ bind = instr.raw[1..-1]
1127
+ end
1128
+
1129
+ s = Symbol.new
1130
+ s.name = readstr[]
1131
+ s.type = 'FUNC'
1132
+ s.bind = bind.upcase
1133
+ # define s.section ? should check the section exporting s.target, but it may not be defined now
1134
+
1135
+ # parse pseudo instruction arguments
1136
+ loop do
1137
+ @lexer.skip_space
1138
+ ntok = @lexer.readtok
1139
+ if not ntok or ntok.type == :eol
1140
+ @lexer.unreadtok ntok
1141
+ break
1142
+ end
1143
+ raise instr, "syntax error: string expected, found #{ntok.raw.inspect}" if ntok.type != :string
1144
+ case ntok.raw
1145
+ when 'undef'
1146
+ s.shndx = 'UNDEF'
1147
+ when 'plt'
1148
+ @lexer.skip_space
1149
+ ntok = @lexer.readtok
1150
+ raise "syntax error: = expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :punct or ntok.raw != '='
1151
+ @lexer.skip_space
1152
+ ntok = @lexer.readtok
1153
+ raise "syntax error: label expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :string
1154
+ s.thunk = ntok.raw
1155
+ when 'type'
1156
+ @lexer.skip_space
1157
+ ntok = @lexer.readtok
1158
+ raise "syntax error: = expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :punct or ntok.raw != '='
1159
+ @lexer.skip_space
1160
+ ntok = @lexer.readtok
1161
+ raise "syntax error: symbol type expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :string or not SYMBOL_TYPE.index(ntok.raw)
1162
+ s.type = ntok.raw
1163
+ when 'size'
1164
+ @lexer.skip_space
1165
+ ntok = @lexer.readtok
1166
+ raise "syntax error: = expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :punct or ntok.raw != '='
1167
+ @lexer.skip_space
1168
+ ntok = @lexer.readtok
1169
+ raise "syntax error: symbol size expected, found #{ntok.raw.inspect if ntok}" if not ntok or ntok.type != :string or not ntok.raw =~ /^\d+$/
1170
+ s.size = ntok.raw.to_i
1171
+ else
1172
+ if not s.value
1173
+ s.value = ntok.raw
1174
+ elsif not s.size
1175
+ s.size = Expression[ntok.raw, :-, s.value]
1176
+ else
1177
+ raise instr, "syntax error: eol expected, found #{ntok.raw.inspect}"
1178
+ end
1179
+ end
1180
+ end
1181
+ s.value ||= s.name if not s.shndx and not s.thunk
1182
+ s.shndx ||= 1 if s.value
1183
+ @symbols << s
1184
+
1185
+ when '.needed'
1186
+ # a required library
1187
+ (@tag['NEEDED'] ||= []) << readstr[]
1188
+ check_eol[]
1189
+
1190
+ when '.soname'
1191
+ # exported library name
1192
+ @tag['SONAME'] = readstr[]
1193
+ check_eol[]
1194
+ @segments.delete_if { |s_| s_.type == 'INTERP' }
1195
+ @header.type = 'DYN'
1196
+
1197
+ when '.interp', '.nointerp'
1198
+ # required ELF interpreter
1199
+ interp = ((instr.raw == '.nointerp') ? 'nil' : readstr[])
1200
+
1201
+ @segments.delete_if { |s_| s_.type == 'INTERP' }
1202
+ case interp.downcase
1203
+ when 'nil', 'no', 'none'
1204
+ @header.shnum = 0
1205
+ else
1206
+ seg = Segment.new
1207
+ seg.type = 'INTERP'
1208
+ seg.encoded = EncodedData.new << interp << 0
1209
+ seg.flags = ['R']
1210
+ seg.memsz = seg.filesz = seg.encoded.length
1211
+ @segments.unshift seg
1212
+ end
1213
+
1214
+ check_eol[]
1215
+
1216
+ when '.pt_gnu_stack'
1217
+ # PT_GNU_STACK marking
1218
+ mode = readstr[]
1219
+
1220
+ @segments.delete_if { |s_| s_.type == 'GNU_STACK' }
1221
+ s = Segment.new
1222
+ s.type = 'GNU_STACK'
1223
+ case mode
1224
+ when /^rw$/i; s.flags = %w[R W]
1225
+ when /^rwx$/i; s.flags = %w[R W X]
1226
+ else raise instr, "syntax error: expected rw|rwx, found #{mode.inspect}"
1227
+ end
1228
+ @segments << s
1229
+
1230
+ when '.init', '.fini'
1231
+ # dynamic tag initialization
1232
+ @lexer.skip_space
1233
+ if tok = @lexer.nexttok and tok.type == :string
1234
+ raise instr, 'syntax error' if not init = Expression.parse(@lexer)
1235
+ else
1236
+ init = new_label(instr.raw[1..-1])
1237
+ @cursource << Label.new(init, instr.backtrace.dup)
1238
+ end
1239
+ @tag[instr.raw[1..-1].upcase] = init
1240
+ check_eol[]
1241
+
1242
+ when '.init_array', '.fini_array', '.preinit_array'
1243
+ t = @tag[instr.raw[1..-1].upcase] ||= []
1244
+ loop do
1245
+ raise instr, 'syntax error' if not e = Expression.parse(@lexer)
1246
+ t << e
1247
+ @lexer.skip_space
1248
+ ntok = @lexer.nexttok
1249
+ break if not ntok or ntok.type == :eol
1250
+ raise instr, "syntax error, ',' expected, found #{ntok.raw.inspect}" if nttok != :punct or ntok.raw != ','
1251
+ @lexer.readtok
1252
+ end
1253
+
1254
+ else super(instr)
1255
+ end
1256
+ end
1257
+
1258
+ # assembles the hash self.source to a section array
1259
+ def assemble(*a)
1260
+ parse(*a) if not a.empty?
1261
+ @source.each { |k, v|
1262
+ raise "no section named #{k} ?" if not s = @sections.find { |s_| s_.name == k }
1263
+ s.encoded << assemble_sequence(v, @cpu)
1264
+ v.clear
1265
+ }
1266
+ end
1267
+
1268
+ def encode_file(path, *a)
1269
+ ret = super(path, *a)
1270
+ File.chmod(0755, path) if @header.entry and @header.entry != 0
1271
+ ret
1272
+ end
1273
+
1274
+ # defines __ELF__
1275
+ def tune_prepro(l)
1276
+ l.define_weak('__ELF__', 1)
1277
+ end
1278
+
1279
+ # set the data model
1280
+ def tune_cparser(cp)
1281
+ super(cp)
1282
+ cp.lp64 if @cpu.size == 64
1283
+ end
1284
+
1285
+ # handles C attributes: export, export_as(foo), import, import_from(libc.so.6), init, fini, entrypoint
1286
+ def read_c_attrs(cp)
1287
+ cp.toplevel.symbol.each_value { |v|
1288
+ next if not v.kind_of? C::Variable
1289
+ if v.has_attribute 'export' or ea = v.has_attribute_var('export_as')
1290
+ s = Symbol.new
1291
+ s.name = ea || v.name
1292
+ s.type = v.type.kind_of?(C::Function) ? 'FUNC' : 'NOTYPE'
1293
+ s.bind = 'GLOBAL'
1294
+ s.shndx = 1
1295
+ s.value = v.name
1296
+ @symbols << s
1297
+ end
1298
+ if v.has_attribute 'import' or ln = v.has_attribute_var('import_from')
1299
+ (@tag['NEEDED'] ||= []) << ln if ln and not @tag['NEEDED'].to_a.include? ln
1300
+ s = Symbol.new
1301
+ s.name = v.name
1302
+ s.type = v.type.kind_of?(C::Function) ? 'FUNC' : 'NOTYPE'
1303
+ s.bind = 'GLOBAL'
1304
+ s.shndx = 'UNDEF'
1305
+ @symbols << s
1306
+ end
1307
+ if v.has_attribute('init') or v.has_attribute('constructor')
1308
+ (@tag['INIT_ARRAY'] ||= []) << v.name
1309
+ end
1310
+ if v.has_attribute('fini') or v.has_attribute('destructor')
1311
+ (@tag['FINI_ARRAY'] ||= []) << v.name
1312
+ end
1313
+ if v.has_attribute 'entrypoint'
1314
+ @header.entry = v.name
1315
+ end
1316
+ }
1317
+ end
1318
+
1319
+ def c_set_default_entrypoint
1320
+ return if @header.entry
1321
+ if @sections.find { |s| s.encoded and s.encoded.export['_start'] }
1322
+ @header.entry = '_start'
1323
+ elsif @sections.find { |s| s.encoded and s.encoded.export['main'] }
1324
+ # entrypoint stack: [sp] = argc, [sp+1] = argv0, [sp+2] = argv1, [sp+argc+1] = 0, [sp+argc+2] = envp0, etc
1325
+ case @cpu.shortname
1326
+ when 'ia32'; assemble <<EOS
1327
+ _start:
1328
+ mov eax, [esp]
1329
+ lea ecx, [esp+4+4*eax+4]
1330
+ push ecx
1331
+ lea ecx, [esp+4+4]
1332
+ push ecx
1333
+ push eax
1334
+ call main
1335
+ push eax
1336
+ call _exit
1337
+ EOS
1338
+ when 'x64'; assemble <<EOS
1339
+ _start:
1340
+ mov rdi, [rsp]
1341
+ lea rsi, [rsp+8]
1342
+ lea rdx, [rsi+8*rdi+8]
1343
+ call main
1344
+ mov rdi, rax
1345
+ call _exit
1346
+ EOS
1347
+ else compile_c <<EOS
1348
+ void _exit(int);
1349
+ int main(int, char**, char**);
1350
+ void _start(void) {
1351
+ _exit(main(0, 0, 0));
1352
+ }
1353
+ EOS
1354
+ end
1355
+ @header.entry = '_start'
1356
+ end
1357
+ end
1358
+ end
1359
+ end
1360
+
1361
+ __END__
1362
+ elf.assemble Ia32.new, <<EOS
1363
+ .text ; @sections << Section.new('.text', ['r' 'x'])
1364
+ .global "foo" foo foo_end ; @symbols ||= [0] << Symbol.new(global, '.foo', addr=foo, size=foo_end - foo)
1365
+ .global "bla" plt=bla_plt
1366
+ .needed 'libc.so.6' ; @tag['NEEDED'] ||= [] << 'libc.so.6'
1367
+ .soname 'lolol' ; @tag['SONAME'] = 'lolol'
1368
+ .interp nil ; @segments.delete_if { |s| s.type == 'INTERP' } ; @sections.delete_if { |s| s.name == '.interp' && vaddr = seg.vaddr etc }
1369
+
1370
+ foo:
1371
+ inc eax
1372
+ call bla_plt
1373
+ ret
1374
+ foo_end:
1375
+ EOS