]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gprofng/src/Disasm.cc
Update year range in gprofng copyright notices
[thirdparty/binutils-gdb.git] / gprofng / src / Disasm.cc
CommitLineData
76bdc726 1/* Copyright (C) 2021-2023 Free Software Foundation, Inc.
bb368aad
VM
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
38struct DisContext
39{
40 bool is_Intel;
41 Stabs *stabs;
42 uint64_t pc; // first_pc <= pc < last_pc
43 uint64_t first_pc;
44 uint64_t last_pc;
45 uint64_t f_offset; // file offset for first_pc
46 int codeptr[4]; // longest instruction length may not be > 16
47 Data_window *elf;
48};
49
50static const int MAX_DISASM_STR = 2048;
51static const int MAX_INSTR_SIZE = 8;
52
53Disasm::Disasm (char *fname)
54{
55 dwin = NULL;
56 dis_str = NULL;
57 need_swap_endian = false;
58 my_stabs = Stabs::NewStabs (fname, fname);
59 if (my_stabs == NULL)
60 return;
61 stabs = my_stabs;
62 platform = stabs->get_platform ();
63 disasm_open ();
64}
65
66Disasm::Disasm (Platform_t _platform, Stabs *_stabs)
67{
68 dwin = NULL;
69 dis_str = NULL;
70 need_swap_endian = false;
71 stabs = _stabs;
72 platform = _platform;
73 my_stabs = NULL;
74 disasm_open ();
75}
76
77static int
78fprintf_func (void *arg, const char *fmt, ...)
79{
80 char buf[512];
81 va_list vp;
82 va_start (vp, fmt);
83 int cnt = vsnprintf (buf, sizeof (buf), fmt, vp);
84 va_end (vp);
85
86 Disasm *dis = (Disasm *) arg;
87 dis->dis_str->append (buf);
88 return cnt;
89}
90
9f184a64
VM
91static int
92fprintf_styled_func (void *arg, enum disassembler_style st ATTRIBUTE_UNUSED,
93 const char *fmt, ...)
94{
95 char buf[512];
96 va_list vp;
97 va_start (vp, fmt);
98 int cnt = vsnprintf (buf, sizeof (buf), fmt, vp);
99 va_end (vp);
100
101 Disasm *dis = (Disasm *) arg;
102 dis->dis_str->append (buf);
103 return cnt;
104}
105
bb368aad
VM
106/* Get LENGTH bytes from info's buffer, at target address memaddr.
107 Transfer them to myaddr. */
108static int
109read_memory_func (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length,
110 disassemble_info *info)
111{
112 unsigned int opb = info->octets_per_byte;
113 size_t end_addr_offset = length / opb;
114 size_t max_addr_offset = info->buffer_length / opb;
115 size_t octets = (memaddr - info->buffer_vma) * opb;
116 if (memaddr < info->buffer_vma
117 || memaddr - info->buffer_vma > max_addr_offset
118 || memaddr - info->buffer_vma + end_addr_offset > max_addr_offset
119 || (info->stop_vma && (memaddr >= info->stop_vma
120 || memaddr + end_addr_offset > info->stop_vma)))
121 return -1;
122 memcpy (myaddr, info->buffer + octets, length);
123 return 0;
124}
125
126static void
127print_address_func (bfd_vma addr, disassemble_info *info)
128{
129 (*info->fprintf_func) (info->stream, "0x%llx", (unsigned long long) addr);
130}
131
132static asymbol *
133symbol_at_address_func (bfd_vma addr ATTRIBUTE_UNUSED,
134 disassemble_info *info ATTRIBUTE_UNUSED)
135{
136 return NULL;
137}
138
139static bfd_boolean
140symbol_is_valid (asymbol * sym ATTRIBUTE_UNUSED,
141 disassemble_info *info ATTRIBUTE_UNUSED)
142{
143 return TRUE;
144}
145
146static void
147memory_error_func (int status, bfd_vma addr, disassemble_info *info)
148{
149 info->fprintf_func (info->stream, "Address 0x%llx is out of bounds.\n",
150 (unsigned long long) addr);
151}
152
153void
154Disasm::disasm_open ()
155{
156 hex_visible = 1;
157 snprintf (addr_fmt, sizeof (addr_fmt), NTXT ("%s"), NTXT ("%8llx: "));
158 if (dis_str == NULL)
159 dis_str = new StringBuilder;
160
161 switch (platform)
162 {
163 case Aarch64:
164 case Intel:
165 case Amd64:
166 need_swap_endian = (DbeSession::platform == Sparc);
167 break;
168 case Sparcv8plus:
169 case Sparcv9:
170 case Sparc:
171 default:
172 need_swap_endian = (DbeSession::platform != Sparc);
173 break;
174 }
175
176 memset (&dis_info, 0, sizeof (dis_info));
177 dis_info.flavour = bfd_target_unknown_flavour;
178 dis_info.endian = BFD_ENDIAN_UNKNOWN;
179 dis_info.endian_code = dis_info.endian;
180 dis_info.octets_per_byte = 1;
181 dis_info.disassembler_needs_relocs = FALSE;
182 dis_info.fprintf_func = fprintf_func;
9f184a64 183 dis_info.fprintf_styled_func = fprintf_styled_func;
bb368aad
VM
184 dis_info.stream = this;
185 dis_info.disassembler_options = NULL;
186 dis_info.read_memory_func = read_memory_func;
187 dis_info.memory_error_func = memory_error_func;
188 dis_info.print_address_func = print_address_func;
189 dis_info.symbol_at_address_func = symbol_at_address_func;
190 dis_info.symbol_is_valid = symbol_is_valid;
191 dis_info.display_endian = BFD_ENDIAN_UNKNOWN;
192 dis_info.symtab = NULL;
193 dis_info.symtab_size = 0;
194 dis_info.buffer_vma = 0;
195 switch (platform)
196 {
197 case Aarch64:
198 dis_info.arch = bfd_arch_aarch64;
199 dis_info.mach = bfd_mach_aarch64;
200 break;
201 case Intel:
202 case Amd64:
203 dis_info.arch = bfd_arch_i386;
204 dis_info.mach = bfd_mach_x86_64;
205 break;
206 case Sparcv8plus:
207 case Sparcv9:
208 case Sparc:
209 default:
210 dis_info.arch = bfd_arch_unknown;
211 dis_info.endian = BFD_ENDIAN_UNKNOWN;
212 break;
213 }
214 dis_info.display_endian = dis_info.endian = BFD_ENDIAN_BIG;
215 dis_info.display_endian = dis_info.endian = BFD_ENDIAN_LITTLE;
216 dis_info.display_endian = dis_info.endian = BFD_ENDIAN_UNKNOWN;
217 disassemble_init_for_target (&dis_info);
218}
219
220Disasm::~Disasm ()
221{
222 delete my_stabs;
223 delete dwin;
224 delete dis_str;
225}
226
227void
228Disasm::set_img_name (char *img_fname)
229{
230 if (stabs == NULL && img_fname && dwin == NULL)
231 {
232 dwin = new Data_window (img_fname);
233 if (dwin->not_opened ())
234 {
235 delete dwin;
236 dwin = NULL;
237 return;
238 }
239 dwin->need_swap_endian = need_swap_endian;
240 }
241}
242
243void
244Disasm::remove_disasm_hndl (void *hndl)
245{
246 DisContext *ctx = (DisContext *) hndl;
247 delete ctx;
248}
249
250#if 0
251int
252Disasm::get_instr_size (uint64_t vaddr, void *hndl)
253{
254 DisContext *ctx = (DisContext *) hndl;
255 if (ctx == NULL || vaddr < ctx->first_pc || vaddr >= ctx->last_pc)
256 return -1;
257 ctx->pc = vaddr;
258 size_t sz = ctx->is_Intel ? sizeof (ctx->codeptr) : 4;
259 if (sz > ctx->last_pc - vaddr)
260 sz = (size_t) (ctx->last_pc - vaddr);
261 if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc),
262 sz, ctx->codeptr) == NULL)
263 return -1;
264
265 char buf[MAX_DISASM_STR];
266 *buf = 0;
267 uint64_t inst_vaddr = vaddr;
268#if MEZ_NEED_TO_FIX
269 size_t instrs_cnt = 0;
270 disasm_err_code_t status = disasm (handle, &inst_vaddr, ctx->last_pc, 1,
271 ctx, buf, sizeof (buf), &instrs_cnt);
272 if (instrs_cnt != 1 || status != disasm_err_ok)
273 return -1;
274#endif
275 return (int) (inst_vaddr - vaddr);
276}
277#endif
278
279void
280Disasm::set_addr_end (uint64_t end_address)
281{
282 char buf[32];
283 int len = snprintf (buf, sizeof (buf), "%llx", (long long) end_address);
284 snprintf (addr_fmt, sizeof (addr_fmt), "%%%dllx: ", len < 8 ? 8 : len);
285}
286
287char *
288Disasm::get_disasm (uint64_t inst_address, uint64_t end_address,
289 uint64_t start_address, uint64_t f_offset, int64_t &inst_size)
290{
291 inst_size = 0;
292 if (inst_address >= end_address)
293 return NULL;
294 Data_window *dw = stabs ? stabs->openElf (false) : dwin;
295 if (dw == NULL)
296 return NULL;
297
298 unsigned char buffer[MAX_DISASM_STR];
299 dis_info.buffer = buffer;
300 dis_info.buffer_length = end_address - inst_address;
301 if (dis_info.buffer_length > sizeof (buffer))
302 dis_info.buffer_length = sizeof (buffer);
303 dw->get_data (f_offset + (inst_address - start_address),
304 dis_info.buffer_length, dis_info.buffer);
305
306 dis_str->setLength (0);
307 bfd abfd;
308 disassembler_ftype disassemble = disassembler (dis_info.arch, dis_info.endian,
309 dis_info.mach, &abfd);
310 if (disassemble == NULL)
311 {
312 printf ("ERROR: unsupported disassemble\n");
313 return NULL;
314 }
315 inst_size = disassemble (0, &dis_info);
316 if (inst_size <= 0)
317 {
318 inst_size = 0;
319 return NULL;
320 }
321 StringBuilder sb;
322 sb.appendf (addr_fmt, inst_address); // Write address
323
324 // Write hex bytes of instruction
325 if (hex_visible)
326 {
327 char bytes[64];
328 *bytes = '\0';
329 for (int i = 0; i < inst_size; i++)
330 {
331 unsigned int hex_value = buffer[i] & 0xff;
332 snprintf (bytes + 3 * i, sizeof (bytes) - 3 * i, "%02x ", hex_value);
333 }
334 const char *fmt = "%s ";
335 if (platform == Intel)
336 fmt = "%-21s "; // 21 = 3 * 7 - maximum instruction length on Intel
337 sb.appendf (fmt, bytes);
338 }
339 sb.append (dis_str);
340#if MEZ_NEED_TO_FIX
341 // Write instruction
342 if (ctx.is_Intel) // longest instruction length for Intel is 7
343 sb.appendf (NTXT ("%-7s %s"), parts_array[1], parts_array[2]);
344 else // longest instruction length for SPARC is 11
345 sb.appendf (NTXT ("%-11s %s"), parts_array[1], parts_array[2]);
346 if (strcmp (parts_array[1], NTXT ("call")) == 0)
347 {
348 if (strncmp (parts_array[2], NTXT ("0x"), 2) == 0)
349 sb.append (GTXT ("\t! (Unable to determine target symbol)"));
350 }
351#endif
352 return sb.toString ();
353}
354
355#if MEZ_NEED_TO_FIX
356void *
357Disasm::get_inst_ptr (disasm_handle_t, uint64_t vaddr, void *pass_through)
358{
359 // Actually it fetches only one instruction at a time for sparc,
360 // and one byte at a time for intel.
361 DisContext *ctx = (DisContext*) pass_through;
362 size_t sz = ctx->is_Intel ? 1 : 4;
363 if (vaddr + sz > ctx->last_pc)
364 {
365 ctx->codeptr[0] = -1;
366 return ctx->codeptr;
367 }
368 if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc), sz, ctx->codeptr) == NULL)
369 {
370 ctx->codeptr[0] = -1;
371 return ctx->codeptr;
372 }
373 if (ctx->elf->need_swap_endian && !ctx->is_Intel)
374 ctx->codeptr[0] = ctx->elf->decode (ctx->codeptr[0]);
375 return ctx->codeptr;
376}
377
378// get a symbol name for an address
379disasm_err_code_t
380Disasm::get_sym_name (disasm_handle_t, // an open disassembler handle
381 uint64_t target_address, // the target virtual address
382 uint64_t inst_address, // virtual address of instruction
383 // being disassembled
384 int use_relocation, // flag to use relocation information
385 char *buffer, // places the symbol here
386 size_t buffer_size, // limit on symbol length
387 int *, // sys/elf_{SPARC.386}.h
388 uint64_t *offset, // from the symbol to the address
389 void *pass_through) // disassembler context
390{
391 char buf[MAXPATHLEN];
392 if (!use_relocation)
393 return disasm_err_symbol;
394
395 DisContext *ctxp = (DisContext*) pass_through;
396 char *name = NULL;
397 if (ctxp->stabs)
398 {
399 uint64_t addr = ctxp->f_offset + (inst_address - ctxp->first_pc);
400 name = ctxp->stabs->sym_name (target_address, addr, use_relocation);
401 }
402 if (name == NULL)
403 return disasm_err_symbol;
404
405 char *s = NULL;
406 if (*name == '_')
407 s = cplus_demangle (name, DMGL_PARAMS);
408 if (s)
409 {
410 snprintf (buffer, buffer_size, NTXT ("%s"), s);
411 free (s);
412 }
413 else
414 snprintf (buffer, buffer_size, NTXT ("%s"), name);
415
416 *offset = 0;
417 return disasm_err_ok;
418}
419#endif