Linux 3.0: widen limits in is_kallsyms_valid_address.
[virt-dmesg.git] / src / ksyms.ml
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 (* Look for ordinary symbol table.
24  *
25  * The ordinary symbol table is defined as:
26  * struct kernel_symbol {
27  *   unsigned long value;
28  *   const char *name;  -------> points to string somewhere else
29  * } symbols[];
30  *
31  * What we do is look for some likely symbols (ie. the strings,
32  * common_ksyms).  Then (since we know the address of those strings)
33  * we look for something which is plausibly a symbol table, searching
34  * forwards and backwards to find the ends of it.  This gives us, for
35  * each likely symbol found, a corresponding potential symbol table.
36  * Then we take a vote for the most frequently occurring symbol table
37  * and use that.
38  *)
39
40 (* Look for some common entries in the exported symbol table and
41  * from that find the symbol table itself.  These are just
42  * supposed to be symbols which are very likely to be present
43  * in any Linux kernel, although we only need one of them to be
44  * present to find the symbol table.
45  *
46  * NB. Must not be __initdata, must be in EXPORT_SYMBOL.
47  *)
48 let common_ksyms = [
49   "init_task";                          (* first task_struct *)
50   "root_mountflags";                    (* flags for mounting root fs *)
51   "init_uts_ns";                        (* uname strings *)
52   "nr_cpu_ids";
53   "speedstep_get_freqs";
54   "hpet_register_irq_handler";
55   "tty_put_char";
56   "driver_find";
57   "scsi_register_driver";
58   "phy_register_fixup";
59   "input_register_handler";
60   "save_processor_state";
61 ]
62
63 let search_ksyms k =
64   let sym_addrs : (string * int64) list =
65     List.concat (
66       List.map (
67         fun symbol ->
68           let addrs = Kernel.find_all k (sprintf "\000%s\000" symbol) in
69           (* Need to +1 because the string we matched started with \0
70            * character.
71            *)
72           let addrs = List.map ((+^) 1L) addrs in
73           List.map (fun addr -> (symbol, addr)) addrs
74       ) common_ksyms
75     ) in
76
77   let sym_tables : (int64 * int64) list =
78     List.concat (
79       List.map (
80         fun (source_symbol, addr) ->
81           let addrs = Kernel.find_all_pointers k addr in
82
83           (* struct kernel_symbol {
84            *   unsigned long value;
85            *   const char *name; <--- each of addrs (could) point here
86            * } symbols[];
87            *)
88
89           (* Look at 'value' field and see if it's likely to be a
90            * value.  Filter out if not.
91            *)
92           let addrs = List.filter (
93             fun a ->
94               let a2 = Kernel.pred_word k a in
95               let a2p = Kernel.follow_pointer k a2 in
96               (* We wouldn't expect the value to point somewhere near
97                * to the name.
98                *)
99               Int64.abs (addr -^ a2p) > 32L
100           ) addrs in
101
102           (* Search back and forward for beginning and end of symbol table. *)
103           let symtab_start_addrs = List.map (
104             fun addr ->
105               let rec loop addr =
106                 (* '*addr' should point to a C identifier.  If it
107                  * does, step backwards to the previous symbol table
108                  * entry.
109                  *)
110                 let addrp = Kernel.follow_pointer k addr in
111                 if Kernel.is_C_identifier k addrp then
112                   loop (Kernel.pred_word k (Kernel.pred_word k addr))
113                 else
114                   Kernel.succ_word k addr
115               in
116               loop addr
117           ) addrs in
118
119           List.iter (
120             fun start_addr ->
121               debug "start_addr %Lx from %s" start_addr source_symbol
122           ) symtab_start_addrs;
123
124           List.map (
125             fun start_addr ->
126               let rec loop addr =
127                 let addr2 = Kernel.succ_word k addr in (* &name *)
128                 let addr2p = Kernel.follow_pointer k addr2 in (* name itself *)
129                 if Kernel.is_C_identifier k addr2p then
130                   loop (Kernel.succ_word k addr2)
131                 else
132                   addr
133               in
134               let end_addr = loop start_addr in
135               start_addr, end_addr -^ start_addr
136           ) symtab_start_addrs
137       ) sym_addrs
138     ) in
139
140   debug "candidate symbol tables:";
141   List.iter (
142     fun (start, size) ->
143       debug "\tstart %Lx size %Ld" start size
144   ) sym_tables;
145
146   (* Simply ignore any symbol table candidates which are too small. *)
147   let sym_tables = List.filter (fun (_, size) -> size > 64L) sym_tables in
148
149   (* Vote for the most popular symbol table. *)
150   let freqs = frequency sym_tables in
151   match freqs with
152   | [] ->
153       (* No symbol table, so this is likely not a kernel that we
154        * know how to parse.
155        *)
156       raise Not_found
157
158   | (_, (start_addr, size)) :: _ ->
159       (* Take the most popular symbol table candidate and discard
160        * the others.  Parse this into a list of symbols.
161        *)
162       let rec loop addr acc =
163         if addr < start_addr +^ size then
164           let value = Kernel.follow_pointer k addr in (* value *)
165           let addr2 = Kernel.succ_word k addr in (* &name *)
166           let addr2p = Kernel.follow_pointer k addr2 in (* name itself *)
167           let symbol = Kernel.get_string k addr2p in
168           let acc = (symbol, value) :: acc in
169           loop (Kernel.succ_word k addr2) acc
170         else
171           List.rev acc
172       in
173       loop start_addr []