Get rid of lookup_ksym function, replace with a map.
[virt-mem.git] / lib / virt_mem_ksyms.ml
1 (* Memory info command for virtual domains.
2    (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc.
3    http://libvirt.org/
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19    Ordinary kernel symbol lookups.
20  *)
21
22 open Unix
23 open Printf
24
25 open Virt_mem_gettext.Gettext
26 open Virt_mem_utils
27 open Virt_mem_types
28
29 (* Look for some common entries in the exported symbol table and
30  * from that find the symbol table itself.  These are just
31  * supposed to be symbols which are very likely to be present
32  * in any Linux kernel, although we only need one of them to be
33  * present to find the symbol table.
34  *
35  * NB. Must not be __initdata, must be in EXPORT_SYMBOL.
36  *)
37 let common_ksyms = [
38   "init_task";                          (* first task_struct *)
39   "root_mountflags";                    (* flags for mounting root fs *)
40   "init_uts_ns";                        (* uname strings *)
41   "sys_open";                           (* open(2) entry point *)
42   "sys_chdir";                          (* chdir(2) entry point *)
43   "sys_chroot";                         (* chroot(2) entry point *)
44   "sys_umask";                          (* umask(2) entry point *)
45   "schedule";                           (* scheduler entry point *)
46 ]
47
48 let find_kernel_symbols debug ({ mem = mem; domname = domname } as image) =
49   (* Searching for <NUL>string<NUL> *)
50   let common_ksyms_nul = List.map (sprintf "\000%s\000") common_ksyms in
51
52   let start_t = gettimeofday () in
53
54   (* Search for these strings in the memory image. *)
55   let ksym_strings =
56     List.map (Virt_mem_mmap.find_all mem) common_ksyms_nul in
57   let ksym_strings = List.concat ksym_strings in
58   (* Adjust found addresses to start of the string (skip <NUL>). *)
59   let ksym_strings = List.map Int64.succ ksym_strings in
60
61   if debug then (
62     let end_t = gettimeofday () in
63     eprintf "timing: searching for common_ksyms took %f seconds\n%!"
64       (end_t -. start_t)
65   );
66
67   let start_t = gettimeofday () in
68
69   (* For any we found, try to look up the symbol table
70    * base addr and size.
71    *)
72   let ksymtabs = List.map (
73     fun addr ->
74       (* Search for 'addr' appearing in the image. *)
75       let addrs = Virt_mem_mmap.find_pointer_all mem addr in
76
77       (* Now consider each of these addresses and search back
78        * until we reach the beginning of the (possible) symbol
79        * table.
80        *
81        * Kernel symbol table struct is:
82        * struct kernel_symbol {
83        *   unsigned long value;
84        *   const char *name;    <-- initial pointer
85        * } symbols[];
86        *)
87       let pred_long2 addr =
88         Virt_mem_mmap.pred_long mem (Virt_mem_mmap.pred_long mem addr)
89       in
90       let base_addrs = List.map (
91         fun addr ->
92           let rec loop addr =
93             (* '*addr' should point to a C identifier.  If it does,
94              * step backwards to the previous symbol table entry.
95              *)
96             let addrp = Virt_mem_mmap.follow_pointer mem addr in
97             if Virt_mem_mmap.is_C_identifier mem addrp then
98               loop (pred_long2 addr)
99             else
100               Virt_mem_mmap.succ_long mem addr
101           in
102           loop addr
103       ) addrs in
104
105       (* Also look for the end of the symbol table and
106        * calculate its size.
107        *)
108       let base_addrs_sizes = List.map (
109         fun base_addr ->
110           let rec loop addr =
111             let addr2 = Virt_mem_mmap.succ_long mem addr in
112             let addr2p = Virt_mem_mmap.follow_pointer mem addr2 in
113             if Virt_mem_mmap.is_C_identifier mem addr2p then
114               loop (Virt_mem_mmap.succ_long mem addr2)
115             else
116               addr
117           in
118           let end_addr = loop base_addr in
119           base_addr, end_addr -^ base_addr
120       ) base_addrs in
121
122       base_addrs_sizes
123   ) ksym_strings in
124   let ksymtabs = List.concat ksymtabs in
125
126   (* Simply ignore any symbol table candidates which are too small. *)
127   let ksymtabs = List.filter (fun (_, size) -> size > 64L) ksymtabs in
128
129   if debug then (
130     eprintf "%s: candidate symbol tables at:\n" domname;
131     List.iter (
132       fun (addr, size) ->
133         eprintf "\t%Lx\t%Lx\t%!" addr size;
134         eprintf "first symbol: %s\n%!"
135           (Virt_mem_mmap.get_string mem
136              (Virt_mem_mmap.follow_pointer mem
137                 (Virt_mem_mmap.succ_long mem addr)))
138     ) ksymtabs
139   );
140
141   (* Vote for the most popular symbol table candidate and from this
142    * generate a function to look up ksyms.
143    *)
144   let ksymmap =
145     let freqs = frequency ksymtabs in
146     match freqs with
147     | [] ->
148         eprintf (f_"%s: cannot find start of kernel symbol table\n") domname;
149         Ksymmap.empty
150
151     | (_, (ksymtab_addr, ksymtab_size)) :: _ ->
152         if debug then
153           eprintf
154             "%s: Kernel symbol table found at %Lx, size %Lx bytes\n%!"
155             domname ksymtab_addr ksymtab_size;
156
157         (* Load the whole symbol table as a bitstring. *)
158         let ksymtab =
159           Bitstring.bitstring_of_string
160             (Virt_mem_mmap.get_bytes mem ksymtab_addr
161                (Int64.to_int ksymtab_size)) in
162
163         (* Construct kernel symbol map. *)
164         let ksymmap =
165           let bits = bits_of_wordsize (Virt_mem_mmap.get_wordsize mem) in
166           let e = Virt_mem_mmap.get_endian mem in
167           let rec loop ksymmap bs =
168             bitmatch bs with
169             | { value : bits : endian(e);
170                 name_ptr : bits : endian(e);
171                 bs : -1 : bitstring } ->
172                 let name = Virt_mem_mmap.get_string mem name_ptr in
173                 let ksymmap = Ksymmap.add name value ksymmap in
174                 loop ksymmap bs
175             | { _ } ->
176                 ksymmap
177           in
178           loop Ksymmap.empty ksymtab in
179
180         ksymmap
181   in
182
183   if debug then (
184     let end_t = gettimeofday () in
185     eprintf "timing: searching for ordinary ksyms took %f seconds\n%!"
186       (end_t -. start_t)
187   );
188
189   ((image, ksymmap) : image1)