"Fossies" - the Fresh Open Source Software Archive

Member "virt-dmesg-0.3.0/src/kallsyms.ml" (21 Jun 2011, 8882 Bytes) of package /linux/privat/old/virt-dmesg-0.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Objective Caml source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    1 (* virt-dmesg
    2  * (C) Copyright 2008-2011 Red Hat Inc.
    3  *
    4  * This program is free software; you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation; either version 2 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with this program; if not, write to the Free Software
   16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   17  *)
   18 
   19 open Printf
   20 
   21 open Utils
   22 
   23 (* This code tries to find the /proc/kallsyms table.  This is in an
   24  * odd compressed format (but not a very successful compression
   25  * format).  However if it exists we know that it will contain
   26  * addresses of the ordinary ksyms, and it has some characteristics
   27  * which make it easy to detect in the memory.
   28  * 
   29  * kallsyms contains a complete list of symbols so is much more useful
   30  * than the basic list of exports.
   31  *)
   32 
   33 let min_kallsyms_tabsize = 1_000L
   34 let max_kallsyms_tabsize = 1_000_000L (* 433056 kallsyms in 64 bit F14 kernel *)
   35 
   36 type kallsyms_compr =
   37   | Compressed of (string * int64) list * int64
   38   | Uncompressed of (string * int64) list
   39 
   40 let search_kallsyms k map =
   41   let ksym_addrs = filter_map (
   42     fun ksym -> try Some (StringMap.find ksym map) with Not_found -> None
   43   ) Ksyms.common_ksyms in
   44 
   45   (* Search for those kernel addresses in the image.  We're looking
   46    * for the table kallsyms_addresses followed by kallsyms_num_syms
   47    * (number of symbols in the table).
   48    *)
   49   let ksym_addrs =
   50     List.concat (List.map (Kernel.find_all_pointers k) ksym_addrs) in
   51 
   52   (* Test each one to see if it's a candidate list of kernel
   53    * addresses followed by length of list.
   54    *)
   55   let kallsymtabs = filter_map (
   56     fun addr ->
   57       (* Search upwards from address until we find the length field.
   58        * If found, jump backwards by length and check all addresses.
   59        *)
   60       debug "testing candidate kallsyms at %Lx" addr;
   61 
   62       (* Addresses which can appear in kallsyms can be validly not
   63        * mapped.  This appears to only happen for 64 bit kernels.
   64        *)
   65       let is_kallsyms_valid_address =
   66         match k.Kernel.wordsize with
   67         | Kernel.Word32 -> Kernel.is_mapped k
   68         | Kernel.Word64 ->
   69             fun addr ->
   70               Kernel.is_mapped k addr
   71               || Kernel.addr_compare addr 0xffffffff_80000000_L >= 0
   72               || Kernel.addr_compare addr 0x00000000_00200000_L <= 0
   73       in
   74 
   75       let rec loop prev_addrp addr =
   76     let addrp = Kernel.follow_pointer k addr in
   77 
   78         (* kallsyms are sorted, so this gives us an additional heuristic *)
   79         let increasing =
   80           match prev_addrp with
   81           | None -> true
   82           | Some prev_addrp -> Kernel.addr_compare prev_addrp addrp <= 0 in
   83 
   84     if increasing && is_kallsyms_valid_address addrp then
   85       (* continue up the table *)
   86       loop (Some addrp) (Kernel.succ_word k addr)
   87     else
   88       if addrp >= min_kallsyms_tabsize &&
   89         addrp <= max_kallsyms_tabsize then (
   90           (* addrp might be the symbol count.  Count backwards and
   91            * check the full table.
   92            *)
   93               debug "addrp (num_entries) = %Lx" addrp;
   94 
   95           let num_entries = Int64.to_int addrp in
   96           let entry_size = Kernel.bytes_of_wordsize k in
   97           let start_addr =
   98         addr -^ Int64.of_int (entry_size * num_entries) in
   99           let end_addr = addr in
  100           let rec loop2 addr =
  101         if Kernel.addr_compare addr end_addr < 0 then (
  102           let addrp = Kernel.follow_pointer k addr in
  103           if is_kallsyms_valid_address addrp then
  104             loop2 (Kernel.succ_word k addr)
  105           else (
  106                     debug "rejected on second pass because addrp = %Lx" addrp;
  107             None
  108                   )
  109         ) else
  110           (* ok! *)
  111           let names_addr = Kernel.succ_word k end_addr in
  112           debug "candidate kallsyms found at %Lx (names_addr at %Lx, num_entries %d)"
  113             start_addr names_addr num_entries;
  114           Some (start_addr, num_entries, names_addr)
  115           in
  116           loop2 start_addr
  117         )
  118       else (
  119             debug "rejected on first pass because addrp = %Lx" addrp;
  120         None
  121           )
  122       in
  123       match loop None addr with
  124       | None -> None
  125       | Some (start_addr, num_entries, names_addr) ->
  126       (* As an additional verification, check the list of
  127        * kallsyms_names.
  128        *)
  129       try
  130         (* If the first byte is '\000' and is followed by a
  131          * C identifier, then this is old-school list of
  132          * symbols with prefix compression as in 2.6.9.
  133          * Otherwise Huffman-compressed kallsyms as in
  134          * 2.6.25.
  135          *)
  136         if Kernel.get_byte k names_addr = 0 &&
  137           Kernel.is_C_identifier k (names_addr+^1L) then (
  138         let names = ref [] in
  139         let prev = ref "" in
  140         let rec loop names_addr start_addr num =
  141           if num > 0 then (
  142             let prefix = Kernel.get_byte k names_addr in
  143             let prefix = String.sub !prev 0 prefix in
  144             let name =
  145               Kernel.get_string k (names_addr+^1L) in
  146             let len = String.length name in
  147             let name = prefix ^ name in
  148             prev := name;
  149             let names_addr = names_addr +^ Int64.of_int len +^ 2L in
  150             let sym_value = Kernel.follow_pointer k start_addr in
  151             let start_addr = Kernel.succ_word k start_addr in
  152             (*eprintf "%S -> %Lx\n" name sym_value;*)
  153             names := (name, sym_value) :: !names;
  154             loop names_addr start_addr (num-1)
  155           )
  156         in
  157         loop names_addr start_addr num_entries;
  158         let names = List.rev !names in
  159 
  160         Some (start_addr, num_entries, names_addr,
  161               Uncompressed names)
  162           )
  163         else ( (* new-style "compressed" names. *)
  164           let compressed_names = ref [] in
  165           let rec loop names_addr start_addr num =
  166         if num > 0 then (
  167           let len = Kernel.get_byte k names_addr in
  168           let name = Kernel.get_memory k (names_addr+^1L) len in
  169           let names_addr = names_addr +^ Int64.of_int len +^ 1L in
  170           let sym_value = Kernel.follow_pointer k start_addr in
  171           let start_addr = Kernel.succ_word k start_addr in
  172           compressed_names :=
  173             (name, sym_value) :: !compressed_names;
  174           loop names_addr start_addr (num-1)
  175         ) else
  176           names_addr
  177           in
  178           let markers_addr = loop names_addr start_addr num_entries in
  179           let markers_addr = Kernel.succ_align k markers_addr in
  180           let compressed_names = List.rev !compressed_names in
  181 
  182           Some (start_addr, num_entries, names_addr,
  183             Compressed (compressed_names, markers_addr))
  184         )
  185       with
  186         Invalid_argument _ -> None (* bad names list *)
  187   ) ksym_addrs in
  188 
  189   debug "candidate kallsyms at:";
  190   List.iter (
  191     function
  192     | (start_addr, num_entries, names_addr, Uncompressed _) ->
  193     debug "\t%Lx %d entries names_addr=%Lx old-style"
  194       start_addr num_entries names_addr
  195     | (start_addr, num_entries, names_addr,
  196        Compressed (_, markers_addr)) ->
  197     debug "\t%Lx %d entries names_addr=%Lx markers_addr=%Lx"
  198       start_addr num_entries names_addr markers_addr
  199   ) kallsymtabs;
  200 
  201   (* Vote for the most popular symbol table candidate and
  202    * return the list of kallsyms.
  203    *)
  204   let freqs = frequency kallsymtabs in
  205   match freqs with
  206   | [] ->
  207       (* Can't find any kallsymtabs. *)
  208       raise Not_found
  209 
  210   | (_, (_, _, _, Uncompressed names)) :: _ ->
  211       names
  212 
  213   | (_, (start_addr, num_entries, names_addr,
  214      Compressed (compressed_names, markers_addr))) :: _ ->
  215       (* Skip the markers and look for the token table. *)
  216       let num_markers = Int64.of_int ((num_entries + 255) / 256) in
  217       let marker_size = Int64.of_int (Kernel.bytes_of_wordsize k) in
  218       let tokens_addr = markers_addr +^ marker_size *^ num_markers in
  219 
  220       (* Now read out the compression tokens, which are just
  221        * 256 ASCIIZ strings that map bytes in the compression
  222        * names to substrings.
  223        *)
  224       let tokens = Array.make 256 "" in
  225       let rec loop i addr =
  226     if i < 256 then (
  227       let str = Kernel.get_string k addr in
  228       let len = String.length str in
  229       let addr = addr +^ Int64.of_int (len+1) in
  230       tokens.(i) <- str;
  231       loop (i+1) addr
  232     )
  233       in
  234       loop 0 tokens_addr;
  235 
  236       (* Expand the compressed names using the tokens. *)
  237       filter_map (
  238     fun (name, sym_value) ->
  239       let f c = tokens.(Char.code c) in
  240       let name = replace_chars f name in
  241       (* First character in uncompressed output is the symbol
  242        * type, eg. 'T'/'t' for text etc.
  243        *)
  244       (* NOTE: Symbol names are NOT unique
  245        * (eg. 'con_start' is both a function and data in
  246        * some kernels).  XXX We need to handle this situation
  247        * better.
  248        *)
  249       (*let typ = name.[0] in*)
  250       let name = String.sub name 1 (String.length name - 1) in
  251       (*eprintf "%S -> %Lx\n" name sym_value;*)
  252       Some (name, sym_value)
  253       ) compressed_names