Commit | Line | Data |
---|---|---|
5796c8dc SS |
1 | /* Disassembly display. |
2 | ||
25e4902b | 3 | Copyright (C) 1998-2015 Free Software Foundation, Inc. |
5796c8dc SS |
4 | |
5 | Contributed by Hewlett-Packard Company. | |
6 | ||
7 | This file is part of GDB. | |
8 | ||
9 | This program is free software; you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation; either version 3 of the License, or | |
12 | (at your option) any later version. | |
13 | ||
14 | This program is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
21 | ||
22 | #include "defs.h" | |
cf7f2e2d | 23 | #include "arch-utils.h" |
5796c8dc SS |
24 | #include "symtab.h" |
25 | #include "breakpoint.h" | |
26 | #include "frame.h" | |
27 | #include "value.h" | |
28 | #include "source.h" | |
29 | #include "disasm.h" | |
5796c8dc SS |
30 | #include "tui/tui.h" |
31 | #include "tui/tui-data.h" | |
32 | #include "tui/tui-win.h" | |
33 | #include "tui/tui-layout.h" | |
34 | #include "tui/tui-winsource.h" | |
35 | #include "tui/tui-stack.h" | |
36 | #include "tui/tui-file.h" | |
37 | #include "tui/tui-disasm.h" | |
cf7f2e2d | 38 | #include "progspace.h" |
25e4902b | 39 | #include "objfiles.h" |
5796c8dc SS |
40 | |
41 | #include "gdb_curses.h" | |
42 | ||
43 | struct tui_asm_line | |
44 | { | |
45 | CORE_ADDR addr; | |
46 | char *addr_string; | |
47 | char *insn; | |
48 | }; | |
49 | ||
50 | /* Function to set the disassembly window's content. | |
51 | Disassemble count lines starting at pc. | |
52 | Return address of the count'th instruction after pc. */ | |
53 | static CORE_ADDR | |
54 | tui_disassemble (struct gdbarch *gdbarch, struct tui_asm_line *asm_lines, | |
55 | CORE_ADDR pc, int count) | |
56 | { | |
57 | struct ui_file *gdb_dis_out; | |
58 | ||
59 | /* Now init the ui_file structure. */ | |
60 | gdb_dis_out = tui_sfileopen (256); | |
61 | ||
62 | /* Now construct each line. */ | |
63 | for (; count > 0; count--, asm_lines++) | |
64 | { | |
65 | if (asm_lines->addr_string) | |
66 | xfree (asm_lines->addr_string); | |
67 | if (asm_lines->insn) | |
68 | xfree (asm_lines->insn); | |
69 | ||
70 | print_address (gdbarch, pc, gdb_dis_out); | |
71 | asm_lines->addr = pc; | |
72 | asm_lines->addr_string = xstrdup (tui_file_get_strbuf (gdb_dis_out)); | |
73 | ||
74 | ui_file_rewind (gdb_dis_out); | |
75 | ||
76 | pc = pc + gdb_print_insn (gdbarch, pc, gdb_dis_out, NULL); | |
77 | ||
78 | asm_lines->insn = xstrdup (tui_file_get_strbuf (gdb_dis_out)); | |
79 | ||
80 | /* Reset the buffer to empty. */ | |
81 | ui_file_rewind (gdb_dis_out); | |
82 | } | |
83 | ui_file_delete (gdb_dis_out); | |
84 | return pc; | |
85 | } | |
86 | ||
87 | /* Find the disassembly address that corresponds to FROM lines above | |
88 | or below the PC. Variable sized instructions are taken into | |
89 | account by the algorithm. */ | |
90 | static CORE_ADDR | |
91 | tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from) | |
92 | { | |
93 | CORE_ADDR new_low; | |
94 | int max_lines; | |
95 | int i; | |
96 | struct tui_asm_line *asm_lines; | |
97 | ||
98 | max_lines = (from > 0) ? from : - from; | |
99 | if (max_lines <= 1) | |
100 | return pc; | |
101 | ||
102 | asm_lines = (struct tui_asm_line*) alloca (sizeof (struct tui_asm_line) | |
103 | * max_lines); | |
104 | memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines); | |
105 | ||
106 | new_low = pc; | |
107 | if (from > 0) | |
108 | { | |
109 | tui_disassemble (gdbarch, asm_lines, pc, max_lines); | |
110 | new_low = asm_lines[max_lines - 1].addr; | |
111 | } | |
112 | else | |
113 | { | |
114 | CORE_ADDR last_addr; | |
115 | int pos; | |
25e4902b | 116 | struct bound_minimal_symbol msymbol; |
5796c8dc SS |
117 | |
118 | /* Find backward an address which is a symbol and for which | |
119 | disassembling from that address will fill completely the | |
120 | window. */ | |
121 | pos = max_lines - 1; | |
122 | do { | |
123 | new_low -= 1 * max_lines; | |
124 | msymbol = lookup_minimal_symbol_by_pc_section (new_low, 0); | |
125 | ||
25e4902b AHJ |
126 | if (msymbol.minsym) |
127 | new_low = BMSYMBOL_VALUE_ADDRESS (msymbol); | |
5796c8dc SS |
128 | else |
129 | new_low += 1 * max_lines; | |
130 | ||
131 | tui_disassemble (gdbarch, asm_lines, new_low, max_lines); | |
132 | last_addr = asm_lines[pos].addr; | |
25e4902b | 133 | } while (last_addr > pc && msymbol.minsym); |
5796c8dc SS |
134 | |
135 | /* Scan forward disassembling one instruction at a time until | |
136 | the last visible instruction of the window matches the pc. | |
137 | We keep the disassembled instructions in the 'lines' window | |
138 | and shift it downward (increasing its addresses). */ | |
139 | if (last_addr < pc) | |
140 | do | |
141 | { | |
142 | CORE_ADDR next_addr; | |
143 | ||
144 | pos++; | |
145 | if (pos >= max_lines) | |
146 | pos = 0; | |
147 | ||
148 | next_addr = tui_disassemble (gdbarch, &asm_lines[pos], | |
149 | last_addr, 1); | |
150 | ||
151 | /* If there are some problems while disassembling exit. */ | |
152 | if (next_addr <= last_addr) | |
153 | break; | |
154 | last_addr = next_addr; | |
155 | } while (last_addr <= pc); | |
156 | pos++; | |
157 | if (pos >= max_lines) | |
158 | pos = 0; | |
159 | new_low = asm_lines[pos].addr; | |
160 | } | |
161 | for (i = 0; i < max_lines; i++) | |
162 | { | |
163 | xfree (asm_lines[i].addr_string); | |
164 | xfree (asm_lines[i].insn); | |
165 | } | |
166 | return new_low; | |
167 | } | |
168 | ||
169 | /* Function to set the disassembly window's content. */ | |
170 | enum tui_status | |
171 | tui_set_disassem_content (struct gdbarch *gdbarch, CORE_ADDR pc) | |
172 | { | |
173 | enum tui_status ret = TUI_FAILURE; | |
174 | int i; | |
175 | int offset = TUI_DISASM_WIN->detail.source_info.horizontal_offset; | |
ef5ccd6c | 176 | int max_lines; |
5796c8dc SS |
177 | CORE_ADDR cur_pc; |
178 | struct tui_gen_win_info *locator = tui_locator_win_info_ptr (); | |
179 | int tab_len = tui_default_tab_len (); | |
180 | struct tui_asm_line *asm_lines; | |
181 | int insn_pos; | |
182 | int addr_size, max_size; | |
183 | char *line; | |
184 | ||
185 | if (pc == 0) | |
186 | return TUI_FAILURE; | |
187 | ||
188 | ret = tui_alloc_source_buffer (TUI_DISASM_WIN); | |
189 | if (ret != TUI_SUCCESS) | |
190 | return ret; | |
191 | ||
192 | TUI_DISASM_WIN->detail.source_info.gdbarch = gdbarch; | |
193 | TUI_DISASM_WIN->detail.source_info.start_line_or_addr.loa = LOA_ADDRESS; | |
194 | TUI_DISASM_WIN->detail.source_info.start_line_or_addr.u.addr = pc; | |
25e4902b | 195 | cur_pc = locator->content[0]->which_element.locator.addr; |
5796c8dc SS |
196 | |
197 | max_lines = TUI_DISASM_WIN->generic.height - 2; /* Account for | |
198 | hilite. */ | |
199 | ||
200 | /* Get temporary table that will hold all strings (addr & insn). */ | |
201 | asm_lines = (struct tui_asm_line*) alloca (sizeof (struct tui_asm_line) | |
202 | * max_lines); | |
203 | memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines); | |
204 | ||
5796c8dc SS |
205 | tui_disassemble (gdbarch, asm_lines, pc, max_lines); |
206 | ||
207 | /* See what is the maximum length of an address and of a line. */ | |
208 | addr_size = 0; | |
209 | max_size = 0; | |
210 | for (i = 0; i < max_lines; i++) | |
211 | { | |
212 | size_t len = strlen (asm_lines[i].addr_string); | |
cf7f2e2d | 213 | |
5796c8dc SS |
214 | if (len > addr_size) |
215 | addr_size = len; | |
216 | ||
217 | len = strlen (asm_lines[i].insn) + tab_len; | |
218 | if (len > max_size) | |
219 | max_size = len; | |
220 | } | |
221 | max_size += addr_size + tab_len; | |
222 | ||
223 | /* Allocate memory to create each line. */ | |
224 | line = (char*) alloca (max_size); | |
225 | insn_pos = (1 + (addr_size / tab_len)) * tab_len; | |
226 | ||
227 | /* Now construct each line. */ | |
228 | for (i = 0; i < max_lines; i++) | |
229 | { | |
230 | struct tui_win_element *element; | |
231 | struct tui_source_element *src; | |
232 | int cur_len; | |
233 | ||
25e4902b | 234 | element = TUI_DISASM_WIN->generic.content[i]; |
5796c8dc SS |
235 | src = &element->which_element.source; |
236 | strcpy (line, asm_lines[i].addr_string); | |
237 | cur_len = strlen (line); | |
238 | ||
239 | /* Add spaces to make the instructions start on the same | |
240 | column. */ | |
241 | while (cur_len < insn_pos) | |
242 | { | |
243 | strcat (line, " "); | |
244 | cur_len++; | |
245 | } | |
246 | ||
247 | strcat (line, asm_lines[i].insn); | |
248 | ||
249 | /* Now copy the line taking the offset into account. */ | |
250 | if (strlen (line) > offset) | |
251 | strcpy (src->line, &line[offset]); | |
252 | else | |
253 | src->line[0] = '\0'; | |
254 | ||
255 | src->line_or_addr.loa = LOA_ADDRESS; | |
256 | src->line_or_addr.u.addr = asm_lines[i].addr; | |
257 | src->is_exec_point = asm_lines[i].addr == cur_pc; | |
258 | ||
259 | /* See whether there is a breakpoint installed. */ | |
260 | src->has_break = (!src->is_exec_point | |
c50c785c JM |
261 | && breakpoint_here_p (current_program_space->aspace, |
262 | pc) | |
cf7f2e2d | 263 | != no_breakpoint_here); |
5796c8dc SS |
264 | |
265 | xfree (asm_lines[i].addr_string); | |
266 | xfree (asm_lines[i].insn); | |
267 | } | |
268 | TUI_DISASM_WIN->generic.content_size = i; | |
269 | return TUI_SUCCESS; | |
270 | } | |
271 | ||
272 | ||
273 | /* Function to display the disassembly window with disassembled code. */ | |
274 | void | |
275 | tui_show_disassem (struct gdbarch *gdbarch, CORE_ADDR start_addr) | |
276 | { | |
25e4902b | 277 | struct symtab *s = find_pc_line_symtab (start_addr); |
5796c8dc SS |
278 | struct tui_win_info *win_with_focus = tui_win_with_focus (); |
279 | struct tui_line_or_address val; | |
280 | ||
281 | val.loa = LOA_ADDRESS; | |
282 | val.u.addr = start_addr; | |
283 | tui_add_win_to_layout (DISASSEM_WIN); | |
284 | tui_update_source_window (TUI_DISASM_WIN, gdbarch, s, val, FALSE); | |
285 | ||
286 | /* If the focus was in the src win, put it in the asm win, if the | |
287 | source view isn't split. */ | |
288 | if (tui_current_layout () != SRC_DISASSEM_COMMAND | |
289 | && win_with_focus == TUI_SRC_WIN) | |
290 | tui_set_win_focus_to (TUI_DISASM_WIN); | |
291 | ||
292 | return; | |
293 | } | |
294 | ||
295 | ||
296 | /* Function to display the disassembly window. */ | |
297 | void | |
298 | tui_show_disassem_and_update_source (struct gdbarch *gdbarch, | |
299 | CORE_ADDR start_addr) | |
300 | { | |
301 | struct symtab_and_line sal; | |
302 | ||
303 | tui_show_disassem (gdbarch, start_addr); | |
304 | if (tui_current_layout () == SRC_DISASSEM_COMMAND) | |
305 | { | |
306 | struct tui_line_or_address val; | |
307 | ||
308 | /* Update what is in the source window if it is displayed too, | |
309 | note that it follows what is in the disassembly window and | |
310 | visa-versa. */ | |
311 | sal = find_pc_line (start_addr, 0); | |
312 | val.loa = LOA_LINE; | |
313 | val.u.line_no = sal.line; | |
314 | tui_update_source_window (TUI_SRC_WIN, gdbarch, sal.symtab, val, TRUE); | |
315 | if (sal.symtab) | |
316 | { | |
317 | set_current_source_symtab_and_line (&sal); | |
ef5ccd6c | 318 | tui_update_locator_fullname (symtab_to_fullname (sal.symtab)); |
5796c8dc SS |
319 | } |
320 | else | |
ef5ccd6c | 321 | tui_update_locator_fullname ("?"); |
5796c8dc SS |
322 | } |
323 | ||
324 | return; | |
325 | } | |
326 | ||
327 | void | |
328 | tui_get_begin_asm_address (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p) | |
329 | { | |
330 | struct tui_gen_win_info *locator; | |
331 | struct tui_locator_element *element; | |
cf7f2e2d | 332 | struct gdbarch *gdbarch = get_current_arch (); |
5796c8dc SS |
333 | CORE_ADDR addr; |
334 | ||
335 | locator = tui_locator_win_info_ptr (); | |
25e4902b | 336 | element = &locator->content[0]->which_element.locator; |
5796c8dc SS |
337 | |
338 | if (element->addr == 0) | |
339 | { | |
25e4902b | 340 | struct bound_minimal_symbol main_symbol; |
5796c8dc SS |
341 | |
342 | /* Find address of the start of program. | |
343 | Note: this should be language specific. */ | |
344 | main_symbol = lookup_minimal_symbol ("main", NULL, NULL); | |
25e4902b | 345 | if (main_symbol.minsym == 0) |
5796c8dc | 346 | main_symbol = lookup_minimal_symbol ("MAIN", NULL, NULL); |
25e4902b | 347 | if (main_symbol.minsym == 0) |
5796c8dc | 348 | main_symbol = lookup_minimal_symbol ("_start", NULL, NULL); |
25e4902b AHJ |
349 | if (main_symbol.minsym) |
350 | addr = BMSYMBOL_VALUE_ADDRESS (main_symbol); | |
5796c8dc SS |
351 | else |
352 | addr = 0; | |
353 | } | |
354 | else /* The target is executing. */ | |
355 | { | |
356 | gdbarch = element->gdbarch; | |
357 | addr = element->addr; | |
358 | } | |
359 | ||
360 | *gdbarch_p = gdbarch; | |
361 | *addr_p = addr; | |
362 | } | |
363 | ||
364 | /* Determine what the low address will be to display in the TUI's | |
365 | disassembly window. This may or may not be the same as the low | |
366 | address input. */ | |
367 | CORE_ADDR | |
368 | tui_get_low_disassembly_address (struct gdbarch *gdbarch, | |
369 | CORE_ADDR low, CORE_ADDR pc) | |
370 | { | |
371 | int pos; | |
372 | ||
373 | /* Determine where to start the disassembly so that the pc is about | |
374 | in the middle of the viewport. */ | |
375 | pos = tui_default_win_viewport_height (DISASSEM_WIN, DISASSEM_COMMAND) / 2; | |
376 | pc = tui_find_disassembly_address (gdbarch, pc, -pos); | |
377 | ||
378 | if (pc < low) | |
379 | pc = low; | |
380 | return pc; | |
381 | } | |
382 | ||
383 | /* Scroll the disassembly forward or backward vertically. */ | |
384 | void | |
385 | tui_vertical_disassem_scroll (enum tui_scroll_direction scroll_direction, | |
386 | int num_to_scroll) | |
387 | { | |
388 | if (TUI_DISASM_WIN->generic.content != NULL) | |
389 | { | |
390 | struct gdbarch *gdbarch = TUI_DISASM_WIN->detail.source_info.gdbarch; | |
391 | CORE_ADDR pc; | |
392 | tui_win_content content; | |
393 | struct tui_line_or_address val; | |
394 | int dir; | |
395 | ||
396 | content = (tui_win_content) TUI_DISASM_WIN->generic.content; | |
397 | ||
398 | pc = content[0]->which_element.source.line_or_addr.u.addr; | |
399 | num_to_scroll++; | |
c50c785c JM |
400 | dir = (scroll_direction == FORWARD_SCROLL) |
401 | ? num_to_scroll : -num_to_scroll; | |
5796c8dc SS |
402 | |
403 | val.loa = LOA_ADDRESS; | |
404 | val.u.addr = tui_find_disassembly_address (gdbarch, pc, dir); | |
c50c785c JM |
405 | tui_update_source_window_as_is (TUI_DISASM_WIN, gdbarch, |
406 | NULL, val, FALSE); | |
5796c8dc SS |
407 | } |
408 | } |