The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/arm/machdep_ptrace.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2004 Olivier Houchard
    3  * Copyright (c) 1994-1998 Mark Brinicombe.
    4  * Copyright (c) 1994 Brini.
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/proc.h>
   34 #include <sys/ptrace.h>
   35 #include <sys/lock.h>
   36 #include <sys/mutex.h>
   37 
   38 #include <machine/machdep.h>
   39 #include <machine/db_machdep.h>
   40 
   41 static int
   42 ptrace_read_int(struct thread *td, vm_offset_t addr, uint32_t *v)
   43 {
   44 
   45         if (proc_readmem(td, td->td_proc, addr, v, sizeof(*v)) != sizeof(*v))
   46                 return (ENOMEM);
   47         return (0);
   48 }
   49 
   50 static int
   51 ptrace_write_int(struct thread *td, vm_offset_t addr, uint32_t v)
   52 {
   53 
   54         if (proc_writemem(td, td->td_proc, addr, &v, sizeof(v)) != sizeof(v))
   55                 return (ENOMEM);
   56         return (0);
   57 }
   58 
   59 static u_int
   60 ptrace_get_usr_reg(void *cookie, int reg)
   61 {
   62         int ret;
   63         struct thread *td = cookie;
   64 
   65         KASSERT(((reg >= 0) && (reg <= ARM_REG_NUM_PC)),
   66          ("reg is outside range"));
   67 
   68         switch(reg) {
   69         case ARM_REG_NUM_PC:
   70                 ret = td->td_frame->tf_pc;
   71                 break;
   72         case ARM_REG_NUM_LR:
   73                 ret = td->td_frame->tf_usr_lr;
   74                 break;
   75         case ARM_REG_NUM_SP:
   76                 ret = td->td_frame->tf_usr_sp;
   77                 break;
   78         default:
   79                 ret = *((register_t*)&td->td_frame->tf_r0 + reg);
   80                 break;
   81         }
   82 
   83         return (ret);
   84 }
   85 
   86 static u_int
   87 ptrace_get_usr_int(void* cookie, vm_offset_t offset, u_int* val)
   88 {
   89         struct thread *td = cookie;
   90         u_int error;
   91 
   92         error = ptrace_read_int(td, offset, val);
   93 
   94         return (error);
   95 }
   96 
   97 /**
   98  * This function parses current instruction opcode and decodes
   99  * any possible jump (change in PC) which might occur after
  100  * the instruction is executed.
  101  *
  102  * @param     td                Thread structure of analysed task
  103  * @param     cur_instr         Currently executed instruction
  104  * @param     alt_next_address  Pointer to the variable where
  105  *                              the destination address of the
  106  *                              jump instruction shall be stored.
  107  *
  108  * @return    <0>               when jump is possible
  109  *            <EINVAL>          otherwise
  110  */
  111 static int
  112 ptrace_get_alternative_next(struct thread *td, uint32_t cur_instr,
  113     uint32_t *alt_next_address)
  114 {
  115         int error;
  116 
  117         if (inst_branch(cur_instr) || inst_call(cur_instr) ||
  118             inst_return(cur_instr)) {
  119                 error = arm_predict_branch(td, cur_instr, td->td_frame->tf_pc,
  120                     alt_next_address, ptrace_get_usr_reg, ptrace_get_usr_int);
  121 
  122                 return (error);
  123         }
  124 
  125         return (EINVAL);
  126 }
  127 
  128 int
  129 ptrace_single_step(struct thread *td)
  130 {
  131         struct proc *p;
  132         int error, error_alt;
  133         uint32_t cur_instr, alt_next = 0;
  134 
  135         /* TODO: This needs to be updated for Thumb-2 */
  136         if ((td->td_frame->tf_spsr & PSR_T) != 0)
  137                 return (EINVAL);
  138 
  139         KASSERT(td->td_md.md_ptrace_instr == 0,
  140          ("Didn't clear single step"));
  141         KASSERT(td->td_md.md_ptrace_instr_alt == 0,
  142          ("Didn't clear alternative single step"));
  143         p = td->td_proc;
  144         PROC_UNLOCK(p);
  145 
  146         error = ptrace_read_int(td, td->td_frame->tf_pc,
  147             &cur_instr);
  148         if (error)
  149                 goto out;
  150 
  151         error = ptrace_read_int(td, td->td_frame->tf_pc + INSN_SIZE,
  152             &td->td_md.md_ptrace_instr);
  153         if (error == 0) {
  154                 error = ptrace_write_int(td, td->td_frame->tf_pc + INSN_SIZE,
  155                     PTRACE_BREAKPOINT);
  156                 if (error) {
  157                         td->td_md.md_ptrace_instr = 0;
  158                 } else {
  159                         td->td_md.md_ptrace_addr = td->td_frame->tf_pc +
  160                             INSN_SIZE;
  161                 }
  162         }
  163 
  164         error_alt = ptrace_get_alternative_next(td, cur_instr, &alt_next);
  165         if (error_alt == 0) {
  166                 error_alt = ptrace_read_int(td, alt_next,
  167                     &td->td_md.md_ptrace_instr_alt);
  168                 if (error_alt) {
  169                         td->td_md.md_ptrace_instr_alt = 0;
  170                 } else {
  171                         error_alt = ptrace_write_int(td, alt_next,
  172                             PTRACE_BREAKPOINT);
  173                         if (error_alt)
  174                                 td->td_md.md_ptrace_instr_alt = 0;
  175                         else
  176                                 td->td_md.md_ptrace_addr_alt = alt_next;
  177                 }
  178         }
  179 
  180 out:
  181         PROC_LOCK(p);
  182         return ((error != 0) && (error_alt != 0));
  183 }
  184 
  185 int
  186 ptrace_clear_single_step(struct thread *td)
  187 {
  188         struct proc *p;
  189 
  190         /* TODO: This needs to be updated for Thumb-2 */
  191         if ((td->td_frame->tf_spsr & PSR_T) != 0)
  192                 return (EINVAL);
  193 
  194         if (td->td_md.md_ptrace_instr != 0) {
  195                 p = td->td_proc;
  196                 PROC_UNLOCK(p);
  197                 ptrace_write_int(td, td->td_md.md_ptrace_addr,
  198                     td->td_md.md_ptrace_instr);
  199                 PROC_LOCK(p);
  200                 td->td_md.md_ptrace_instr = 0;
  201         }
  202 
  203         if (td->td_md.md_ptrace_instr_alt != 0) {
  204                 p = td->td_proc;
  205                 PROC_UNLOCK(p);
  206                 ptrace_write_int(td, td->td_md.md_ptrace_addr_alt,
  207                     td->td_md.md_ptrace_instr_alt);
  208                 PROC_LOCK(p);
  209                 td->td_md.md_ptrace_instr_alt = 0;
  210         }
  211 
  212         return (0);
  213 }
  214 
  215 int
  216 ptrace_set_pc(struct thread *td, unsigned long addr)
  217 {
  218         td->td_frame->tf_pc = addr;
  219         return (0);
  220 }
  221 
  222 int
  223 arm_predict_branch(void *cookie, u_int insn, register_t pc, register_t *new_pc,
  224     u_int (*fetch_reg)(void*, int),
  225     u_int (*read_int)(void*, vm_offset_t, u_int*))
  226 {
  227         u_int addr, nregs, offset = 0;
  228         int error = 0;
  229 
  230         switch ((insn >> 24) & 0xf) {
  231         case 0x2:       /* add pc, reg1, #value */
  232         case 0x0:       /* add pc, reg1, reg2, lsl #offset */
  233                 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
  234                 if (((insn >> 16) & 0xf) == 15)
  235                         addr += 8;
  236                 if (insn & 0x0200000) {
  237                         offset = (insn >> 7) & 0x1e;
  238                         offset = (insn & 0xff) << (32 - offset) |
  239                             (insn & 0xff) >> offset;
  240                 } else {
  241                         offset = fetch_reg(cookie, insn & 0x0f);
  242                         if ((insn & 0x0000ff0) != 0x00000000) {
  243                                 if (insn & 0x10)
  244                                         nregs = fetch_reg(cookie,
  245                                             (insn >> 8) & 0xf);
  246                                 else
  247                                         nregs = (insn >> 7) & 0x1f;
  248                                 switch ((insn >> 5) & 3) {
  249                                 case 0:
  250                                         /* lsl */
  251                                         offset = offset << nregs;
  252                                         break;
  253                                 case 1:
  254                                         /* lsr */
  255                                         offset = offset >> nregs;
  256                                         break;
  257                                 default:
  258                                         break; /* XXX */
  259                                 }
  260                         }
  261                         *new_pc = addr + offset;
  262                         return (0);
  263                 }
  264 
  265         case 0xa:       /* b ... */
  266         case 0xb:       /* bl ... */
  267                 addr = ((insn << 2) & 0x03ffffff);
  268                 if (addr & 0x02000000)
  269                         addr |= 0xfc000000;
  270                 *new_pc = (pc + 8 + addr);
  271                 return (0);
  272         case 0x7:       /* ldr pc, [pc, reg, lsl #2] */
  273                 addr = fetch_reg(cookie, insn & 0xf);
  274                 addr = pc + 8 + (addr << 2);
  275                 error = read_int(cookie, addr, &addr);
  276                 *new_pc = addr;
  277                 return (error);
  278         case 0x1:       /* mov pc, reg */
  279                 *new_pc = fetch_reg(cookie, insn & 0xf);
  280                 return (0);
  281         case 0x4:
  282         case 0x5:       /* ldr pc, [reg] */
  283                 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
  284                 /* ldr pc, [reg, #offset] */
  285                 if (insn & (1 << 24))
  286                         offset = insn & 0xfff;
  287                 if (insn & 0x00800000)
  288                         addr += offset;
  289                 else
  290                         addr -= offset;
  291                 error = read_int(cookie, addr, &addr);
  292                 *new_pc = addr;
  293 
  294                 return (error);
  295         case 0x8:       /* ldmxx reg, {..., pc} */
  296         case 0x9:
  297                 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
  298                 nregs = (insn  & 0x5555) + ((insn  >> 1) & 0x5555);
  299                 nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333);
  300                 nregs = (nregs + (nregs >> 4)) & 0x0f0f;
  301                 nregs = (nregs + (nregs >> 8)) & 0x001f;
  302                 switch ((insn >> 23) & 0x3) {
  303                 case 0x0:       /* ldmda */
  304                         addr = addr - 0;
  305                         break;
  306                 case 0x1:       /* ldmia */
  307                         addr = addr + 0 + ((nregs - 1) << 2);
  308                         break;
  309                 case 0x2:       /* ldmdb */
  310                         addr = addr - 4;
  311                         break;
  312                 case 0x3:       /* ldmib */
  313                         addr = addr + 4 + ((nregs - 1) << 2);
  314                         break;
  315                 }
  316                 error = read_int(cookie, addr, &addr);
  317                 *new_pc = addr;
  318 
  319                 return (error);
  320         default:
  321                 return (EINVAL);
  322         }
  323 }

Cache object: 4751c4cb5948495d43c8e13a7f2fe560


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.