1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
// SPDX-License-Identifier: GPL-2.0
#include <stdlib.h>
#include <string.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/zalloc.h>
#include "../../util/disasm.h"
#include "../../util/map.h"
#include "../../util/maps.h"
#include "../../util/symbol.h"
#include "../../util/thread.h"
/*
* Alpha control-transfer instructions, as printed by objdump:
*
* PC-relative (opcode group 0x30-0x3f), 21-bit displacement:
* br, bsr unconditional / to-subroutine
* beq bne blt ble bgt bge blbc blbs integer conditional
* fbeq fbne fblt fble fbgt fbge floating conditional
*
* Register-indirect (JSR group, opcode 0x1a):
* jmp, jsr, ret, jcr
*
* bsr/jsr (and jcr, the coroutine form, which binutils prints in preference to
* the jsr_coroutine spelling) save a return address, so they are calls; ret
* returns; everything else that transfers control is a jump.
*
* Alpha has no machine "mov"; objdump prints "mov"/"fmov" as pseudos for
* bis/cpys, so map them to mov_ops when present. The no-ops are deliberately
* left alone: nop_ops would let delete_last_nop() trim the padding gcc leaves
* at the end of a function, but its scnprintf() prints the literal "nop", and
* Alpha pads with unop (ldq_u $31) rather than nop.
*/
/*
* The generic call__parse() expects the target address to be the first thing
* in the operand string, but a bsr prints its return-address register first:
*
* bsr t0,fffffc0001031dc0 <cserve_ena>
*
* so take the address from after the comma. Without this the address comes
* out as 0, and neither the callee symbol nor the annotation browser's
* "go to target" work.
*/
static int alpha_call__parse(const struct arch *arch, struct ins_operands *ops,
struct map_symbol *ms,
struct disasm_line *dl __maybe_unused)
{
char *endptr, *tok, *name;
struct map *map = ms->map;
struct addr_map_symbol target;
tok = strchr(ops->raw, ',');
if (tok == NULL)
return -1;
ops->target.addr = strtoull(tok + 1, &endptr, 16);
if (endptr == tok + 1)
return -1;
/* A stripped object has no "<symbol>" to name the target with. */
name = strchr(endptr, '<');
if (name == NULL)
goto find_target;
name++;
if (arch->objdump.skip_functions_char &&
strchr(name, arch->objdump.skip_functions_char))
return -1;
tok = strchr(name, '>');
if (tok == NULL)
return -1;
*tok = '\0';
ops->target.name = strdup(name);
*tok = '>';
if (ops->target.name == NULL)
return -1;
find_target:
target = (struct addr_map_symbol) {
.ms = { .map = map__get(map), },
.addr = map__objdump_2mem(map, ops->target.addr),
};
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map,
map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
ops->target.sym = target.ms.sym;
addr_map_symbol__exit(&target);
return 0;
}
static const struct ins_ops alpha_call_ops = {
.parse = alpha_call__parse,
.scnprintf = call__scnprintf,
.is_call = true,
};
/*
* jsr and jmp transfer control to a register, and their trailing operand is
* only a branch prediction hint:
*
* jsr ra,(t12),fffffc0001014ee8 <_printk>
*
* binutils extracts that hint as a 14-bit signed field scaled by four and
* prints it relative to the next instruction (extract_jhint() in alpha-opc.c,
* print_insn_alpha() in alpha-dis.c), so it can name the callee only when the
* callee lies within the resulting +-32KB. It also defaults to zero, which
* prints as the next instruction. Most hints are therefore not the callee at
* all, and parsing one would invent a call target, so these resolve no target
* and keep their operands, as an indirect call does elsewhere. The hint on
* jcr is not even an address.
*/
static const struct ins_ops alpha_indirect_call_ops = {
.scnprintf = ins__raw_scnprintf,
.is_call = true,
};
static const struct ins_ops alpha_indirect_jump_ops = {
.scnprintf = ins__raw_scnprintf,
.is_jump = true,
};
static int is_alpha_cond_branch(const char *name)
{
static const char *const branches[] = {
"beq", "bne", "blt", "ble", "bgt", "bge", "blbc", "blbs",
"fbeq", "fbne", "fblt", "fble", "fbgt", "fbge",
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(branches); i++) {
if (!strcmp(name, branches[i]))
return 1;
}
return 0;
}
static const struct ins_ops *alpha__associate_instruction_ops(struct arch *arch, const char *name)
{
const struct ins_ops *ops = NULL;
if (!strcmp(name, "bsr")) {
ops = &alpha_call_ops;
} else if (!strcmp(name, "jsr") ||
!strcmp(name, "jcr")) {
ops = &alpha_indirect_call_ops;
} else if (!strcmp(name, "ret")) {
ops = &ret_ops;
} else if (!strcmp(name, "jmp")) {
ops = &alpha_indirect_jump_ops;
} else if (!strcmp(name, "br") ||
is_alpha_cond_branch(name)) {
ops = &jump_ops;
} else if (!strcmp(name, "mov") ||
!strcmp(name, "fmov")) {
ops = &mov_ops;
}
if (ops)
arch__associate_ins_ops(arch, name, ops);
return ops;
}
const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id,
const char *cpuid __maybe_unused)
{
struct arch *arch = zalloc(sizeof(*arch));
if (!arch)
return NULL;
arch->name = "alpha";
arch->id = *id;
arch->associate_instruction_ops = alpha__associate_instruction_ops;
/* objdump emits no comments for Alpha; '#' is what the assembler uses. */
arch->objdump.comment_char = '#';
return arch;
}
|