rocky / elisp-decompile

Emacs Lisp Decompiler
GNU General Public License v3.0
29 stars 4 forks source link

disassemble-file only processes first code block in .elc file #4

Closed danmcmahill closed 4 years ago

danmcmahill commented 4 years ago

TL;DR - only first block of code is disassembled by M-x disassemble-file even if there are multiple (byte-code ...) blocks along with various (defvar ...), (defconst ...), etc in the .elc file. Is there a way to easily process each block in the file?


There is an emacs major mode I use but unfortunately a disk crash apparently claimed the sources about 25 years ago. It is limping along but currently I can only make it run in xemacs and that is with hacking the .elc file somewhere where there is a check against emacs version. elisp-decompile sounded like it might be helpful to bring this code back to life.

When I try the first step of disassembling using M-x disassemble-file I get a disassembled version of just that check for emacs version. If I remove those lines from the file I get the disassembled code for loading font-lock. If I strip those out to the first (byte-code I get the first function. Since each of the .elc files seems to have many byte-code, defconst, defvar, fset along with the random autoload and the (if (and (boundp 'emacs-version... stuff,

beginning of one of the .elc files in question:

;ELC^S^@^@^@
;;; compiled by...
;;; emacs version 19.13 XEmacs Lucid.
;;; byptecomp version 2.25; 1-Sep-94
;;; optimization is on.
;;; this file uses opcodes which do not exist in Emacs 18.

(if (and (boundp 'emacs-version)
     (or (and (boundp 'epoch::version) epoch::version)
         (string-lessp emacs-version "19")))
    (error "This file was compiled for Emacs 19."))

(autoload 'turn-on-font-lock "font-lock" "\
" t)
(autoload 'fast-lock-mode "fast-lock" "\
" t)
(autoload 'turn-on-fast-lock "fast-lock" "\
" t)
(autoload 'lazy-lock-mode "lazy-lock" "\
" t)
(autoload 'turn-on-lazy-lock "lazy-lock" "\
" t)
(autoload 'turn-on-face-lock "face-lock" "\
" t)
(autoload 'face-lock-mode "face-lock" "\
" t)
(byte-code .........

disassembly gives:

byte code:
  args: nil
0       constant  boundp
1       constant  emacs-version
2       call      1
3       goto-if-nil-else-pop 3
6       constant  boundp
7       constant  epoch::version
8       call      1
9       goto-if-nil 1
12      varref    epoch::version
13      goto-if-not-nil 2
16:1    varref    emacs-version
17      constant  "19"
18      string<   
19      goto-if-nil-else-pop 3
22:2    constant  error
23      constant  "This file was compiled for Emacs 19."
24      call      1
25:3    return    

if I strip out that first block of code from the .elc file I get:

byte code:
  args: nil
0       constant  autoload
1       constant  turn-on-font-lock
2       constant  "font-lock"
3       constant  ""
4       constant  t
5       call      4
6       return    

If I am able to get past this, I may still have issues in that I can't load the .elc file in emacs-26 but I do have one I can load in xemacs-21 but don't seem to have 'macroexp on xemacs-21.

rocky commented 4 years ago

I am not going to have much time in the next couple of weeks to look at anything in detail, but one obvious suggestion is just to wrap everything in a progn, e.g.:

;ELC^S^@^@^@
;;; compiled by...
;;; emacs version 19.13 XEmacs Lucid.
;;; byptecomp version 2.25; 1-Sep-94
;;; optimization is on.
;;; this file uses opcodes which do not exist in Emacs 18.

(progn ;; added this
(if (and (boundp 'emacs-version)
     (or (and (boundp 'epoch::version) epoch::version)
         (string-lessp emacs-version "19")))
    (error "This file was compiled for Emacs 19."))

(autoload 'turn-on-font-lock "font-lock" "\
" t)
(autoload 'fast-lock-mode "fast-lock" "\
" t)
(autoload 'turn-on-fast-lock "fast-lock" "\
" t)
(autoload 'lazy-lock-mode "lazy-lock" "\
" t)
(autoload 'turn-on-lazy-lock "lazy-lock" "\
" t)
(autoload 'turn-on-face-lock "face-lock" "\
" t)
(autoload 'face-lock-mode "face-lock" "\
" t)
(byte-code .........
) ;; added this
danmcmahill commented 4 years ago

That's a good idea. It seems to partially work. The whole file seems to get processed although it appears that the byte-code parts didn't actually get disassembled. In other words, the generated LAP turned all of those initial autoloads into things like:

47      constant  autoload
48      constant  lazy-lock-mode
49      constant  "lazy-lock"
50      constant  ""
51      constant  t
52      call      4
53      discard

but the byte-code blocks produced things like

75      constant  byte-code
76      constant  "\300\301\302\"\210\303\304!\210\303\305!\210\306^V^G\310^V   \312^V^K\314^V^M\316^V^O\320^V^Q\322^V^S\300\207"
77      constant  [autoload Edit-options-mode "options" load "skill-defaults" "skill_loaddefs" "*Help*" il-help "*Warnings*" il-warning "*Hierarchy*" il-hierarchy "N/A" il-not-applicable "" il-empty-string ";\n; LOCAL FUNCTION" il-header-symbol-headline "Revisions:" il-header-revision-headline]
78      constant  3
79      call      3
80      discard
rocky commented 4 years ago

I believe that if you can arrange to call disassemble-1 from disass.el after converting this to a code object it will get disassembled.

I know disassemble() and disassemble-full will recurse if it finds code objects inside the constants vector such as you would find in say the bytecode for:

(defun test-define-minor-mode()
  (define-minor-mode test-minor-mode "Testing" nil))

I'm guessing that the code could be extended to look for code-objects outside of the code vector and do the same thing.

If you do work out something, consider contributing back.

danmcmahill commented 4 years ago

Thanks, your fix did in fact solve this completely. If I'm able to fix anything myself, I'll certainly contribute back.