;;; Copyright 2009 Clozure Associates ;;; This file is part of Clozure CL. ;;; ;;; Clozure CL is licensed under the terms of the Lisp Lesser GNU ;;; Public License , known as the LLGPL and distributed with Clozure ;;; CL as the file "LICENSE". The LLGPL consists of a preamble and ;;; the LGPL, which is distributed with Clozure CL as the file "LGPL". ;;; Where these conflict, the preamble takes precedence. ;;; ;;; Clozure CL is referenced in the preamble as the "LIBRARY." ;;; ;;; The LLGPL is also available online at ;;; http://opensource.franz.com/preamble.html (in-package "CCL") (eval-when (:compile-toplevel :execute) (use-interface-dir :elf)) (defloadvar *readonly-area* (do-consing-areas (a) (when (eql (%fixnum-ref a target::area.code) ccl::area-readonly) (return a)))) ;;; String tables: used both for symbol names and for section names. (defstruct elf-string-table (hash (make-hash-table :test #'equal)) (string (make-array 100 :element-type '(unsigned-byte 8) :fill-pointer 1 :adjustable t))) ;;; Collect info about Elf symbols. (defstruct elf-symbol-table (strings (make-elf-string-table)) data ; foreign pointer nsyms ) ;;; Wrapper around libelf's "elf" pointer (defstruct elf-object libelf-pointer fd pathname ) ;;; Is libelf thread-safe ? Who knows, there's no ;;; documentation ... (defun libelf-error-string (&optional (errnum -1)) (let* ((p (#_elf_errmsg errnum))) (if (%null-ptr-p p) (format nil "ELF error ~d" errnum) (%get-cstring p)))) (defloadvar *checked-libelf-version* nil) (defun check-libelf-version () (or *checked-libelf-version* (progn (open-shared-library "libelf.so") (let* ((version (#_elf_version #$EV_CURRENT))) (if (eql #$EV_NONE version) (error "ELF library initialization failed: ~a" (libelf-error-string))) (setq *checked-libelf-version* version))))) ;;; Prepate to create an ELF object file at PATHNAME, overwriting ;;; whatever might have been there. (defun create-elf-object (pathname) (let* ((namestring (native-translated-namestring pathname)) (fd (ccl::fd-open namestring (logior #$O_RDWR #$O_CREAT #$O_TRUNC) #o755))) (if (< fd 0) (signal-file-error fd pathname) (progn (check-libelf-version) (let* ((ptr (#_elf_begin fd #$ELF_C_WRITE +null-ptr+))) (if (%null-ptr-p ptr) (error "Can't initialize libelf object for ~s: ~a" pathname (libelf-error-string)) (make-elf-object :libelf-pointer (assert-pointer-type ptr :lf) :fd fd :pathname pathname))))))) (defun elf-end (object) (#_elf_end (elf-object-libelf-pointer object)) (setf (elf-object-libelf-pointer object) nil (elf-object-fd object) nil)) (defun new-elf-file-header (object format type machine) (let* ((ehdr (#+64-bit-target #_elf64_newehdr #+32-bit-target #_elf32_newehdr (elf-object-libelf-pointer object)))) (if (%null-ptr-p ehdr) (error "Can't create ELF file header for ~s: ~a" (elf-object-pathname object) (libelf-error-string)) (progn (setf (paref (pref ehdr #+64-bit-target :lf64_hdr.e_ident #+32-bit-target :lf32_hdr.e_ident) (:* :unsigned-char) #$EI_DATA) format (pref ehdr #+64-bit-target :lf64_hdr.e_machine #+32-bit-target :lf32_hdr.e_machine) machine (pref ehdr #+64-bit-target :lf64_hdr.e_type #+32-bit-target :lf32_hdr.e_type) type (pref ehdr #+64-bit-target :lf64_hdr.e_version #+32-bit-target :lf32_hdr.e_version) *checked-libelf-version*) (assert-pointer-type ehdr #+64-bit-target :lf64_hdr #+32-bit-target :lf32_hdr))))) (defun new-elf-program-header (object &optional (count 1)) (let* ((phdr (#+64-bit-target #_elf64_newphdr #+32-bit-target #_elf32_newphdr (elf-object-libelf-pointer object) count))) (if (%null-ptr-p phdr) (error "Can't create ELF program header for ~s: ~a" (elf-object-pathname object) (libelf-error-string)) (assert-pointer-type phdr #+64-bit-target :lf64_

hdr #+32-bit-target :lf32_

hdr)))) (defun new-elf-section (object) (let* ((scn (#_elf_newscn (elf-object-libelf-pointer object)))) (if (%null-ptr-p scn) (error "Can' create ELF section for ~s: ~a" (elf-object-pathname object) (libelf-error-string)) (assert-pointer-type scn :lf_cn)))) (defun elf-section-header-for-section (object section) (let* ((shdr (#+64-bit-target #_elf64_getshdr #+32-bit-target #_elf32_getshdr section))) (if (%null-ptr-p shdr) (error "Can' obtain ELF section header for ~s: ~a" (elf-object-pathname object) (libelf-error-string)) (assert-pointer-type shdr #+64-bit-target :lf64_hdr #+32-bit-target :lf32_hdr)))) (defun elf-data-pointer-for-section (object section) (let* ((data (#_elf_newdata section))) (if (%null-ptr-p data) (error "Can' obtain ELF data pointer for ~s: ~a" (elf-object-pathname object) (libelf-error-string)) (assert-pointer-type data :lf_ata)))) (defun elf-register-string (string table) (let* ((hash (elf-string-table-hash table)) (s (elf-string-table-string table))) (when (gethash string hash) (format t "~& duplicate: ~s" string)) (or (gethash string hash) (setf (gethash string hash) (let* ((n (length s))) (dotimes (i (length string) (progn (vector-push-extend 0 s) n)) (let* ((code (char-code (char string i)))) (declare (type (mod #x110000) code)) (if (> code 255) (vector-push-extend (char-code #\sub) s) (vector-push-extend code s))))))))) (defun elf-lisp-function-name (f) (let* ((name (function-name f))) (if (and (symbolp name) (eq f (fboundp name))) (with-standard-io-syntax (format nil "~s" name)) (let ((str (format nil "~s" f))) (subseq (nsubstitute #\0 #\# (nsubstitute #\. #\Space str)) 1))))) #+x86-target (defun collect-elf-static-functions () (collect ((functions)) (purify) (block walk (%map-areas (lambda (o) (when (typep o #+x8664-target 'function-vector #-x8664-target 'function) (functions (function-vector-to-function o)))) :readonly )) (functions))) #+(or arm-target ppc-target) (defun collect-elf-static-functions () (ccl::purify) (multiple-value-bind (pure-low pure-high) (ccl::do-gc-areas (a) (when (eql(ccl::%fixnum-ref a target::area.code) ccl::area-readonly) (return (values (ash (ccl::%fixnum-ref a target::area.low) target::fixnumshift) (ash (ccl::%fixnum-ref a target::area.active) target::fixnumshift))))) (let* ((hash (make-hash-table :test #'eq)) (code-vector-index #+ppc-target 0 #+arm-target 1)) (ccl::%map-lfuns #'(lambda (f) (let* ((code-vector (ccl:uvref f code-vector-index)) (startaddr (+ (ccl::%address-of code-vector) target::misc-data-offset))) (when (and (>= startaddr pure-low) (< startaddr pure-high)) (push f (gethash code-vector hash)))))) (let* ((n 0)) (declare (fixnum n)) (maphash #'(lambda (k v) (declare (ignore k)) (if (null (cdr v)) (incf n))) hash) (let* ((functions ())) (maphash #'(lambda (k v) (declare (ignore k)) (when (null (cdr v)) (push (car v) functions))) hash) (sort functions #'(lambda (x y) (< (ccl::%address-of (uvref x code-vector-index)) (ccl::%address-of (uvref y code-vector-index)))))))))) (defun register-elf-functions (section-number) (let* ((functions (collect-elf-static-functions)) (n (length functions)) (data (#_calloc (1+ n) (record-length #+64-bit-target :lf64_ym #+32-bit-target :lf32_ym))) (string-table (make-elf-string-table))) (declare (fixnum n)) (do* ((i 0 (1+ i)) (p (%inc-ptr data (record-length #+64-bit-target :lf64_ym #+32-bit-target :lf32_ym)) (progn (%incf-ptr p (record-length #+64-bit-target :lf64_ym #+32-bit-target :lf32_ym)) p)) (f (pop functions) (pop functions))) ((= i n) (make-elf-symbol-table :strings string-table :data data :nsyms n)) (declare (fixnum n)) (setf (pref p #+64-bit-target :lf64_ym.st_name #+32-bit-target :lf32_ym.st_name) (elf-register-string (elf-lisp-function-name f) string-table) (pref p #+64-bit-target :lf64_ym.st_info #+32-bit-target :lf32_ym.st_info) (logior (ash #$STB_GLOBAL 4) #$STT_FUNC) (pref p #+64-bit-target :lf64_ym.st_shndx #+32-bit-target :lf32_ym.st_shndx) section-number (pref p #+64-bit-target :lf64_ym.st_value #+32-bit-target :lf32_ym.st_value) #+x86-target (%address-of f) #+ppc-target (- (%address-of (uvref f 0)) (- ppc::fulltag-misc ppc::node-size)) #+arm-target (- (%address-of (uvref f 1)) (- arm::fulltag-misc arm::node-size)) (pref p #+64-bit-target :lf64_ym.st_size #+32-bit-target :lf32_ym.st_size) #+x86-target (1+ (ash (1- (%function-code-words f)) target::word-shift)) #+ppc-target (ash (uvsize (uvref f 0)) ppc::word-shift) #+arm-target (ash (uvsize (uvref f 1)) arm::word-shift) )))) (defun elf-section-index (section) (#_elf_ndxscn section)) (defun elf-set-shstrab-section (object scn) #+freebsd-target (#_elf_setshstrndx (elf-object-libelf-pointer object) (elf-section-index scn)) #-freebsd-target (declare (ignore object scn))) (defun elf-init-section-data-from-string-table (object section string-table) (let* ((strings-data (elf-data-pointer-for-section object section)) (s (elf-string-table-string string-table)) (bytes (array-data-and-offset s)) (n (length s)) (buf (#_malloc n))) (%copy-ivector-to-ptr bytes 0 buf 0 n) (setf (pref strings-data :lf_ata.d_align) 1 (pref strings-data :lf_ata.d_off) 0 (pref strings-data :lf_ata.d_type) #$ELF_T_BYTE (pref strings-data :lf_ata.d_version) #$EV_CURRENT (pref strings-data :lf_ata.d_size) n (pref strings-data :lf_ata.d_buf) buf) n)) (defun elf-init-symbol-section-from-symbol-table (object section symbols) (let* ((symbols-data (elf-data-pointer-for-section object section)) (buf (elf-symbol-table-data symbols)) (nsyms (elf-symbol-table-nsyms symbols) ) (n (* (1+ nsyms) (record-length #+64-bit-target :lf64_ym #+32-bit-target :lf32_ym)))) (setf (pref symbols-data :lf_ata.d_align) 8 (pref symbols-data :lf_ata.d_off) 0 (pref symbols-data :lf_ata.d_type) #$ELF_T_SYM (pref symbols-data :lf_ata.d_version) #$EV_CURRENT (pref symbols-data :lf_ata.d_size) n (pref symbols-data :lf_ata.d_buf) buf) nsyms)) (defun elf-make-empty-data-for-section (object section &optional (size 0)) (let* ((data (elf-data-pointer-for-section object section)) (buf +null-ptr+)) (setf (pref data :lf_ata.d_align) 0 (pref data :lf_ata.d_off) 0 (pref data :lf_ata.d_type) #$ELF_T_BYTE (pref data :lf_ata.d_version) #$EV_CURRENT (pref data :lf_ata.d_size) size (pref data :lf_ata.d_buf) buf) 0)) (defun elf-flag-phdr (object cmd flags) (#_elf_flagphdr (elf-object-libelf-pointer object) cmd flags)) (defun elf-update (object cmd) (let* ((size (#_elf_update (elf-object-libelf-pointer object) cmd))) (if (< size 0) (error "elf_update failed for for ~s: ~a" (elf-object-pathname object) (libelf-error-string)) size))) (defun fixup-lisp-section-offset (fd eof sectnum) (fd-lseek fd 0 #$SEEK_SET) (rlet ((fhdr #+64-bit-target :lf64_hdr #+32-bit-target :lf32_hdr) (shdr #+64-bit-target :lf64_hdr #+32-bit-target :lf32_hdr)) (fd-read fd fhdr (record-length #+64-bit-target :lf64_hdr #+32-bit-target :lf32_hdr)) (let* ((pos (+ (pref fhdr #+64-bit-target :lf64_hdr.e_shoff #+32-bit-target :lf32_hdr.e_shoff) (* sectnum (pref fhdr #+64-bit-target :lf64_hdr.e_shentsize #+32-bit-target :lf32_hdr.e_shentsize))))) (fd-lseek fd pos #$SEEK_SET) (fd-read fd shdr (record-length #+64-bit-target :lf64_hdr #+32-bit-target :lf32_hdr)) ;; On 64-bit platforms, the section data precedes the image ;; header; on 32-bit platforms, the image header and image ;; section table precede the image data for the first (static) ;; section. With alignment, the header/section headers are ;; one 4K page, and the static section size is 8K ... (setf (pref shdr #+64-bit-target :lf64_hdr.sh_offset #+32-bit-target :lf32_hdr.sh_offset) (+ #+32-bit-target #x1000 #+64-bit-target 0 #x2000 (logandc2 (+ eof 4095) 4095))) (setf (pref shdr #+64-bit-target :lf64_hdr.sh_type #+32-bit-target :lf32_hdr.sh_type) #$SHT_PROGBITS) (fd-lseek fd pos #$SEEK_SET) (fd-write fd shdr (record-length #+64-bit-target :lf64_hdr #+32-bit-target :lf32_hdr)) t))) (defun write-elf-symbols-to-file (pathname) (let* ((object (create-elf-object pathname)) (file-header (new-elf-file-header object #+little-endian-target #$ELFDATA2LSB #+big-endian-target #$ELFDATA2MSB #$ET_DYN #+x8664-target #$EM_X86_64 #+x8632-target #$EM_386 #+ppc32-target #$EM_PPC #+ppc64-target #$EM_PPC64 #+arm-target #$EM_ARM )) (program-header (new-elf-program-header object)) (lisp-section (new-elf-section object)) (symbols-section (new-elf-section object)) (strings-section (new-elf-section object)) (shstrtab-section (new-elf-section object)) (prelink-id-section (new-elf-section object)) (section-names (make-elf-string-table)) (lisp-section-index (elf-section-index lisp-section)) (symbols (register-elf-functions lisp-section-index)) (lisp-section-header (elf-section-header-for-section object lisp-section)) (symbols-section-header (elf-section-header-for-section object symbols-section)) (strings-section-header (elf-section-header-for-section object strings-section)) (shstrtab-section-header (elf-section-header-for-section object shstrtab-section)) (prelink-id-section-header (elf-section-header-for-section object prelink-id-section))) (setf (pref file-header #+64-bit-target :lf64_hdr.e_shstrndx #+32-bit-target :lf32_hdr.e_shstrndx) (elf-section-index shstrtab-section)) (setf (pref lisp-section-header #+64-bit-target :lf64_hdr.sh_name #+32-bit-target :lf32_hdr.sh_name) (elf-register-string ".lisp" section-names) (pref lisp-section-header #+64-bit-target :lf64_hdr.sh_type #+32-bit-target :lf32_hdr.sh_type) #$SHT_NOBITS (pref lisp-section-header #+64-bit-target :lf64_hdr.sh_flags #+32-bit-target :lf32_hdr.sh_flags) (logior #$SHF_WRITE #$SHF_ALLOC #$SHF_EXECINSTR) (pref lisp-section-header #+64-bit-target :lf64_hdr.sh_addr #+32-bit-target :lf32_hdr.sh_addr) (ash (%fixnum-ref *readonly-area* target::area.low) target::fixnumshift) (pref lisp-section-header #+64-bit-target :lf64_hdr.sh_size #+32-bit-target :lf32_hdr.sh_size) (ash (- (%fixnum-ref *readonly-area* target::area.active) (%fixnum-ref *readonly-area* target::area.low) )target::fixnumshift) (pref lisp-section-header #+64-bit-target :lf64_hdr.sh_offset #+32-bit-target :lf32_hdr.sh_offset) 0 (pref lisp-section-header #+64-bit-target :lf64_hdr.sh_addralign #+32-bit-target :lf32_hdr.sh_addralign) 1) (setf (pref symbols-section-header #+64-bit-target :lf64_hdr.sh_name #+32-bit-target :lf32_hdr.sh_name) (elf-register-string ".symtab" section-names) (pref symbols-section-header #+64-bit-target :lf64_hdr.sh_type #+32-bit-target :lf32_hdr.sh_type) #$SHT_SYMTAB (pref symbols-section-header #+64-bit-target :lf64_hdr.sh_entsize #+32-bit-target :lf32_hdr.sh_entsize) (record-length #+64-bit-target :lf64_ym #+32-bit-target :lf32_ym) (pref symbols-section-header #+64-bit-target :lf64_hdr.sh_link #+32-bit-target :lf32_hdr.sh_link) (elf-section-index strings-section)) (setf (pref strings-section-header #+64-bit-target :lf64_hdr.sh_name #+32-bit-target :lf32_hdr.sh_name) (elf-register-string ".strtab" section-names) (pref strings-section-header #+64-bit-target :lf64_hdr.sh_type #+32-bit-target :lf32_hdr.sh_type) #$SHT_STRTAB (pref strings-section-header #+64-bit-target :lf64_hdr.sh_flags #+32-bit-target :lf32_hdr.sh_flags) (logior #$SHF_STRINGS #$SHF_ALLOC)) (setf (pref shstrtab-section-header #+64-bit-target :lf64_hdr.sh_name #+32-bit-target :lf32_hdr.sh_name) (elf-register-string ".shstrtab" section-names) (pref shstrtab-section-header #+64-bit-target :lf64_hdr.sh_type #+32-bit-target :lf32_hdr.sh_type) #$SHT_STRTAB (pref shstrtab-section-header #+64-bit-target :lf64_hdr.sh_flags #+32-bit-target :lf32_hdr.sh_flags) (logior #$SHF_STRINGS #$SHF_ALLOC)) ;; The perf profiler recognizes prelinked libraries by the presence of ;; some section with this exact name; it doesn't care about the section's ;; contents or other attributes, currently. ;; We want that profiler to treat the lisp section as if it was prelinked. (setf (pref prelink-id-section-header #+64-bit-target :lf64_hdr.sh_name #+32-bit-target :lf32_hdr.sh_name) (elf-register-string ".gnu.prelink_undo" section-names)) (elf-make-empty-data-for-section object lisp-section (ash (- (%fixnum-ref *readonly-area* target::area.active) (%fixnum-ref *readonly-area* target::area.low) )target::fixnumshift)) (elf-init-section-data-from-string-table object strings-section (elf-symbol-table-strings symbols)) (elf-init-section-data-from-string-table object shstrtab-section section-names) (elf-init-symbol-section-from-symbol-table object symbols-section symbols) (elf-make-empty-data-for-section object prelink-id-section 0) ;; Prepare in-memory data structures. (elf-update object #$ELF_C_NULL) ;; Fix up the program header. (setf (pref program-header #+64-bit-target :lf64_

hdr.p_type #+32-bit-target :lf32_

hdr.p_type) #$PT_PHDR (pref program-header #+64-bit-target :lf64_

hdr.p_offset #+32-bit-target :lf32_

hdr.p_offset) (pref file-header #+64-bit-target :lf64_hdr.e_phoff #+32-bit-target :lf32_hdr.e_phoff) (pref program-header #+64-bit-target :lf64_

hdr.p_filesz #+32-bit-target :lf32_

hdr.p_filesz) (#+64-bit-target #_elf64_fsize #+32-bit-target #_elf32_fsize #$ELF_T_PHDR 1 #$EV_CURRENT)) ;; Mark the program header as being dirty. (elf-flag-phdr object #$ELF_C_SET #$ELF_F_DIRTY) (let* ((eof (elf-update object #$ELF_C_WRITE)) (fd (elf-object-fd object))) (elf-end object) (fixup-lisp-section-offset fd eof lisp-section-index) (fd-close fd)) pathname))