--- /dev/null
+(* Memory info command for virtual domains.
+ (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc.
+ http://libvirt.org/
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Ordinary kernel symbol lookups.
+ *)
+
+open Unix
+open Printf
+
+open Virt_mem_gettext.Gettext
+open Virt_mem_utils
+open Virt_mem_types
+
+(* Look for some common entries in the exported symbol table and
+ * from that find the symbol table itself. These are just
+ * supposed to be symbols which are very likely to be present
+ * in any Linux kernel, although we only need one of them to be
+ * present to find the symbol table.
+ *
+ * NB. Must not be __initdata, must be in EXPORT_SYMBOL.
+ *)
+let common_ksyms = [
+ "init_task"; (* first task_struct *)
+ "root_mountflags"; (* flags for mounting root fs *)
+ "init_uts_ns"; (* uname strings *)
+ "sys_open"; (* open(2) entry point *)
+ "sys_chdir"; (* chdir(2) entry point *)
+ "sys_chroot"; (* chroot(2) entry point *)
+ "sys_umask"; (* umask(2) entry point *)
+ "schedule"; (* scheduler entry point *)
+]
+
+let find_kernel_symbols debug (domid, name, arch, mem) =
+ (* Searching for <NUL>string<NUL> *)
+ let common_ksyms_nul = List.map (sprintf "\000%s\000") common_ksyms in
+
+ let start_t = gettimeofday () in
+
+ (* Search for these strings in the memory image. *)
+ let ksym_strings =
+ List.map (Virt_mem_mmap.find_all mem) common_ksyms_nul in
+ let ksym_strings = List.concat ksym_strings in
+ (* Adjust found addresses to start of the string (skip <NUL>). *)
+ let ksym_strings = List.map Int64.succ ksym_strings in
+
+ if debug then (
+ let end_t = gettimeofday () in
+ eprintf "timing: searching for common_ksyms took %f seconds\n%!"
+ (end_t -. start_t)
+ );
+
+ let start_t = gettimeofday () in
+
+ (* For any we found, try to look up the symbol table
+ * base addr and size.
+ *)
+ let ksymtabs = List.map (
+ fun addr ->
+ (* Search for 'addr' appearing in the image. *)
+ let addrs = Virt_mem_mmap.find_pointer_all mem addr in
+
+ (* Now consider each of these addresses and search back
+ * until we reach the beginning of the (possible) symbol
+ * table.
+ *
+ * Kernel symbol table struct is:
+ * struct kernel_symbol {
+ * unsigned long value;
+ * const char *name; <-- initial pointer
+ * } symbols[];
+ *)
+ let pred_long2 addr =
+ Virt_mem_mmap.pred_long mem (Virt_mem_mmap.pred_long mem addr)
+ in
+ let base_addrs = List.map (
+ fun addr ->
+ let rec loop addr =
+ (* '*addr' should point to a C identifier. If it does,
+ * step backwards to the previous symbol table entry.
+ *)
+ let addrp = Virt_mem_mmap.follow_pointer mem addr in
+ if Virt_mem_mmap.is_C_identifier mem addrp then
+ loop (pred_long2 addr)
+ else
+ Virt_mem_mmap.succ_long mem addr
+ in
+ loop addr
+ ) addrs in
+
+ (* Also look for the end of the symbol table and
+ * calculate its size.
+ *)
+ let base_addrs_sizes = List.map (
+ fun base_addr ->
+ let rec loop addr =
+ let addr2 = Virt_mem_mmap.succ_long mem addr in
+ let addr2p = Virt_mem_mmap.follow_pointer mem addr2 in
+ if Virt_mem_mmap.is_C_identifier mem addr2p then
+ loop (Virt_mem_mmap.succ_long mem addr2)
+ else
+ addr
+ in
+ let end_addr = loop base_addr in
+ base_addr, end_addr -^ base_addr
+ ) base_addrs in
+
+ base_addrs_sizes
+ ) ksym_strings in
+ let ksymtabs = List.concat ksymtabs in
+
+ (* Simply ignore any symbol table candidates which are too small. *)
+ let ksymtabs = List.filter (fun (_, size) -> size > 64L) ksymtabs in
+
+ if debug then (
+ eprintf "%s: candidate symbol tables at:\n" name;
+ List.iter (
+ fun (addr, size) ->
+ eprintf "\t%Lx\t%Lx\t%!" addr size;
+ eprintf "first symbol: %s\n%!"
+ (Virt_mem_mmap.get_string mem
+ (Virt_mem_mmap.follow_pointer mem
+ (Virt_mem_mmap.succ_long mem addr)))
+ ) ksymtabs
+ );
+
+ (* Vote for the most popular symbol table candidate and from this
+ * generate a function to look up ksyms.
+ *)
+ let lookup_ksym =
+ let freqs = frequency ksymtabs in
+ match freqs with
+ | [] ->
+ eprintf (f_"%s: cannot find start of kernel symbol table\n") name;
+ (fun _ -> raise Not_found)
+
+ | (_, (ksymtab_addr, ksymtab_size)) :: _ ->
+ if debug then
+ eprintf
+ "%s: Kernel symbol table found at %Lx, size %Lx bytes\n%!"
+ name ksymtab_addr ksymtab_size;
+
+ (* Load the whole symbol table as a bitstring. *)
+ let ksymtab =
+ Bitstring.bitstring_of_string
+ (Virt_mem_mmap.get_bytes mem ksymtab_addr
+ (Int64.to_int ksymtab_size)) in
+
+ (* Function to look up an address in the symbol table. *)
+ let lookup_ksym sym =
+ let bits = bits_of_wordsize (Virt_mem_mmap.get_wordsize mem) in
+ let e = Virt_mem_mmap.get_endian mem in
+ let rec loop bs =
+ bitmatch bs with
+ | { value : bits : endian(e);
+ name_ptr : bits : endian(e) }
+ when Virt_mem_mmap.get_string mem name_ptr = sym ->
+ value
+ | { _ : bits : endian(e);
+ _ : bits : endian(e);
+ bs : -1 : bitstring } ->
+ loop bs
+ | { _ } -> raise Not_found
+ in
+ loop ksymtab
+ in
+
+ lookup_ksym
+ in
+
+ if debug then (
+ let end_t = gettimeofday () in
+ eprintf "timing: searching for ordinary ksyms took %f seconds\n%!"
+ (end_t -. start_t)
+ );
+
+ ((domid, name, arch, mem, lookup_ksym) : image1)