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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
|
/*
ARM EABI argument extraction
Copyright (C) 2009 Akos Pasztory <akos.pasztory@gmail.com>
This file is part of the latrace.
The latrace is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The latrace is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the latrace (file COPYING). If not, see
<http://www.gnu.org/licenses/>.
*/
#include "config.h"
static inline unsigned long align_up(unsigned long v, unsigned long a)
{
return (v + a-1) & ~(a-1);
}
/*
* The .arch field is split the following way:
* -- valid :1
* -- size :15
* -- offset :16
*/
static inline void arch_set(struct lt_arg *arg,
unsigned int size, unsigned int offs)
{
arg->arch = (void *)((size << 16) | offs | 0x80000000);
}
static inline int arch_valid(struct lt_arg *arg)
{
return (unsigned int)arg->arch & 0x80000000;
}
static inline unsigned int arch_get_size(struct lt_arg *arg)
{
return ((unsigned int)arg->arch & ~0x80000000) >> 16;
}
static inline unsigned int arch_get_offs(struct lt_arg *arg)
{
return (unsigned int)arg->arch & 0xffff;
}
/* Processes a struct entirely in memory. */
static int process_struct_mem(struct lt_config_shared *cfg, struct lt_arg *arg,
void *pval, struct lt_args_data *data)
{
int i;
unsigned long npval;
struct lt_arg *a;
lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ITSELF, arg, 0, data, 0);
npval = (unsigned long)pval;
i = 0;
lt_list_for_each_entry(a, arg->args_head, args_list) {
lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ARG, a,
(void *)(npval + arch_get_offs(a)),
data, i+1 == arg->mmbcnt);
++i;
}
return 0;
}
/* Returns a bytearray slice from OFFS with SIZE length, treating the
* registers and the stack consecutively. Return value is static,
* only valid until the next get() call. */
static void *get(unsigned long offs, int size, La_regs *regs)
{
/* NB: size of buf is fixed. But if we only copy POD types, hopefully
* it won't become a problem. */
static __thread char buf[64];
int i;
for (i = 0; i < size; ++i) {
if (offs+i < 16)
buf[i] = ((char *)regs->lr_reg)[offs+i];
else
buf[i] = *(char *)(regs->lr_sp + (offs+i-16));
}
return buf;
}
/* Calculate member sizes and offsets of a structure. Returns the
* size of the entire structure. */
static unsigned int calcstruct(struct lt_arg *arg)
{
unsigned int maxl, moffs;
struct lt_arg *a;
/* XXX: this is incorrect for struct-in-struct... */
maxl = moffs = 0;
lt_list_for_each_entry(a, arg->args_head, args_list) {
/* We need to know the strictest aligned member to
* determine the size of the entire struct. */
if (a->type_len > maxl)
maxl = a->type_len;
/* Align members naturally. */
moffs = align_up(moffs, a->type_len);
arch_set(a, a->type_len, moffs);
moffs += a->type_len;
}
return align_up(moffs, maxl);
}
/* Calculates all offsets and `real' (padded) sizes of arguments
* (including struct members). Stores them in ->arch. */
static void calcofs(struct lt_args_sym *asym)
{
int i;
unsigned int offs;
struct lt_arg *arg;
/* If function returns a struct by value larger than a word,
* it is stored in callee-allocated memory and r0 contains the
* address. */
arg = asym->args[LT_ARGS_RET];
offs = 0;
if (arg->dtype == LT_ARGS_DTYPE_STRUCT) {
if (!arg->pointer)
offs = 4;
arch_set(arg, calcstruct(arg), 0);
}
for (i = 1; i < asym->argcnt; ++i) {
unsigned int asize;
arg = asym->args[i];
/* Struct sizes/offsets should be calculated at all times. */
if (arg->dtype == LT_ARGS_DTYPE_STRUCT)
calcstruct(arg);
if (arg->dtype != LT_ARGS_DTYPE_STRUCT || arg->pointer) {
/* Non-composite type. */
/* Size is 4 if arg is smaller than a word. */
asize = arg->type_len;
if (asize < 4)
asize = 4;
/* Types are aligned to their sizes. */
offs = align_up(offs, asize);
arch_set(arg, asize, offs);
} else {
/* Composite type. */
asize = calcstruct(arg);
arch_set(arg, asize, offs);
}
offs += asize;
/* If we are allocating from registers (4*4 bytes),
* each arg must start in a new one. */
if (offs < 16)
offs = align_up(offs, 4);
}
}
/*
* -- args which fit are in r0..r3, rest is on the stack
* -- if return type is struct, r0 contains the address for it
* -- dword sized args must be aligned at even registers
* -- one argument may be split between registers and stack
* -- on stack, 8byte values are aligned % 8 == 0
*/
int lt_stack_process(struct lt_config_shared *cfg, struct lt_args_sym *asym,
La_regs *regs, struct lt_args_data *data)
{
int i;
for (i = 1; i < asym->argcnt; ++i) {
struct lt_arg *arg;
void *p;
unsigned int o, lastp;
lastp = i+1 == asym->argcnt;
arg = asym->args[i];
if (!arch_valid(arg))
calcofs(asym);
/* POD */
if (arg->dtype != LT_ARGS_DTYPE_STRUCT) {
p = get(arch_get_offs(arg), arch_get_size(arg), regs);
lt_args_cb_arg(cfg, arg, p, data, lastp, 1);
continue;
}
/* struct pointer */
if (arg->pointer) {
p = get(arch_get_offs(arg), sizeof(void *), regs);
p = *(void **)p;
lt_args_cb_arg(cfg, arg, p, data, lastp, 1);
if (cfg->args_detailed)
process_struct_mem(cfg, arg, p, data);
continue;
}
/* struct by value */
o = arch_get_offs(arg);
/* If part of the struct is in registers this outputs 'REG',
* otherwise, the stack address is printed. */
lt_args_cb_arg(cfg, arg,
(void *)(o < 16 ? 0 : regs->lr_sp + o-16),
data, lastp, 1);
if (cfg->args_detailed) {
int j;
struct lt_arg *m;
j = 0;
lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ITSELF, arg,
0, data, 0);
lt_list_for_each_entry(m, arg->args_head, args_list) {
unsigned int o;
o = arch_get_offs(arg) + arch_get_offs(m);
p = get(o, arch_get_size(m), regs);
lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ARG, m,
p, data, j+1 == arg->mmbcnt);
++j;
}
}
}
return 0;
}
/*
* -- 32bit return values in r0
* -- 64bit in {r0, r1}
* -- composite < 4bytes in r0
* -- composite > 4bytes in memory (address is passed as the first parameter
* of the call)
*/
int lt_stack_process_ret(struct lt_config_shared *cfg,
struct lt_args_sym *asym, La_retval *regs,
struct lt_args_data *data)
{
struct lt_arg *arg;
void *pval;
/* I think this works for both 32 and 64 bit values (r0 and r1
* are consecutive in lrv_reg[]). */
pval = &(regs->lrv_reg[0]);
arg = asym->args[LT_ARGS_RET];
lt_args_cb_arg(cfg, arg, pval, data, 1, 0);
if (cfg->args_detailed && arg->dtype == LT_ARGS_DTYPE_STRUCT) {
unsigned int asize;
/* AAPCS says: a composite type less than 4 bytes is
* returned in r0. Larger types have memory allocated
* by caller and its address passed as the (hidden)
* 1st argument.
*
* NB: Though I, personally, haven't found it in the
* AAPCS, it's both logical (and apparently gcc does
* so) to return this address in r0. */
asize = arch_get_size(arg);
if (arg->pointer || asize > 4)
pval = (void *)regs->lrv_reg[0];
else if (asize > 4)
pval = ®s->lrv_reg[0];
process_struct_mem(cfg, arg, pval, data);
}
return 0;
}
/* End of stack.c */
|