elf-mode - view the symbol list in a binary
28 Aug 2016Recently, I've been looking at
libigl. I
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 Makefile
;
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
command:
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 auto-mode-alist
:
(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 *.a
or *.so
.
And here's the body of elf-mode
:
(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
read-only-mode
and 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 loaddefs.el
:
(autoload 'elf-mode "modes/ora-elf" "" t nil)
My init.el
always loads loaddefs.el
, but never loads ora-elf.el
where the function is defined. That file is only loaded when the
function elf-mode
is called for the first time. The above autoload
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 ace-link-autoloads.el
,
which instructs Emacs to load ace-link.el
when one of the
;;;###autoload
functions is called and ace-link.el
isn't yet
loaded.
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 *-autoloads.el
files
that I have in my elpa/
.
Outro
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!