[Ltrace-devel] [PATCH v3] Tracing PLT-less MIPS binaries

Sedat Dilek sedat.dilek at gmail.com
Mon Feb 9 05:29:39 UTC 2015


On Mon, Feb 9, 2015 at 6:15 AM, Faraz Shahbazker
<Faraz.Shahbazker at imgtec.com> wrote:
> Okay, this version marks all symbols as unresolved initially and modifies them
> later in arch_dynlink_done. Hope I've covered all the other remarks.
>

Hi,

Thanks for all your efforts.

Against which Git branch is this patch?
master?
pmachata/mips?

A "changelog" within your patches will help to reflect on "the other
remarks" :-).
It's good for you and followers.

Regards,
- Sedat -

> Regards,
> Faraz Shahbazker
>
> _---
>  sysdeps/linux-gnu/mips/plt.c   |  110 ++++++++++++++++++++++++----------------
>  sysdeps/linux-gnu/mips/trace.c |   93 +++++++++++++++++++++++++++++++--
>  2 files changed, 156 insertions(+), 47 deletions(-)
>
> diff --git a/sysdeps/linux-gnu/mips/plt.c b/sysdeps/linux-gnu/mips/plt.c
> index 84e2234..9084bb9 100644
> --- a/sysdeps/linux-gnu/mips/plt.c
> +++ b/sysdeps/linux-gnu/mips/plt.c
> @@ -139,7 +139,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela)
>  {
>         debug(1,"plt_addr %zx ndx %#zx",lte->arch.pltgot_addr, ndx);
>
> -       if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
> +       if (mips_elf_is_cpic(lte->ehdr.e_flags) && lte->plt_addr != 0) {
>                 /* Return a pointer into the PLT.  */
>                 return lte->plt_addr + 16 * 2 + (ndx * 16);
>         } else {
> @@ -204,7 +204,7 @@ arch_elf_init(struct ltelf *lte, struct library *lib)
>          * to pick up relocations to external functions.  Right now
>          * function pointers from the main binary to external functions
>          * can't be traced in CPIC mode.  */
> -       if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
> +       if (mips_elf_is_cpic(lte->ehdr.e_flags) && lte->plt_addr != 0) {
>                 return 0; /* We are already done.  */
>         }
>
> @@ -358,11 +358,6 @@ arch_symbol_ret(struct process *proc, struct library_symbol *libsym)
>  {
>  }
>
> -void
> -arch_dynlink_done(struct process *proc)
> -{
> -}
> -
>  static int
>  read_got_entry(struct process *proc, GElf_Addr addr, GElf_Addr *valp)
>  {
> @@ -385,16 +380,52 @@ struct mips_unresolve_data {
>         GElf_Addr stub_addr;
>  };
>
> +void
> +arch_dynlink_done(struct process *proc)
> +{
> +       struct library_symbol *libsym = NULL;
> +
> +       while ((libsym = proc_each_symbol(proc, libsym,
> +                                         library_symbol_delayed_cb, NULL))) {
> +               GElf_Addr got_entry_value = 0;
> +               GElf_Addr resolved_value = libsym->arch.resolved_value;
> +
> +               if (read_got_entry(proc, libsym->arch.got_entry_addr,
> +                                  &got_entry_value) < 0) {
> +                       perror("dynlink_done: read got entry");
> +                       return;
> +               }
> +
> +               if ((got_entry_value != resolved_value)) &&
> +                       (got_entry_value != 0)) {
> +                       fprintf(stderr, "%s resolved\n", libsym->name);
> +                       libsym->arch.type = MIPS_PLT_NEED_UNRESOLVE;
> +                       libsym->arch.data = malloc(sizeof *libsym->arch.data);
> +                       if (libsym->arch.data == NULL) {
> +                               perror("dynlink done: malloc unresolve data");
> +                               return;
> +                       }
> +
> +                       libsym->arch.data->self = libsym->arch.data;
> +                       libsym->arch.data->got_entry_value = got_entry_value;
> +                       libsym->arch.data->stub_addr = resolved_value;
> +               }
> +
> +               if (proc_activate_delayed_symbol(proc, libsym) < 0)
> +                       return;
> +       }
> +}
> +
>  enum plt_status
>  arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
>                         const char *a_name, GElf_Rela *rela, size_t ndx,
>                         struct library_symbol **ret)
>  {
> -       if (mips_elf_is_cpic(lte->ehdr.e_flags))
> +       if (mips_elf_is_cpic(lte->ehdr.e_flags) && (lte->plt_addr != 0))
>                 return PLT_DEFAULT;
>
> -       GElf_Addr got_entry_addr = rela->r_offset;
> -       GElf_Addr stub_addr = rela->r_addend;
> +       GElf_Addr got_entry_addr = rela->r_offset + lte->bias;
> +       GElf_Addr stub_addr = rela->r_addend + lte->bias;
>
>         fprintf(stderr, "PLT-less arch_elf_add_plt_entry %s = %#llx\n",
>                 a_name, stub_addr);
> @@ -406,37 +437,12 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
>                         a_name, stub_addr, strerror(errno));
>                 goto fail;
>         }
> -       libsym->arch.got_entry_addr = got_entry_addr;
> -
> -       GElf_Addr got_entry_value;
> -       if (read_got_entry(proc, got_entry_addr, &got_entry_value) < 0)
> -               goto fail;
> -
> -       fprintf(stderr, " + .got contains %#" PRIx64 "\n", got_entry_value);
> -
> -       if (got_entry_value == stub_addr || got_entry_value == 0) {
> -               fprintf(stderr, "   + unresolved\n");
> -               libsym->arch.type = MIPS_PLT_UNRESOLVED;
> -               libsym->arch.resolved_value = stub_addr;
>
> -       } else {
> -               fprintf(stderr, "   + resolved\n");
> -
> -               /* Mark the symbol for later unresolving.  We may not
> -                * do this right away, as this is called by ltrace
> -                * core for all symbols, and only later filtered.  We
> -                * only unresolve the symbol before the breakpoint is
> -                * enabled.  */
> -
> -               libsym->arch.type = MIPS_PLT_NEED_UNRESOLVE;
> -               libsym->arch.data = malloc(sizeof *libsym->arch.data);
> -               if (libsym->arch.data == NULL)
> -                       goto fail;
> -
> -               libsym->arch.data->self = libsym->arch.data;
> -               libsym->arch.data->got_entry_value = got_entry_value;
> -               libsym->arch.data->stub_addr = stub_addr;
> -       }
> +       fprintf(stderr, "%s unresolved\n", libsym->name);
> +       libsym->arch.got_entry_addr = got_entry_addr;
> +       libsym->arch.resolved_value = stub_addr;
> +       libsym->arch.type = MIPS_PLT_UNRESOLVED;
> +       libsym->delayed = 1;
>
>         *ret = libsym;
>         return PLT_OK;
> @@ -474,6 +480,14 @@ arch_library_symbol_clone(struct library_symbol *retp,
>                            struct library_symbol *libsym)
>  {
>         retp->arch = libsym->arch;
> +       if (libsym->arch.type == MIPS_PLT_NEED_UNRESOLVE) {
> +               assert(libsym->arch.data->self == libsym->arch.data);
> +               retp->arch.data = malloc(sizeof *retp->arch.data);
> +               if (retp->arch.data == NULL)
> +                       return -1;
> +               *retp->arch.data = *libsym->arch.data;
> +               retp->arch.data->self = retp->arch.data;
> +       }
>         return 0;
>  }
>
> @@ -650,7 +664,9 @@ mips_stub_bp_retract(struct breakpoint *bp, struct process *proc)
>         struct library_symbol *libsym = bp->libsym;
>         assert(libsym != NULL);
>
> -       fprintf(stderr, "May need to retract %s.\n", libsym->name);
> +       /* Restore resolved .got entry before detaching */
> +       unresolve_got_entry(proc, libsym->arch.got_entry_addr,
> +                               libsym->arch.resolved_value);
>  }
>
>  static void
> @@ -661,9 +677,15 @@ mips_stub_bp_install(struct breakpoint *bp, struct process *proc)
>         assert(libsym != NULL);
>
>         if (libsym->arch.type == MIPS_PLT_NEED_UNRESOLVE) {
> -               assert(! "MIPS_PLT_NEED_UNRESOLVE unsupported");
> -               abort();
> -               /* Here comes unresolve code.  */
> +               /* Re-route the got-entry to the stub and save resolved address
> +                  for the break-point handler */
> +               GElf_Addr got_entry_value = libsym->arch.data->got_entry_value;
> +
> +               if (unresolve_got_entry(proc, libsym->arch.got_entry_addr,
> +                                          libsym->arch.data->stub_addr) < 0)
> +                       return;
> +               free(libsym->arch.data);
> +               mark_as_resolved(libsym, got_entry_value);
>         }
>  }
>
> diff --git a/sysdeps/linux-gnu/mips/trace.c b/sysdeps/linux-gnu/mips/trace.c
> index 88e13ac..2a4fc7b 100644
> --- a/sysdeps/linux-gnu/mips/trace.c
> +++ b/sysdeps/linux-gnu/mips/trace.c
> @@ -264,16 +264,100 @@ fail:
>         return 0;
>  }
>
> +/**
> + * \param proc         The process to work on.
> + * \param pc           The current program counter
> + * \return newpc Array of next possible PC values
> + * \return nr  Count of next possible PC values
> + *
> + * An atomic Read-Modify-Write, starting with LL and ending with SC needs to
> + * be treated as a single instruction and stepped over, otherwise ERET issued
> + * within the SYSCALL will cause the write to fail, even for a single thread
> + * of execution. LL and SC must exist within a 2048-byte contiguous region.
> + *
> + * We record all outgoing branches from the LL-SC sequence and try to set
> + * breakpoints at their destinations.  Currently, ltrace only allows 2
> + * breakpoints per single step, so if there are more than one outgoing
> + * branches within the LL-SC sequence, single-stepping will eventually fail.
> + *
> + * The first possible PC value is always the instruction following the
> + * store-conditional, or if no SC is found within 2048 bytes, the next
> + * instruction following LL. Rest are destination addresses of outgoing
> + * branches within the LL-SC sequence.
> + */
> +#define inrange(x,lo,hi) ((x)<=(hi) && (x)>=(lo))
> +static int
> +mips_atomic_next_pcs(struct process *proc, uint32_t lladdr, uint32_t *newpcs)
> +{
> +       int nr = 0;
> +
> +       uint32_t scaddr;
> +       for (scaddr = lladdr + 4; scaddr - lladdr <= 2048; scaddr += 4) {
> +               /* Found SC, now stepover trailing branch */
> +               uint32_t inst;
> +               if (proc_read_32(proc, (arch_addr_t)scaddr, &inst) >= 0 &&
> +                   itype_op(inst) == 0x38) {
> +                       newpcs[nr++] = scaddr + 4;
> +                       break;
> +               }
> +       }
> +
> +       /* No SC within 2048 bytes, assume LL is standalone */
> +       if (nr == 0) {
> +               scaddr = 0;
> +               newpcs[nr++] = lladdr + 4;
> +       }
> +
> +       /* Scan LL<->SC range for branches going outside that range */
> +       uint32_t spc;
> +       for (spc = lladdr + 4; spc < scaddr; spc += 4) {
> +               uint32_t scanpcs[2];
> +               int snr = mips_next_pcs(proc, spc, scanpcs);
> +
> +               int i;
> +               for (i = 0; i < snr; ++i) {
> +                       if (!inrange(scanpcs[i], lladdr, scaddr)) {
> +                               uint32_t *tmp = realloc(newpcs, (nr + 1) *
> +                                                       sizeof *newpcs);
> +                               if (tmp == NULL) {
> +                                       perror("malloc atomic next pcs");
> +                                       return -1;
> +                               }
> +
> +                               newpcs = tmp;
> +                               newpcs[nr++] = scanpcs[i];
> +                       }
> +               }
> +       }
> +
> +       assert(nr > 0);
> +       return nr;
> +}
> +
>  enum sw_singlestep_status
>  arch_sw_singlestep(struct process *proc, struct breakpoint *bp,
>                    int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
>                    struct sw_singlestep_data *add_cb_data)
>  {
>         uint32_t pc = (uint32_t) get_instruction_pointer(proc);
> -       uint32_t newpcs[2];
> +       uint32_t *newpcs;
>         int nr;
> +       uint32_t inst;
> +
> +       if (proc_read_32(proc, (arch_addr_t)pc, &inst) < 0)
> +               return SWS_FAIL;
>
> -       nr = mips_next_pcs(proc, pc, newpcs);
> +       if ((newpcs = malloc(2 * sizeof *newpcs)) == NULL)
> +               return SWS_FAIL;
> +
> +       /* Starting an atomic read-modify-write sequence */
> +       if (itype_op(inst) == 0x30)
> +               nr = mips_atomic_next_pcs(proc, pc, newpcs);
> +       else
> +               nr = mips_next_pcs(proc, pc, newpcs);
> +
> +       if (nr <= 0)
> +               return SWS_FAIL;
>
>         while (nr-- > 0) {
>                 arch_addr_t baddr = (arch_addr_t) newpcs[nr];
> @@ -283,11 +367,14 @@ arch_sw_singlestep(struct process *proc, struct breakpoint *bp,
>                         continue;
>                 }
>
> -               if (add_cb(baddr, add_cb_data) < 0)
> +               if (add_cb(baddr, add_cb_data) < 0) {
> +                       free(newpcs);
>                         return SWS_FAIL;
> +               }
>         }
>
>         ptrace(PTRACE_SYSCALL, proc->pid, 0, 0);
> +       free(newpcs);
>         return SWS_OK;
>  }
>
> --
> 1.7.9.5
>
> _______________________________________________
> Ltrace-devel mailing list
> Ltrace-devel at lists.alioth.debian.org
> http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/ltrace-devel



More information about the Ltrace-devel mailing list