elf-mode - view the symbol list in a binary28 Aug 2016
Recently, I've been looking at
didn't manage to fully figure out their CMake build system for
tutorials: although each tutorial has a
CMakeLists.txt, it's only
possible to build them all at once.
So I decided to replace
CMakeLists.txt with a good-old
how hard can it be? Concerning includes, not at all hard: the missing
files are found with
counsel-locate and added to the include path.
But I had some trouble matching a missing
ld dependency to a library
file. Fixed it with a bunch of googling and guesswork; I still wonder
if there's a better way. But in the process, I've found this useful
readelf --syms libGL.so
which produces e.g.:
Symbol table '.dynsym' contains 2732 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 000000000004faf0 0 SECTION LOCAL DEFAULT 8 2: 00000000000e8f20 0 FUNC GLOBAL DEFAULT 11 glGetIntegerui64i_vNV 3: 00000000000e13e0 0 FUNC GLOBAL DEFAULT 11 glGetMultiTexEnvfvEXT 4: 00000000000d7440 0 FUNC GLOBAL DEFAULT 11 glProgramUniform2uiv 5: 00000000000cfdc0 0 FUNC GLOBAL DEFAULT 11 glMultiTexCoord3sv
This is a pretty good representation of a binary file: in this example, instead of one megabyte of gibberish I see a bit more than 2732 lines describing the functions this file uses and provides.
Viewing the symbol list automatically
I liked the above representation so much that I want to see it by
default. In Emacs, it's pretty easy to do with
(add-to-list 'auto-mode-alist '("\\.\\(?:a\\|so\\)\\'" . elf-mode))
The above code instructs Emacs to call
elf-mode function whenever
the file name ends in
And here's the body of
(defvar-local elf-mode nil) ;;;###autoload (defun elf-mode () (interactive) (let ((inhibit-read-only t)) (if elf-mode (progn (delete-region (point-min) (point-max)) (insert-file-contents (buffer-file-name)) (setq elf-mode nil)) (setq elf-mode t) (delete-region (point-min) (point-max)) (insert (shell-command-to-string (format "readelf --syms %s" (buffer-file-name))))) (set-buffer-modified-p nil) (read-only-mode 1)))
The idea is very simple:
elf-mode is a toggle function that replaces
the buffer contents with the shell command output. It carefully uses
set-buffer-modified-p so that the file will not
be overwritten by accident with the symbol names.
Using autoload to avoid overhead
As you might imagine, looking at binaries isn't really a common task. Is it worth to be dragging this code around from now on, loading it on each start? The answer is yes, of course. Since the actual cost is negligible until the feature is used.
If you look above,
elf-mode has an
;;;###autoload cookie before
it. The cookie results in this line in my
(autoload 'elf-mode "modes/ora-elf" "" t nil)
init.el always loads
loaddefs.el, but never loads
where the function is defined. That file is only loaded when the
elf-mode is called for the first time. The above
statement simply instructs Emacs to load a particular file when
elf-mode needs to be called.
When you use the package manager, the autoloads file is generated and loaded for you automatically:
$ tree elpa/ace-link-20160811.112/ elpa/ace-link-20160811.112/ ├── ace-link-autoloads.el ├── ace-link.el ├── ace-link.elc └── ace-link-pkg.el 0 directories, 4 files
Here, the package manager will always load
which instructs Emacs to load
ace-link.el when one of the
;;;###autoload functions is called and
ace-link.el isn't yet
As an example of how useful delayed loading is: my 6000 line
config starts in 1.8 seconds.
About 40% of that time is spent on
(package-initialize), which I
assume is the package manager loading all those
that I have in my
Let me know if there's interest to have
elf-mode on MELPA. Also, if
anyone knows how to set mode automatically based on the first few
chars of the file (all binaries seem to start with
^?ELF), I'd like
to know that as well. Happy hacking!