Sorted out section classification.
[cl-elf.git] / elf-rw.lisp
blobd3f00fe8de4b4e2e2debbab7dad4e105ddd8b7c7
1 ;;;; Silly emacs, this is -*- Lisp -*-
3 (in-package :cl-elf)
5 (defmacro with-gensyms ((&rest names) &body body)
6 `(let ,(loop for n in names collect `(,n (make-symbol ,(string n))))
7 ,@body))
9 ;; length of elf magic bytes
10 (defconstant +EI_NIDENT+ 16)
12 ;; These constants are for the segment types stored in the image headers
13 (defconstant +PT_NULL+ 0)
14 (defconstant +PT_LOAD+ 1)
15 (defconstant +PT_DYNAMIC+ 2)
16 (defconstant +PT_INTERP+ 3)
17 (defconstant +PT_NOTE+ 4)
18 (defconstant +PT_SHLIB+ 5)
19 (defconstant +PT_PHDR+ 6)
20 (defconstant +PT_TLS+ 7) ;; Thread local storage segment
21 (defconstant +PT_LOOS+ #X60000000) ;; OS-specific
22 (defconstant +PT_HIOS+ #X6fffffff) ;; OS-specific
23 (defconstant +PT_LOPROC+ #X70000000)
24 (defconstant +PT_HIPROC+ #X7fffffff)
25 (defconstant +PT_GNU_EH_FRAME+ #X6474e550)
27 (defconstant +PT_GNU_STACK+ (+ +PT_LOOS+ #X474e551))
29 ;; These constants define the different elf file types
30 (defconstant +ET_NONE+ 0)
31 (defconstant +ET_REL+ 1)
32 (defconstant +ET_EXEC+ 2)
33 (defconstant +ET_DYN+ 3)
34 (defconstant +ET_CORE+ 4)
35 (defconstant +ET_LOPROC+ #xff00)
36 (defconstant +ET_HIPROC+ #xffff)
38 ;; These constants define the various ELF target machines
39 (defconstant +EM_NONE+ 0)
40 (defconstant +EM_M32+ 1)
41 (defconstant +EM_SPARC+ 2)
42 (defconstant +EM_386+ 3)
43 (defconstant +EM_68K+ 4)
44 (defconstant +EM_88K+ 5)
45 (defconstant +EM_486+ 6) ;; Perhaps disused
46 (defconstant +EM_860+ 7)
47 (defconstant +EM_MIPS+ 8) ;; MIPS R3000 (officially, big-endian only)
48 (defconstant +EM_MIPS_RS4_BE+ 10) ;; MIPS R4000 big-endian
49 (defconstant +EM_PARISC+ 15) ;; HPPA
50 (defconstant +EM_SPARC32PLUS+ 18) ;; Sun's "v8plus"
51 (defconstant +EM_PPC+ 20) ;; PowerPC
52 (defconstant +EM_PPC64+ 21) ;; PowerPC64
53 (defconstant +EM_SH+ 42) ;; SuperH
54 (defconstant +EM_SPARCV9+ 43) ;; SPARC v9 64-bit
55 (defconstant +EM_IA_64+ 50) ;; HP/Intel IA-64
56 (defconstant +EM_X86_64+ 62) ;; AMD x86-64
57 (defconstant +EM_S390+ 22) ;; IBM S/390
58 (defconstant +EM_CRIS+ 76) ;; Axis Communications 32-bit embedded processor
59 (defconstant +EM_V850+ 87) ;; NEC v850
61 (defconstant +EM_M32R+ 88) ;; Renesas M32R
63 (defconstant +EM_H8_300+ 46) ;; Renesas H8/300,300H,H8S
66 ;; This is an interim value that we will use until the committee comes
67 ;; up with a final number.
69 (defconstant +EM_ALPHA+ #x9026)
71 ;; Bogus old v850 magic number, used by old tools.
72 (defconstant +EM_CYGNUS_V850+ #x9080)
74 ;; Bogus old m32r magic number, used by old tools.
75 (defconstant +EM_CYGNUS_M32R+ #x9041)
78 ;; This is the old interim value for S/390 architecture
80 (defconstant +EM_S390_OLD+ #xA390)
82 (defconstant +EM_FRV+ #x5441) ;; Fujitsu FR-V
84 ;; Legal values for sh_type (section type).
86 (defconstant +SHT_NULL+ 0) ;; Section header table entry unused
87 (defconstant +SHT_PROGBITS+ 1) ;; Program data
88 (defconstant +SHT_SYMTAB+ 2) ;; Symbol table
89 (defconstant +SHT_STRTAB+ 3) ;; String table
90 (defconstant +SHT_RELA+ 4) ;; Relocation entries with addends
91 (defconstant +SHT_HASH+ 5) ;; Symbol hash table
92 (defconstant +SHT_DYNAMIC+ 6) ;; Dynamic linking information
93 (defconstant +SHT_NOTE+ 7) ;; Notes
94 (defconstant +SHT_NOBITS+ 8) ;; Program space with no data (bss)
95 (defconstant +SHT_REL+ 9) ;; Relocation entries, no addends
96 (defconstant +SHT_SHLIB+ 10) ;; Reserved
97 (defconstant +SHT_DYNSYM+ 11) ;; Dynamic linker symbol table
98 (defconstant +SHT_INIT_ARRAY+ 14) ;; Array of constructors
99 (defconstant +SHT_FINI_ARRAY+ 15) ;; Array of destructors
100 (defconstant +SHT_PREINIT_ARRAY+ 16) ;; Array of pre-constructors
101 (defconstant +SHT_GROUP+ 17) ;; Section group
102 (defconstant +SHT_SYMTAB_SHNDX+ 18) ;; Extended section indeces
103 (defconstant +SHT_NUM+ 19) ;; Number of defined types.
104 (defconstant +SHT_LOOS+ #X60000000) ;; Start OS-specific
105 (defconstant +SHT_GNU_LIBLIST+ #X6ffffff7) ;; Prelink library list
106 (defconstant +SHT_CHECKSUM+ #X6ffffff8) ;; Checksum for DSO content.
107 (defconstant +SHT_LOSUNW+ #X6ffffffa) ;; Sun-specific low bound.
108 (defconstant +SHT_SUNW_MOVE+ #X6ffffffa)
109 (defconstant +SHT_SUNW_COMDAT+ #X6ffffffb)
110 (defconstant +SHT_SUNW_SYMINFO+ #X6ffffffc)
111 (defconstant +SHT_GNU_VERDEF+ #X6ffffffd) ;; Version definition section.
112 (defconstant +SHT_GNU_VERNEED+ #X6ffffffe) ;; Version needs section.
113 (defconstant +SHT_GNU_VERSYM+ #X6fffffff) ;; Version symbol table.
114 (defconstant +SHT_HISUNW+ #X6fffffff) ;; Sun-specific high bound.
115 (defconstant +SHT_HIOS+ #X6fffffff) ;; End OS-specific type
116 (defconstant +SHT_LOPROC+ #X70000000) ;; Start of processor-specific
117 (defconstant +SHT_HIPROC+ #X7fffffff) ;; End of processor-specific
118 (defconstant +SHT_LOUSER+ #X80000000) ;; Start of application-specific
119 (defconstant +SHT_HIUSER+ #X8fffffff) ;; End of application-specific
121 ;; This is the info that is needed to parse the dynamic section of the file
122 (defconstant +DT_NULL+ 0)
123 (defconstant +DT_NEEDED+ 1)
124 (defconstant +DT_PLTRELSZ+ 2)
125 (defconstant +DT_PLTGOT+ 3)
126 (defconstant +DT_HASH+ 4)
127 (defconstant +DT_STRTAB+ 5)
128 (defconstant +DT_SYMTAB+ 6)
129 (defconstant +DT_RELA+ 7)
130 (defconstant +DT_RELASZ+ 8)
131 (defconstant +DT_RELAENT+ 9)
132 (defconstant +DT_STRSZ+ 10)
133 (defconstant +DT_SYMENT+ 11)
134 (defconstant +DT_INIT+ 12)
135 (defconstant +DT_FINI+ 13)
136 (defconstant +DT_SONAME+ 14)
137 (defconstant +DT_RPATH+ 15)
138 (defconstant +DT_SYMBOLIC+ 16)
139 (defconstant +DT_REL+ 17)
140 (defconstant +DT_RELSZ+ 18)
141 (defconstant +DT_RELENT+ 19)
142 (defconstant +DT_PLTREL+ 20)
143 (defconstant +DT_DEBUG+ 21)
144 (defconstant +DT_TEXTREL+ 22)
145 (defconstant +DT_JMPREL+ 23)
146 (defconstant +DT_LOPROC+ #x70000000)
147 (defconstant +DT_HIPROC+ #x7fffffff)
149 ;; This info is needed when parsing the symbol table
150 (defconstant +STB_LOCAL+ 0)
151 (defconstant +STB_GLOBAL+ 1)
152 (defconstant +STB_WEAK+ 2)
154 (defconstant +STT_NOTYPE+ 0)
155 (defconstant +STT_OBJECT+ 1)
156 (defconstant +STT_FUNC+ 2)
157 (defconstant +STT_SECTION+ 3)
158 (defconstant +STT_FILE+ 4)
159 (defconstant +STT_COMMON+ 5)
160 (defconstant +STT_TLS+ 6)
162 ;; Legal values for sh_flags (section flags).
164 (defparameter +SHF_WRITE+ (ASH 1 0)) ;; Writable
165 (defparameter +SHF_ALLOC+ (ASH 1 1)) ;; Occupies memory during execution
166 (defparameter +SHF_EXECINSTR+ (ASH 1 2)) ;; Executable
167 (defparameter +SHF_MERGE+ (ASH 1 4)) ;; Might be merged
168 (defparameter +SHF_STRINGS+ (ASH 1 5)) ;; Contains nul-terminated strings
169 (defparameter +SHF_INFO_LINK+ (ASH 1 6)) ;; `sh_info' contains SHT index
170 (defparameter +SHF_LINK_ORDER+ (ASH 1 7)) ;; Preserve order after combining
171 (defparameter +SHF_OS_NONCONFORMING+ (ASH 1 8)) ;; Non-standard OS specific handling required
172 (defparameter +SHF_GROUP+ (ASH 1 9)) ;; Section is member of a group.
173 (defparameter +SHF_TLS+ (ASH 1 10)) ;; Section hold thread-local data.
174 (defparameter +SHF_MASKOS+ #xff00000) ;; OS-specific.
175 (defparameter +SHF_MASKPROC+ #xf0000000) ;; Processor-specific
176 (defparameter +SHF_ORDERED+ (ASH 1 30)) ;; Special ordering requirement (Solaris).
177 (defparameter +SHF_EXCLUDE+ (ASH 1 31)) ;; Section is excluded unless referenced or allocated (Solaris).
179 ;; Intel 80386 specific definitions.
181 ;; i386 relocs.
183 (defconstant +R_386_NONE+ 0) ; No reloc
184 (defconstant +R_386_32+ 1) ; Direct 32 bit
185 (defconstant +R_386_PC32+ 2) ; PC relative 32 bit
186 (defconstant +R_386_GOT32+ 3) ; 32 bit GOT entry
187 (defconstant +R_386_PLT32+ 4) ; 32 bit PLT address
188 (defconstant +R_386_COPY+ 5) ; Copy symbol at runtime
189 (defconstant +R_386_GLOB_DAT+ 6) ; Create GOT entry
190 (defconstant +R_386_JMP_SLOT+ 7) ; Create PLT entry
191 (defconstant +R_386_RELATIVE+ 8) ; Adjust by program base
192 (defconstant +R_386_GOTOFF+ 9) ; 32 bit offset to GOT
193 (defconstant +R_386_GOTPC+ 10) ; 32 bit PC relative offset to GOT
194 (defconstant +R_386_32PLT+ 11)
195 (defconstant +R_386_TLS_TPOFF+ 14) ; Offset in static TLS block
196 (defconstant +R_386_TLS_IE+ 15) ; Address of GOT entry for static TLS block offset
197 (defconstant +R_386_TLS_GOTIE+ 16) ; GOT entry for static TLS block offset
198 (defconstant +R_386_TLS_LE+ 17) ; Offset relative to static TLS block
199 (defconstant +R_386_TLS_GD+ 18) ; Direct 32 bit for GNU version of general dynamic thread local data
200 (defconstant +R_386_TLS_LDM+ 19) ; Direct 32 bit for GNU version of local dynamic thread local data in LE code
201 (defconstant +R_386_16+ 20)
202 (defconstant +R_386_PC16+ 21)
203 (defconstant +R_386_8+ 22)
204 (defconstant +R_386_PC8+ 23)
205 (defconstant +R_386_TLS_GD_32+ 24) ; Direct 32 bit for general dynamic thread local data
206 (defconstant +R_386_TLS_GD_PUSH+ 25) ; Tag for pushl in GD TLS code
207 (defconstant +R_386_TLS_GD_CALL+ 26) ; Relocation for call to __tls_get_addr()
208 (defconstant +R_386_TLS_GD_POP+ 27) ; Tag for popl in GD TLS code
209 (defconstant +R_386_TLS_LDM_32+ 28) ; Direct 32 bit for local dynamic thread local data in LE code
210 (defconstant +R_386_TLS_LDM_PUSH+ 29) ; Tag for pushl in LDM TLS code
211 (defconstant +R_386_TLS_LDM_CALL+ 30) ; Relocation for call to __tls_get_addr() in LDM code
212 (defconstant +R_386_TLS_LDM_POP+ 31) ; Tag for popl in LDM TLS code
213 (defconstant +R_386_TLS_LDO_32+ 32) ; Offset relative to TLS block
214 (defconstant +R_386_TLS_IE_32+ 33) ; GOT entry for negated static TLS block offset
215 (defconstant +R_386_TLS_LE_32+ 34) ; Negated offset relative to static TLS block
216 (defconstant +R_386_TLS_DTPMOD32+ 35) ; ID of module containing symbol
217 (defconstant +R_386_TLS_DTPOFF32+ 36) ; Offset in TLS block
218 (defconstant +R_386_TLS_TPOFF32+ 37) ; Negated offset in static TLS block
219 ; Keep this the last entry.
220 (defconstant +R_386_NUM+ 38)
222 (defconstant +null+ (code-char 0))
224 (defun read-file-to-usb8-array (filepath)
225 "Opens a reads a file. Returns the contents as single unsigned-byte array"
226 (with-open-file (in filepath :direction :input :element-type '(unsigned-byte 8))
227 (let* ((file-len (file-length in))
228 (usb8 (make-array file-len :element-type '(unsigned-byte 8)))
229 (pos (read-sequence usb8 in)))
230 (unless (= file-len pos)
231 (error "Length read (~D) doesn't match file length (~D)~%" pos file-len))
232 usb8)))
234 (defun write-usb8-array-to-file (usb8 file)
235 "Opens a reads a file. Returns the contents as single unsigned-byte array"
236 (with-open-file (out file :direction :output
237 :if-exists :supersede
238 :if-does-not-exist :create
239 :element-type '(unsigned-byte 8))
240 (let* ((pos (length (write-sequence usb8 out)))
241 (file-len (file-length out)))
242 (unless (= file-len pos)
243 (error "Length written (~D) doesn't match file length (~D)~%" pos file-len))
244 usb8)))
246 (defclass buffered-data-mixin ()
247 ((buffered-data :accessor buffered-data-of :initform nil)
248 (buffer-pos :accessor buffer-pos-of :initform 0)))
250 (defgeneric advance-data-pointer (buffer advance))
252 (defmethod advance-data-pointer ((buffer buffered-data-mixin) advance)
253 (let
254 ((pos
255 (buffer-pos-of buffer)))
256 (setf (buffer-pos-of buffer)
257 (+ pos advance))))
260 (defmethod read-buffered-data ((self buffered-data-mixin) &key filepath)
261 (format t "Reading buffered data from ~A ~%" filepath)
262 (setf (buffered-data-of self)
263 (read-file-to-usb8-array filepath)))
265 (defgeneric write-buffered-data (data &key filepath)
266 (:documentation "Write the buffered data to the elf file"))
268 (defmethod write-buffered-data ((self buffered-data-mixin) &key filepath)
269 (write-usb8-array-to-file (buffered-data-of self) filepath))
272 (defgeneric read-value (type buffered-data-mixin &key &allow-other-keys)
273 (:documentation "Read a value of the given type from the file."))
276 (defmethod read-value ((type (eql 'u8)) (self buffered-data-mixin) &key)
277 (let ((result (aref (buffered-data-of self) (buffer-pos-of self))))
278 (incf (buffer-pos-of self))
279 result))
281 (defmethod read-value ((type (eql 's8)) (self buffered-data-mixin) &key)
282 (let ((u8 (read-value 'u8 self)))
283 (if (> u8 #X7F)
284 (- u8 #X100)
285 u8)))
287 (defmethod read-value ((type (eql 'u16)) (self buffered-data-mixin) &key)
288 (let ((u16 0))
289 (setf (ldb (byte 8 0) u16) (read-value 'u8 self))
290 (setf (ldb (byte 8 8) u16) (read-value 'u8 self))
291 u16))
293 (defmethod read-value ((type (eql 's16)) (self buffered-data-mixin) &key)
294 (let ((u16 (read-value 'u16 self)))
295 (if (> u16 #X7FFFF)
296 (- u16 #X10000))))
298 (defmethod read-value ((type (eql 'u32)) (self buffered-data-mixin) &key)
299 (let ((u32 0))
300 (setf (ldb (byte 8 0) u32) (read-value 'u8 self))
301 (setf (ldb (byte 8 8) u32) (read-value 'u8 self))
302 (setf (ldb (byte 8 16) u32) (read-value 'u8 self))
303 (setf (ldb (byte 8 24) u32) (read-value 'u8 self))
304 u32))
306 (defmethod read-value ((type (eql 's32)) (self buffered-data-mixin) &key)
307 (let ((u32 (read-value 'u32 self)))
308 (if (> u32 #X7FFFF)
309 (- u32 #X100000000)
310 u32)))
312 (defgeneric align-for-read (buffered-data-mixin alignment))
314 (defmethod align-for-read ((self buffered-data-mixin) alignment)
315 (loop
316 until (zerop (logand (buffer-pos-of self) (1- alignment)))
318 (incf (buffer-pos-of self))))
321 (defmacro def-read-elf-value (type base-type alignment)
322 `(defmethod read-value ((type (eql ',type)) (self buffered-data-mixin) &key)
323 (progn
324 (align-for-read self ,alignment)
325 (read-value ',base-type self))))
327 (def-read-elf-value elf32-addr u32 4)
328 (def-read-elf-value elf32-half u16 2)
329 (def-read-elf-value elf32-off u32 4)
330 (def-read-elf-value elf32-sword s32 4)
331 (def-read-elf-value elf32-word u32 4)
333 (defmethod read-value ((type (eql 'asciiz)) (self buffered-data-mixin) &key)
334 (with-output-to-string (s)
335 (loop for char = (code-char (read-value 'u8 self))
336 until (char= char +null+) do (write-char char s))))
338 (defmethod read-value ((type (eql 'usb8-array)) (self buffered-data-mixin) &key length)
339 (let ((usb8-array (make-array (list length) :element-type '(unsigned-byte 8))))
340 (loop
341 for index from 0 below length
343 (setf (aref usb8-array index) (read-value 'u8 self)))
344 usb8-array))
346 ;; -- writing to elf files -----------------------------------------
348 (defgeneric write-value (type buffered-data-mixin value &key &allow-other-keys)
349 (:documentation "Read a value of the given type from the file."))
351 (defmethod write-value ((type (eql 'u8)) (self buffered-data-mixin) value &key)
352 (vector-push-extend value (buffered-data-of self)))
354 (defmethod write-value ((type (eql 's8)) (self buffered-data-mixin) value &key)
355 (vector-push-extend value (buffered-data-of self)))
357 (defmethod write-value ((type (eql 'u16)) (self buffered-data-mixin) value &key)
358 (vector-push-extend (ldb (byte 8 0) value)
359 (buffered-data-of self))
360 (vector-push-extend (ldb (byte 8 8) value)
361 (buffered-data-of self)))
363 (defmethod write-value ((type (eql 's16)) (self buffered-data-mixin) value &key)
364 (vector-push-extend (ldb (byte 8 0) value)
365 (buffered-data-of self))
366 (vector-push-extend (ldb (byte 8 8) value)
367 (buffered-data-of self)))
369 (defmethod write-value ((type (eql 'u32)) (self buffered-data-mixin) value &key)
370 (vector-push-extend (ldb (byte 8 0) value) (buffered-data-of self))
371 (vector-push-extend (ldb (byte 8 8) value) (buffered-data-of self))
372 (vector-push-extend (ldb (byte 8 16) value) (buffered-data-of self))
373 (vector-push-extend (ldb (byte 8 24) value) (buffered-data-of self)))
375 (defmethod write-value ((type (eql 's32)) (self buffered-data-mixin) value &key)
376 (vector-push-extend (ldb (byte 8 0) value) (buffered-data-of self))
377 (vector-push-extend (ldb (byte 8 8) value) (buffered-data-of self))
378 (vector-push-extend (ldb (byte 8 16) value) (buffered-data-of self))
379 (vector-push-extend (ldb (byte 8 24) value) (buffered-data-of self)))
381 (defmethod write-value ((type (eql 'asciiz)) (self buffered-data-mixin) string &key)
382 (loop
383 for char across string
384 do (vector-push-extend
385 (char-code char)
386 (buffered-data-of self))
387 (vector-push-extend (char-code +null+) (buffered-data-of self))))
389 (defgeneric align-for-write (buffered-data-mixin alignment))
391 (defmethod align-for-write ((self buffered-data-mixin) alignment)
392 (loop
393 until (zerop (logand (length (buffered-data-of self)) (1- alignment)))
395 (vector-push-extend 0 (buffered-data-of self))))
397 (defmacro def-write-elf-value (type base-type alignment)
398 `(defmethod write-value ((type (eql ',type)) (self buffered-data-mixin) value &key)
399 (progn
400 (align-for-read self ,alignment)
401 (write-value ',base-type self value))))
403 (def-write-elf-value elf32-addr u32 4)
404 (def-write-elf-value elf32-half u16 2)
405 (def-write-elf-value elf32-off u32 4)
406 (def-write-elf-value elf32-sword s32 4)
407 (def-write-elf-value elf32-word u32 4)
409 (defmethod write-value ((type (eql 'usb8-array))
410 (self buffered-data-mixin) usb8-array &key length)
411 (loop
412 for index from 0 below length
414 (vector-push-extend (aref usb8-array index) (buffered-data-of self))))
417 ;; define-elf-class ------------------------------------------------------
418 ;; Heavily influenced by (stolen, really) from Practical Common Lisp
420 (defun as-keyword (sym)
421 (intern (string sym) :keyword))
423 (defun as-accessor (sym)
424 (intern (concatenate 'string (string sym) "-OF")))
427 (defun slot->defclass-slot (spec)
428 "Map a define-elf-class slot spec to a clos slot spec"
429 (let ((name (first spec)))
430 `(,name :initarg ,(as-keyword name) :accessor ,(as-accessor name))))
432 (defun mklist (x)
433 "Return the argument as a list if it isn't already"
434 (if (listp x) x (list x)))
436 (defun normalize-slot-spec (spec)
437 (list (first spec) (mklist (second spec))))
439 (defun slot->read-value (spec elf-data)
440 (destructuring-bind (name (type &rest args)) (normalize-slot-spec spec)
441 `(setf ,name (read-value ',type ,elf-data ,@args))))
443 (defun slot->write-value (spec elf-data)
444 (destructuring-bind (name (type &rest args)) (normalize-slot-spec spec)
445 `(write-value ',type ,elf-data ,name ,@args)))
447 (defun normalize-slot-description (spec)
448 (list (first spec)
449 (mklist (second spec))))
451 (defmacro define-elf-class (name slots)
452 (with-gensyms (typevar objectvar elf-data-var)
453 `(progn
454 ;; generate a defclass form
455 (defclass ,name ()
456 ,(mapcar #'slot->defclass-slot slots))
457 ;; generate a method to read all slots
458 (defmethod read-value ((,typevar (eql ',name)) ,elf-data-var &key)
459 (let ((,objectvar (make-instance ',name)))
460 (with-slots ,(mapcar #'first slots) ,objectvar
461 ,@(mapcar #'(lambda (x) (slot->read-value x elf-data-var)) slots))
462 ,objectvar))
463 ;; generate a method to write all slots
464 (defmethod write-value ((,typevar (eql ',name)) ,elf-data-var ,objectvar &key)
465 (with-slots ,(mapcar #'first slots) ,objectvar
466 ,@(mapcar #'(lambda (x) (slot->write-value x elf-data-var)) slots))))))