82e0b37e820e7f1a7f389685d0fb92cf46201cf8
[virt-mem.git] / lib / virt_mem.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
20 open Unix
21 open Printf
22 open ExtList
23
24 open Virt_mem_utils
25 module MMap = Virt_mem_mmap
26
27 (* Main program. *)
28 let () =
29   (* Verbose messages. *)
30   let verbose = ref false in
31
32   (* Default wordsize. *)
33   let def_wordsize = ref None in
34   let set_wordsize = function
35     | "32" -> def_wordsize := Some W32
36     | "64" -> def_wordsize := Some W64
37     | "auto" -> def_wordsize := None
38     | str -> failwith (sprintf "set_wordsize: %s: unknown wordsize" str)
39   in
40
41   (* Default endianness. *)
42   let def_endian = ref None in
43   let set_endian = function
44     | "auto" -> def_endian := None
45     | "le" | "little" | "littleendian" | "intel" ->
46         def_endian := Some Bitmatch.LittleEndian
47     | "be" | "big" | "bigendian" | "motorola" ->
48         def_endian := Some Bitmatch.BigEndian
49     | str -> failwith (sprintf "set_endian: %s: unknown endianness" str)
50   in
51
52   (* Default architecture. *)
53   let def_architecture = ref None in
54   let set_architecture = function
55     | "auto" -> def_architecture := None
56     | arch ->
57         let arch = architecture_of_string arch in
58         def_architecture := Some arch;
59         def_endian := Some (endian_of_architecture arch);
60         def_wordsize := Some (wordsize_of_architecture arch)
61   in
62
63   (* Default text address. *)
64   let def_text_addr = ref 0L (* 0 = auto-detect *) in
65   let set_text_addr = function
66     | "auto" -> def_text_addr := 0L
67     | "i386" -> def_text_addr := 0xc010_0000_L (* common for x86 *)
68     | "x86-64"|"x86_64" -> def_text_addr := 0xffffffff_81000000_L (* x86-64? *)
69     | str -> def_text_addr := Int64.of_string str
70   in
71
72   (* List of kernel images. *)
73   let images = ref [] in
74
75   let memory_image filename =
76     images :=
77       (!def_wordsize, !def_endian, !def_architecture, !def_text_addr, filename)
78     :: !images
79   in
80
81   let argspec = Arg.align [
82     "-A", Arg.String set_architecture,
83     "arch " ^ "Set kernel architecture, endianness and word size";
84     "-E", Arg.String set_endian,
85     "endian " ^ "Set kernel endianness";
86     "-T", Arg.String set_text_addr,
87     "addr " ^ "Set kernel text address";
88     "-W", Arg.String set_wordsize,
89     "addr " ^ "Set kernel word size";
90     "-t", Arg.String memory_image,
91     "image " ^ "Use saved kernel memory image";
92     "-verbose", Arg.Set verbose,
93     " " ^ "Verbose messages";
94   ] in
95
96   let anon_fun str =
97     raise (Arg.Bad (sprintf "%s: unknown parameter" str)) in
98   let usage_msg = "virt-mem: shows memory information for guests
99
100 SUMMARY
101   virt-mem [-options]
102
103 OPTIONS" in
104
105   Arg.parse argspec anon_fun usage_msg;
106
107   let images = !images in
108   let verbose = !verbose in
109
110   (* Get the kernel images. *)
111   let images =
112     if images = [] then
113       (* XXX use libvirt to get images *)
114       failwith "libvirt: not yet implemented"
115     else
116       List.map (
117         fun (wordsize, endian, arch, text_addr, filename) ->
118           (* Quite a lot of limitations on the kernel images we can
119            * handle at the moment ...
120            *)
121           (* XXX We could auto-detect wordsize easily. *)
122           let wordsize =
123             match wordsize with
124             | None ->
125                 failwith
126                   (sprintf "%s: use -W to define word size for this image"
127                      filename);
128             | Some ws -> ws in
129           let endian =
130             match endian with
131             | None ->
132                 failwith
133                   (sprintf "%s: use -E to define endianness for this image"
134                      filename);
135             | Some e -> e in
136
137           let arch =
138             match arch with
139             | Some I386 -> I386 | Some X86_64 -> X86_64
140             | _ ->
141                 failwith
142                   (sprintf "%s: use -A to define architecture (i386/x86-64 only) for this image" filename) in
143
144           if text_addr = 0L then
145             failwith
146               (sprintf "%s: use -T to define kernel load address for this image"
147                  filename);
148
149           (* Map the virtual memory. *)
150           let fd = openfile filename [O_RDONLY] 0 in
151           let mem = MMap.of_file fd text_addr in
152
153           (* Force the wordsize and endianness. *)
154           let mem = MMap.set_wordsize mem wordsize in
155           let mem = MMap.set_endian mem endian in
156
157           (filename, (arch, mem))
158       ) images in
159
160   List.iter (
161     fun (name, (arch, mem)) ->
162       (* Look for some common entries in the symbol table and from
163        * that find the symbol table itself.  These are just supposed to
164        * be symbols which are very likely to be present in any Linux
165        * kernel, although we only need one of them to be present to
166        * find the symbol table.
167        *
168        * NB. Must not be __initdata.
169        *)
170       let common_ksyms = [
171         "init_task";                    (* first task_struct *)
172         "root_mountflags";              (* flags for mounting root fs *)
173         "init_uts_ns";                  (* uname strings *)
174         "sys_open";                     (* open(2) entry point *)
175         "sys_chdir";                    (* chdir(2) entry point *)
176         "sys_chroot";                   (* chroot(2) entry point *)
177         "sys_umask";                    (* umask(2) entry point *)
178         "schedule";                     (* scheduler entry point *)
179       ] in
180       (* Searching for <NUL>string<NUL> *)
181       let common_ksyms = List.map (sprintf "\000%s\000") common_ksyms in
182
183       (* Search for these strings in the memory image. *)
184       let ksym_strings = List.map (MMap.find_all mem) common_ksyms in
185       let ksym_strings = List.concat ksym_strings in
186       (* Adjust found addresses to start of the string (skip <NUL>). *)
187       let ksym_strings = List.map Int64.succ ksym_strings in
188
189       (* For any we found, try to look up the symbol table
190        * base addr and size.
191        *)
192       let ksymtabs = List.map (
193         fun addr ->
194           (* Search for 'addr' appearing in the image. *)
195           let addrs = MMap.find_pointer_all mem addr in
196
197           (* Now consider each of these addresses and search back
198            * until we reach the beginning of the (possible) symbol
199            * table.
200            *
201            * Kernel symbol table struct is:
202            * struct kernel_symbol {
203            *   unsigned long value;
204            *   const char *name;    <-- initial pointer
205            * } symbols[];
206            *)
207           let pred_long2 addr = MMap.pred_long mem (MMap.pred_long mem addr) in
208           let base_addrs = List.map (
209             fun addr ->
210               let rec loop addr =
211                 (* '*addr' should point to a C identifier.  If it does,
212                  * step backwards to the previous symbol table entry.
213                  *)
214                 let addrp = MMap.follow_pointer mem addr in
215                 if MMap.is_C_identifier mem addrp then
216                   loop (pred_long2 addr)
217                 else
218                   MMap.succ_long mem addr
219               in
220               loop addr
221           ) addrs in
222
223           (* Also look for the end of the symbol table and
224            * calculate its size.
225            *)
226           let base_addrs_sizes = List.map (
227             fun base_addr ->
228               let rec loop addr =
229                 let addr2 = MMap.succ_long mem addr in
230                 let addr2p = MMap.follow_pointer mem addr2 in
231                 if MMap.is_C_identifier mem addr2p then
232                   loop (MMap.succ_long mem addr2)
233                 else
234                   addr
235               in
236               let end_addr = loop base_addr in
237               base_addr, end_addr -^ base_addr
238           ) base_addrs in
239
240           base_addrs_sizes
241       ) ksym_strings in
242       let ksymtabs = List.concat ksymtabs in
243
244       (* Simply ignore any symbol table candidates which are too small. *)
245       let ksymtabs = List.filter (fun (_, size) -> size > 64L) ksymtabs in
246
247       if verbose then (
248         printf "name %s:\n" name;
249         List.iter (
250           fun (addr, size) ->
251             printf "\t%Lx\t%Lx\t%!" addr size;
252             printf "first symbol: %s\n%!"
253               (MMap.get_string mem
254                  (MMap.follow_pointer mem
255                     (MMap.succ_long mem addr)))
256         ) ksymtabs
257       );
258
259       (* Vote for the most popular symbol table candidate. *)
260       let freqs = frequency ksymtabs in
261       match freqs with
262       | [] ->
263           eprintf "%s: cannot find start of kernel symbol table\n" name
264       | (_, (ksymtab_addr, ksymtab_size)) :: _ ->
265           if verbose then
266             printf "%s: Kernel symbol table found at %Lx, size %Lx bytes\n%!"
267               name ksymtab_addr ksymtab_size;
268
269           (* Load the whole symbol table as a bitstring. *)
270           let ksymtab =
271             Bitmatch.bitstring_of_string
272               (MMap.get_bytes mem ksymtab_addr (Int64.to_int ksymtab_size)) in
273
274           (* Function to look up an address in the symbol table. *)
275           let lookup_ksym sym =
276             let bits = bits_of_wordsize (MMap.get_wordsize mem) in
277             let e = MMap.get_endian mem in
278             let rec loop bs =
279               bitmatch bs with
280               | { value : bits : endian(e);
281                   name_ptr : bits : endian(e) }
282                   when MMap.get_string mem name_ptr = sym ->
283                   value
284               | { _ : bits : endian(e);
285                   _ : bits : endian(e);
286                   bs : -1 : bitstring } ->
287                   loop bs
288               | { _ } -> raise Not_found
289             in
290             loop ksymtab
291           in
292
293           if verbose then (
294             (* This just tests looking up kernel symbols. *)
295             printf "init_task = %Lx\n" (lookup_ksym "init_task");
296             printf "schedule = %Lx\n" (lookup_ksym "schedule");
297             printf "system_utsname = %s\n"
298               (try
299                  let addr = lookup_ksym "system_utsname" in
300                  sprintf "%Lx" addr
301                with Not_found -> "not found");
302             printf "init_uts_ns = %s\n"
303               (try
304                  let addr = lookup_ksym "init_uts_ns" in
305                  sprintf "%Lx" addr
306                with Not_found -> "not found");
307           );
308
309           
310
311
312   ) images