summaryrefslogtreecommitdiff
path: root/gdb/arc-arguments.c
blob: bd3404a69864ded95e71584072dcc2e8b4d18818 (plain)
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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/* Target dependent code for ARC processor family, for GDB, the GNU debugger.

   Copyright 2008, 2009 Free Software Foundation, Inc.

   Contributed by ARC International (www.arc.com)

   Authors:
      Richard Stuckey      <richard.stuckey@arc.com>

   This file is part of GDB.

   This program 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.

   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */

/******************************************************************************/
/*                                                                            */
/* Outline:                                                                   */
/*     This module implements operations for setting up the command line      */
/*     arguments to the program which is being debugged.                      */
/*                                                                            */
/*     E.g. if we are passing 4 arguments to the program's 'main' function,   */
/*          we must place them on the stack in the layout:                    */
/*                                                                            */
/*        .                                                                   */
/*        .                                                                   */
/*     stack[top + A3] <== <arg_3>                                            */
/*        .                                                                   */
/*        .                                                                   */
/*     stack[top + A2] <== <arg_2>                                            */
/*        .                                                                   */
/*        .                                                                   */
/*     stack[top + A1] <== <arg_1>                                            */
/*        .                                                                   */
/*        .                                                                   */
/*     stack[top + A0] <== <arg_0>                                            */
/*     stack[top + 24] <== 0x0           # ? NULL terminator                  */
/*     stack[top + 20] <== 0x0           # envp NULL terminator               */
/*     stack[top + 16] <== 0x0           # argv NULL terminator               */
/*     stack[top + 12] <== TOP + A3      # argv[3]                            */
/*     stack[top +  8] <== TOP + A2      # argv[2]                            */
/*     stack[top +  4] <== TOP + A1      # argv[1]                            */
/*     stack[top +  0] <== TOP + A0      # argv[0]                            */
/*                                                                            */
/*     where TOP = &stack[top]                                                */
/*       and A0 .. A3 are the offsets of the stored arguments from the stack  */
/*                    top.                                                    */
/*                                                                            */
/******************************************************************************/

/* system header files */
#include <byteswap.h>

/* gdb header files */
#include "defs.h"
#include "target.h"
#include "symtab.h"
#include "regcache.h"
#include "objfiles.h"

/* ARC header files */
#include "arc-arguments.h"
#include "arc-support.h"
#include "arc-tdep.h"


/* -------------------------------------------------------------------------- */
/*                               local data                                   */
/* -------------------------------------------------------------------------- */

#define MINIMUM_INSTRUCTION_SIZE       2
#define MOV_SP_INSTRUCTION_LE          0x3F80240A
#define MOV_SP_INSTRUCTION_BE          0x803F0A24


/* The address of the top of the target program's stack before the program
   arguments were pushed onto the stack.  */
static CORE_ADDR old_stack_top;

/* The address in the target program code of the instructions which set up the
   stack pointer (SP) at program start-up.  */
static CORE_ADDR stack_pointer_setup_code_operand_address;


/* -------------------------------------------------------------------------- */
/*                               local macros                                 */
/* -------------------------------------------------------------------------- */

#define TARGET_IS_BIG_ENDIAN  (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)

#define TARGET_ENDIAN(word)   ((TARGET_IS_BIG_ENDIAN) ? __bswap_32(word) : (word))

#define MK_OPERAND_LE(x)      (ARC_Word) ((((x) & 0xffff0000) >> 16) | \
                                          (((x) & 0x0000ffff) << 16))

#define MK_OPERAND_BE(x)      (ARC_Word) (__bswap_32(x))


/* -------------------------------------------------------------------------- */
/*                               local functions                              */
/* -------------------------------------------------------------------------- */

/* Find the address in the target program code of the given label.
   Return 0 if the label is not found.  */

static CORE_ADDR
find_label (const char *label)
{
    struct minimal_symbol *msymbol = lookup_minimal_symbol(label, NULL, symfile_objfile);
    CORE_ADDR              address = 0;

    if (msymbol != NULL)
        address = SYMBOL_VALUE_ADDRESS (msymbol);

    DEBUG("%s = %x\n", label, (unsigned int) address);

    return address;
}


/* Write a word of data to target memory.

   Parameters:
       address : the address in memory to write the data
       word    : the data to be written

   Returns TRUE if the data is written. FALSE otherwise.

   If the data is written, the given address is incremented to reference the
   next word in target memory.  */

static Boolean
write_word (CORE_ADDR *address, ARC_Word word)
{
    word = TARGET_ENDIAN(word);

    if (target_write_memory(*address, (gdb_byte*) &word, BYTES_IN_WORD) == 0)
    {
        *address += BYTES_IN_WORD;
        return TRUE;
    }

    return FALSE;
}


/* Try to find the address in the target program code of the instructions which
   set up the program stack pointer (SP).

   Parameter:
       stack_top: the address which is loaded into SP by the setup code

   Return TRUE if the code is found.

   If the setup code is found, the variable stack_pointer_setup_code_operand_address
   is set to the code's address.  */

static Boolean
find_stack_top_setup_code (CORE_ADDR stack_top)
{
    /* Try to find the start address in the target program.  */
    CORE_ADDR code_start = find_label("__start");

    if (code_start != 0)
    {
        CORE_ADDR code = (ARC_Address) code_start;
        ARC_Word  set_sp_insn[2];
        ARC_Byte  buffer[16 * BYTES_IN_WORD];

        DEBUG("setting up arguments: stack_top = %x, code_start = %x\n",
              (unsigned int) stack_top, (unsigned int) code_start);

        if (TARGET_IS_BIG_ENDIAN)
        {
            set_sp_insn[0] = MOV_SP_INSTRUCTION_BE;
            set_sp_insn[1] = MK_OPERAND_BE(stack_top);
        }
        else
        {
            set_sp_insn[0] = MOV_SP_INSTRUCTION_LE;
            set_sp_insn[1] = MK_OPERAND_LE(stack_top);
        }

        /* Scan through the start code of the program, looking for the code that
           sets up the program's stack pointer; we recognize this as a 32-bit
           'mov sp' instruction followed by a 32-bit operand which is the
           address of the stack top (which we obtained from the executable file).  */
        while (TRUE)
        {
            int result = target_read_memory(code, (gdb_byte*) buffer, (int) sizeof(buffer));

            if (result == 0)
            {
                size_t offset = 0;

                while (offset <= sizeof(buffer) - sizeof(set_sp_insn))
                {
                    if (memcmp(buffer + offset, set_sp_insn, sizeof(set_sp_insn)) == 0)
                    {
                        stack_pointer_setup_code_operand_address = code + (CORE_ADDR) offset + BYTES_IN_WORD;

                        DEBUG("found 'mov sp, <stacktop>' instruction operand at address 0x%x\n",
                              (unsigned int) stack_pointer_setup_code_operand_address);
                        return TRUE;
                    }

                    offset += MINIMUM_INSTRUCTION_SIZE;
                }
            }
            else
            {
                warning(_("can not find read target program start code"));
                break;
            }

            code += (CORE_ADDR) (sizeof(buffer) - sizeof(set_sp_insn));

            /* If we haven't found it in the first 100 bytes.  */
            if (code - code_start > 100)
            {
                warning(_("can not find 'mov sp, <stacktop>' instruction in start code"));
                break;
            }
        }
    }

    return FALSE;
}


/* Try to change the setup code in the program so that SP is loaded with a
   given address.

   Parameters:
       old_stack_top: the address which is currently loaded into SP by the code
       new_stack_top: the address which we wish to be loaded into SP by the code

   Return TRUE if the setup code is changed.  */

static Boolean
set_stack_top (CORE_ADDR old_stack_top, CORE_ADDR new_stack_top)
{
    ARC_Word operand = (TARGET_IS_BIG_ENDIAN) ? MK_OPERAND_BE(new_stack_top)
                                              : MK_OPERAND_LE(new_stack_top);

    /* If we do not yet know the address in the program code at which the
       program's stack pointer is set up.  */
    if (stack_pointer_setup_code_operand_address == 0)
    {
        /* Try to find it.  */
        if (!find_stack_top_setup_code(old_stack_top))
            return FALSE;
    }

    DEBUG("set stack top @ 0x%08X to 0x%08X (0x%08X)\n",
          (unsigned int) stack_pointer_setup_code_operand_address,
          (unsigned int) new_stack_top,
          operand);

    return (target_write_memory(stack_pointer_setup_code_operand_address,
                                (gdb_byte*) &operand,
                                BYTES_IN_WORD) == 0);
}


/* -------------------------------------------------------------------------- */
/*                               externally visible functions                 */
/* -------------------------------------------------------------------------- */

/* This is called when a program is downloaded to the debug target. */

void
arc_program_loaded (void)
{
    /* The program has just been loaded, so we do not yet know the address in
       the program code at which the program's stack pointer is set up.  */
    stack_pointer_setup_code_operand_address = 0;
}


/* Store the program's arguments on the stack.
   Return TRUE if they are stored successfully.  */

Boolean
arc_setup_arguments (char *args)
{
    Boolean done = FALSE;

    /* Try to find the top of stack in the target program.  */
    old_stack_top = find_label("__stack_top");

    if (old_stack_top != 0)
    {
        char       **argv = buildargv (args);
        char       **argp;
        size_t       string_length = 0;
        unsigned int argc          = 0;
        unsigned int num_pointers;
        unsigned int total_size;
        CORE_ADDR    new_stack_top;

        if (argv == NULL)
            nomem (0);

        /* Calculate the space required to hold the args.  */

        for (argp = argv; *argp != NULL; argp++)
        {
            string_length += strlen (*argp) + 1;
            argc++;
        }

        DEBUG("%d arguments\n", argc);

        num_pointers = argc + 3;

        total_size = (unsigned int) string_length + num_pointers * BYTES_IN_WORD;

        /* Round up to a multiple of 32: strlen expects memory to come in chunks
         * that are at least cache-line (32 bytes) sized.  */
        total_size +=  31;
        total_size &= -32;

        DEBUG("total size: %d\n", total_size);

        new_stack_top = old_stack_top - total_size;

        DEBUG("new stack top: 0x%08x\n", (unsigned int) new_stack_top);

        /* Adjust the setting of the top of the stack in the object code.  */
        if (set_stack_top(old_stack_top, new_stack_top))
        {
            struct regcache *regcache   = get_current_regcache();
            CORE_ADDR        data_space = new_stack_top + num_pointers * BYTES_IN_WORD;
            CORE_ADDR        stack_top  = new_stack_top;
            unsigned int i;

            DEBUG("data space: 0x%08x\n", (unsigned int) data_space);

            done = TRUE;

            /* Write the args onto the top of the stack.  */

            for (i = 0; i < argc; i++)
            {
                char  *parameter = argv[i];
                size_t length    = strlen(parameter) + 1;
                int    result    = target_write_memory(data_space, (gdb_byte*) parameter, (int) length);

                if (result == 0)
                {
                    DEBUG("written argv[%d] to 0x%08x: \"%s\"\n",
                          i, (unsigned int) data_space, parameter);
                }
                else
                    done = FALSE;

                /* Write a pointer to the argument onto the stack.  */
                if (!write_word(&stack_top, (ARC_Word) data_space))
                    done = FALSE;

                data_space += length;
            }

            /* Try to write the NULLs.  */
            if (!write_word(&stack_top, 0) ||
                !write_word(&stack_top, 0) ||
                !write_word(&stack_top, 0))
                done = FALSE;

            /* Set up the R0 and R1 parameter registers.  */

            /* Convert to target byte order if necessary.  */
            if (HOST_AND_TARGET_ENDIANNESS_DIFFER(current_gdbarch))
            {
                 argc          = __bswap_32(argc);
                 new_stack_top = __bswap_32(new_stack_top);
            }

            regcache_raw_supply   (regcache, 0, &argc);
            regcache_raw_supply   (regcache, 1, &new_stack_top);
            target_store_registers(regcache, 0);
            target_store_registers(regcache, 1);
        }

        freeargv(argv);
    }

    return done;
}


/* This function is called just before disconnection from the debug target.  */

void
arc_restore_stack_top_address (void)
{
    /* If we know the address in the program start-up code at which the stack
       pointer is set up, it must be because we changed the stack top address
       in the code - so change it back to the original address as read from the
       excutable file.

       This is done so that if the user disconnects from the target, then
       reconnects to it in a subsequent debugging session but does NOT download
       the program to the target again (as it is still in target memory), the
       mechanism for altering the stack top will still work.

       Note that this has no effect if the target is allowed to resume execution
       (i.e. a 'detach' is being performed) as we are changing code that has
       already been executed.

       0 is passed as the "old" stack top as it is not used in this situation.  */

    if (stack_pointer_setup_code_operand_address != 0)
        (void) set_stack_top(0, old_stack_top);
}

/******************************************************************************/