1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
2 Contributed by Oracle.
3
4 This file is part of GNU Binutils.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
20
21 #include "config.h"
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/param.h>
25
26 #include "disassemble.h"
27 #include "dis-asm.h"
28 #include "demangle.h"
29 #include "dbe_types.h"
30 #include "DbeSession.h"
31 #include "Elf.h"
32 #include "Disasm.h"
33 #include "Stabs.h"
34 #include "i18n.h"
35 #include "util.h"
36 #include "StringBuilder.h"
37 #include "Function.h"
38
39 struct DisContext
40 {
41 bool is_Intel;
42 Stabs *stabs;
43 uint64_t pc; // first_pc <= pc < last_pc
44 uint64_t first_pc;
45 uint64_t last_pc;
46 uint64_t f_offset; // file offset for first_pc
47 int codeptr[4]; // longest instruction length may not be > 16
48 Data_window *elf;
49 };
50
51 static const int MAX_DISASM_STR = 2048;
52 static const int MAX_INSTR_SIZE = 8;
53
Disasm(char * fname)54 Disasm::Disasm (char *fname)
55 {
56 dwin = NULL;
57 dis_str = NULL;
58 need_swap_endian = false;
59 my_stabs = Stabs::NewStabs (fname, fname);
60 if (my_stabs == NULL)
61 return;
62 stabs = my_stabs;
63 platform = stabs->get_platform ();
64 disasm_open ();
65 }
66
Disasm(Platform_t _platform,Stabs * _stabs)67 Disasm::Disasm (Platform_t _platform, Stabs *_stabs)
68 {
69 dwin = NULL;
70 dis_str = NULL;
71 need_swap_endian = false;
72 stabs = _stabs;
73 platform = _platform;
74 my_stabs = NULL;
75 disasm_open ();
76 }
77
78 static int
fprintf_func(void * arg,const char * fmt,...)79 fprintf_func (void *arg, const char *fmt, ...)
80 {
81 char buf[512];
82 va_list vp;
83 va_start (vp, fmt);
84 int cnt = vsnprintf (buf, sizeof (buf), fmt, vp);
85 va_end (vp);
86
87 Disasm *dis = (Disasm *) arg;
88 dis->dis_str->append (buf);
89 return cnt;
90 }
91
92 static int
fprintf_styled_func(void * arg,enum disassembler_style st ATTRIBUTE_UNUSED,const char * fmt,...)93 fprintf_styled_func (void *arg, enum disassembler_style st ATTRIBUTE_UNUSED,
94 const char *fmt, ...)
95 {
96 char buf[512];
97 va_list vp;
98 va_start (vp, fmt);
99 int cnt = vsnprintf (buf, sizeof (buf), fmt, vp);
100 va_end (vp);
101
102 Disasm *dis = (Disasm *) arg;
103 dis->dis_str->append (buf);
104 return cnt;
105 }
106
107 /* Get LENGTH bytes from info's buffer, at target address memaddr.
108 Transfer them to myaddr. */
109 static int
read_memory_func(bfd_vma memaddr,bfd_byte * myaddr,unsigned int length,disassemble_info * info)110 read_memory_func (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length,
111 disassemble_info *info)
112 {
113 unsigned int opb = info->octets_per_byte;
114 size_t end_addr_offset = length / opb;
115 size_t max_addr_offset = info->buffer_length / opb;
116 size_t octets = (memaddr - info->buffer_vma) * opb;
117 if (memaddr < info->buffer_vma
118 || memaddr - info->buffer_vma > max_addr_offset
119 || memaddr - info->buffer_vma + end_addr_offset > max_addr_offset
120 || (info->stop_vma && (memaddr >= info->stop_vma
121 || memaddr + end_addr_offset > info->stop_vma)))
122 return -1;
123 memcpy (myaddr, info->buffer + octets, length);
124 return 0;
125 }
126
127 static void
print_address_func(bfd_vma addr,disassemble_info * info)128 print_address_func (bfd_vma addr, disassemble_info *info)
129 {
130 bfd_signed_vma off;
131 unsigned long long ta;
132 Disasm *dis;
133 switch (info->insn_type)
134 {
135 case dis_branch:
136 case dis_condbranch:
137 off = (bfd_signed_vma) addr;
138 dis = (Disasm *) info->stream;
139 ta = dis->inst_addr + off;
140 (*info->fprintf_func) (info->stream, ".%c0x%llx [ 0x%llx ]",
141 off > 0 ? '+' : '-', (long long) (off > 0 ? off : -off), ta);
142 return;
143 case dis_jsr:
144 off = (bfd_signed_vma) addr;
145 dis = (Disasm *) info->stream;
146 ta = dis->inst_addr + off;
147 const char *nm = NULL;
148 Function *f = dis->map_PC_to_func (ta);
149 if (f)
150 {
151 if (dis->inst_addr >= f->img_offset
152 && dis->inst_addr < f->img_offset + f->size)
153 { // Same function
154 (*info->fprintf_func) (info->stream, ".%c0x%llx [ 0x%llx ]",
155 off > 0 ? '+' : '-', (long long) (off > 0 ? off : -off), ta);
156 return;
157 }
158 if (f->flags & FUNC_FLAG_PLT)
159 nm = dis->get_funcname_in_plt(ta);
160 if (nm == NULL)
161 nm = f->get_name ();
162 }
163 if (nm)
164 (*info->fprintf_func) (info->stream, "%s [ 0x%llx, .%c0x%llx]",
165 nm, ta, off > 0 ? '+' : '-', (long long) (off > 0 ? off : -off));
166 else
167 (*info->fprintf_func) (info->stream,
168 ".%c0x%llx [ 0x%llx ] // Unable to determine target symbol",
169 off > 0 ? '+' : '-', (long long) (off > 0 ? off : -off), ta);
170 return;
171 }
172 (*info->fprintf_func) (info->stream, "0x%llx", (long long) addr);
173 }
174
175 static asymbol *
symbol_at_address_func(bfd_vma addr ATTRIBUTE_UNUSED,disassemble_info * info ATTRIBUTE_UNUSED)176 symbol_at_address_func (bfd_vma addr ATTRIBUTE_UNUSED,
177 disassemble_info *info ATTRIBUTE_UNUSED)
178 {
179 return NULL;
180 }
181
182 static bfd_boolean
symbol_is_valid(asymbol * sym ATTRIBUTE_UNUSED,disassemble_info * info ATTRIBUTE_UNUSED)183 symbol_is_valid (asymbol * sym ATTRIBUTE_UNUSED,
184 disassemble_info *info ATTRIBUTE_UNUSED)
185 {
186 return TRUE;
187 }
188
189 static void
memory_error_func(int status,bfd_vma addr,disassemble_info * info)190 memory_error_func (int status, bfd_vma addr, disassemble_info *info)
191 {
192 info->fprintf_func (info->stream, "Address 0x%llx is out of bounds.\n",
193 (unsigned long long) addr);
194 }
195
196 void
disasm_open()197 Disasm::disasm_open ()
198 {
199 hex_visible = 1;
200 snprintf (addr_fmt, sizeof (addr_fmt), NTXT ("%s"), NTXT ("%8llx: "));
201 if (dis_str == NULL)
202 dis_str = new StringBuilder;
203
204 switch (platform)
205 {
206 case Aarch64:
207 case Intel:
208 case Amd64:
209 need_swap_endian = (DbeSession::platform == Sparc);
210 break;
211 case Sparcv8plus:
212 case Sparcv9:
213 case Sparc:
214 default:
215 need_swap_endian = (DbeSession::platform != Sparc);
216 break;
217 }
218
219 memset (&dis_info, 0, sizeof (dis_info));
220 dis_info.flavour = bfd_target_unknown_flavour;
221 dis_info.endian = BFD_ENDIAN_UNKNOWN;
222 dis_info.endian_code = dis_info.endian;
223 dis_info.octets_per_byte = 1;
224 dis_info.disassembler_needs_relocs = FALSE;
225 dis_info.fprintf_func = fprintf_func;
226 dis_info.fprintf_styled_func = fprintf_styled_func;
227 dis_info.stream = this;
228 dis_info.disassembler_options = NULL;
229 dis_info.read_memory_func = read_memory_func;
230 dis_info.memory_error_func = memory_error_func;
231 dis_info.print_address_func = print_address_func;
232 dis_info.symbol_at_address_func = symbol_at_address_func;
233 dis_info.symbol_is_valid = symbol_is_valid;
234 dis_info.display_endian = BFD_ENDIAN_UNKNOWN;
235 dis_info.symtab = NULL;
236 dis_info.symtab_size = 0;
237 dis_info.buffer_vma = 0;
238 switch (platform)
239 {
240 case Aarch64:
241 dis_info.arch = bfd_arch_aarch64;
242 dis_info.mach = bfd_mach_aarch64;
243 break;
244 case Intel:
245 case Amd64:
246 dis_info.arch = bfd_arch_i386;
247 dis_info.mach = bfd_mach_x86_64;
248 break;
249 case Sparcv8plus:
250 case Sparcv9:
251 case Sparc:
252 default:
253 dis_info.arch = bfd_arch_unknown;
254 dis_info.endian = BFD_ENDIAN_UNKNOWN;
255 break;
256 }
257 dis_info.display_endian = dis_info.endian = BFD_ENDIAN_BIG;
258 dis_info.display_endian = dis_info.endian = BFD_ENDIAN_LITTLE;
259 dis_info.display_endian = dis_info.endian = BFD_ENDIAN_UNKNOWN;
260 disassemble_init_for_target (&dis_info);
261 }
262
~Disasm()263 Disasm::~Disasm ()
264 {
265 delete my_stabs;
266 delete dwin;
267 delete dis_str;
268 }
269
270 void
set_img_name(char * img_fname)271 Disasm::set_img_name (char *img_fname)
272 {
273 if (stabs == NULL && img_fname && dwin == NULL)
274 {
275 dwin = new Data_window (img_fname);
276 if (dwin->not_opened ())
277 {
278 delete dwin;
279 dwin = NULL;
280 return;
281 }
282 dwin->need_swap_endian = need_swap_endian;
283 }
284 }
285
286 void
remove_disasm_hndl(void * hndl)287 Disasm::remove_disasm_hndl (void *hndl)
288 {
289 DisContext *ctx = (DisContext *) hndl;
290 delete ctx;
291 }
292
293 void
set_addr_end(uint64_t end_address)294 Disasm::set_addr_end (uint64_t end_address)
295 {
296 char buf[32];
297 int len = snprintf (buf, sizeof (buf), "%llx", (long long) end_address);
298 snprintf (addr_fmt, sizeof (addr_fmt), "%%%dllx: ", len < 8 ? 8 : len);
299 }
300
301 char *
get_disasm(uint64_t inst_address,uint64_t end_address,uint64_t start_address,uint64_t f_offset,int64_t & inst_size)302 Disasm::get_disasm (uint64_t inst_address, uint64_t end_address,
303 uint64_t start_address, uint64_t f_offset, int64_t &inst_size)
304 {
305 inst_size = 0;
306 if (inst_address >= end_address)
307 return NULL;
308 Data_window *dw = stabs ? stabs->openElf (false) : dwin;
309 if (dw == NULL)
310 return NULL;
311
312 unsigned char buffer[MAX_DISASM_STR];
313 dis_info.buffer = buffer;
314 dis_info.buffer_length = end_address - inst_address;
315 if (dis_info.buffer_length > sizeof (buffer))
316 dis_info.buffer_length = sizeof (buffer);
317 dw->get_data (f_offset + (inst_address - start_address),
318 dis_info.buffer_length, dis_info.buffer);
319
320 dis_str->setLength (0);
321 bfd abfd;
322 disassembler_ftype disassemble = disassembler (dis_info.arch, dis_info.endian,
323 dis_info.mach, &abfd);
324 if (disassemble == NULL)
325 {
326 printf ("ERROR: unsupported disassemble\n");
327 return NULL;
328 }
329 inst_addr = inst_address;
330 inst_size = disassemble (0, &dis_info);
331 if (inst_size <= 0)
332 {
333 inst_size = 0;
334 return NULL;
335 }
336 StringBuilder sb;
337 sb.appendf (addr_fmt, inst_address); // Write address
338
339 // Write hex bytes of instruction
340 if (hex_visible)
341 {
342 char bytes[64];
343 *bytes = '\0';
344 for (int i = 0; i < inst_size; i++)
345 {
346 unsigned int hex_value = buffer[i] & 0xff;
347 snprintf (bytes + 3 * i, sizeof (bytes) - 3 * i, "%02x ", hex_value);
348 }
349 const char *fmt = "%s ";
350 if (platform == Intel)
351 fmt = "%-21s "; // 21 = 3 * 7 - maximum instruction length on Intel
352 sb.appendf (fmt, bytes);
353 }
354 sb.append (dis_str);
355 return sb.toString ();
356 }
357
358 Function *
map_PC_to_func(uint64_t pc)359 Disasm::map_PC_to_func (uint64_t pc)
360 {
361 uint64_t low_pc = 0;
362 if (stabs)
363 return stabs->map_PC_to_func (pc, low_pc, NULL);
364 return NULL;
365 }
366
367 const char *
get_funcname_in_plt(uint64_t pc)368 Disasm::get_funcname_in_plt (uint64_t pc)
369 {
370 if (stabs)
371 {
372 Elf *elf = stabs->openElf (true);
373 if (elf)
374 return elf->get_funcname_in_plt (pc);
375 }
376 return NULL;
377 }
378