(* 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 ({ mem = mem; domname = domname } as image) = (* Searching for string *) 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 ). *) 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" domname; 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 ksymmap = let freqs = frequency ksymtabs in match freqs with | [] -> eprintf (f_"%s: cannot find start of kernel symbol table\n") domname; None | (_, (ksymtab_addr, ksymtab_size)) :: _ -> if debug then eprintf "%s: Kernel symbol table found at %Lx, size %Lx bytes\n%!" domname 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 (* Construct kernel symbol map. *) let ksymmap = let bits = bits_of_wordsize (Virt_mem_mmap.get_wordsize mem) in let e = Virt_mem_mmap.get_endian mem in let rec loop ksymmap bs = bitmatch bs with | { value : bits : endian(e); name_ptr : bits : endian(e); bs : -1 : bitstring } -> let name = Virt_mem_mmap.get_string mem name_ptr in let ksymmap = Ksymmap.add name value ksymmap in loop ksymmap bs | { _ } -> ksymmap in loop Ksymmap.empty ksymtab in Some ksymmap in if debug then ( let end_t = gettimeofday () in eprintf "timing: searching for ordinary ksyms took %f seconds\n%!" (end_t -. start_t) ); (image, ksymmap)