1 ;;;; Silly emacs, this is -*- Lisp -*-
5 (defmacro with-gensyms
((&rest names
) &body body
)
6 `(let ,(loop for n in names collect
`(,n
(make-symbol ,(string n
))))
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.
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
))
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
))
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
)
255 (buffer-pos-of buffer
)))
256 (setf (buffer-pos-of buffer
)
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
))
281 (defmethod read-value ((type (eql 's8
)) (self buffered-data-mixin
) &key
)
282 (let ((u8 (read-value 'u8 self
)))
287 (defmethod read-value ((type (eql 'u16
)) (self buffered-data-mixin
) &key
)
289 (setf (ldb (byte 8 0) u16
) (read-value 'u8 self
))
290 (setf (ldb (byte 8 8) u16
) (read-value 'u8 self
))
293 (defmethod read-value ((type (eql 's16
)) (self buffered-data-mixin
) &key
)
294 (let ((u16 (read-value 'u16 self
)))
298 (defmethod read-value ((type (eql 'u32
)) (self buffered-data-mixin
) &key
)
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
))
306 (defmethod read-value ((type (eql 's32
)) (self buffered-data-mixin
) &key
)
307 (let ((u32 (read-value 'u32 self
)))
312 (defgeneric align-for-read
(buffered-data-mixin alignment
))
314 (defmethod align-for-read ((self buffered-data-mixin
) alignment
)
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
)
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))))
341 for index from
0 below length
343 (setf (aref usb8-array index
) (read-value 'u8 self
)))
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
)
383 for char across string
384 do
(vector-push-extend
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
)
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
)
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
)
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
))))
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)
449 (mklist (second spec
))))
451 (defmacro define-elf-class
(name slots
)
452 (with-gensyms (typevar objectvar elf-data-var
)
454 ;; generate a defclass form
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
))
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
))))))