xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/src/Disasm.cc (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
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