Merge branch 'rc-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuil...
[linux-2.6.git] / scripts / recordmcount.c
index 34f32be..f2f32ee 100644 (file)
@@ -212,549 +212,42 @@ is_mcounted_section_name(char const *const txtname)
                0 == strcmp(".text.unlikely", txtname);
 }
 
-/* Append the new shstrtab, Elf32_Shdr[], __mcount_loc and its relocations. */
-static void append32(Elf32_Ehdr *const ehdr,
-                    Elf32_Shdr *const shstr,
-                    uint32_t const *const mloc0,
-                    uint32_t const *const mlocp,
-                    Elf32_Rel const *const mrel0,
-                    Elf32_Rel const *const mrelp,
-                    unsigned int const rel_entsize,
-                    unsigned int const symsec_sh_link)
-{
-       /* Begin constructing output file */
-       Elf32_Shdr mcsec;
-       char const *mc_name = (sizeof(Elf32_Rela) == rel_entsize)
-               ? ".rela__mcount_loc"
-               :  ".rel__mcount_loc";
-       unsigned const old_shnum = w2(ehdr->e_shnum);
-       uint32_t const old_shoff = w(ehdr->e_shoff);
-       uint32_t const old_shstr_sh_size   = w(shstr->sh_size);
-       uint32_t const old_shstr_sh_offset = w(shstr->sh_offset);
-       uint32_t t = 1 + strlen(mc_name) + w(shstr->sh_size);
-       uint32_t new_e_shoff;
-
-       shstr->sh_size = w(t);
-       shstr->sh_offset = w(sb.st_size);
-       t += sb.st_size;
-       t += (3u & -t);  /* 4-byte align */
-       new_e_shoff = t;
-
-       /* body for new shstrtab */
-       ulseek(fd_map, sb.st_size, SEEK_SET);
-       uwrite(fd_map, old_shstr_sh_offset + (void *)ehdr, old_shstr_sh_size);
-       uwrite(fd_map, mc_name, 1 + strlen(mc_name));
-
-       /* old(modified) Elf32_Shdr table, 4-byte aligned */
-       ulseek(fd_map, t, SEEK_SET);
-       t += sizeof(Elf32_Shdr) * old_shnum;
-       uwrite(fd_map, old_shoff + (void *)ehdr,
-              sizeof(Elf32_Shdr) * old_shnum);
-
-       /* new sections __mcount_loc and .rel__mcount_loc */
-       t += 2*sizeof(mcsec);
-       mcsec.sh_name = w((sizeof(Elf32_Rela) == rel_entsize) + strlen(".rel")
-               + old_shstr_sh_size);
-       mcsec.sh_type = w(SHT_PROGBITS);
-       mcsec.sh_flags = w(SHF_ALLOC);
-       mcsec.sh_addr = 0;
-       mcsec.sh_offset = w(t);
-       mcsec.sh_size = w((void *)mlocp - (void *)mloc0);
-       mcsec.sh_link = 0;
-       mcsec.sh_info = 0;
-       mcsec.sh_addralign = w(4);
-       mcsec.sh_entsize = w(4);
-       uwrite(fd_map, &mcsec, sizeof(mcsec));
-
-       mcsec.sh_name = w(old_shstr_sh_size);
-       mcsec.sh_type = (sizeof(Elf32_Rela) == rel_entsize)
-               ? w(SHT_RELA)
-               : w(SHT_REL);
-       mcsec.sh_flags = 0;
-       mcsec.sh_addr = 0;
-       mcsec.sh_offset = w((void *)mlocp - (void *)mloc0 + t);
-       mcsec.sh_size   = w((void *)mrelp - (void *)mrel0);
-       mcsec.sh_link = w(symsec_sh_link);
-       mcsec.sh_info = w(old_shnum);
-       mcsec.sh_addralign = w(4);
-       mcsec.sh_entsize = w(rel_entsize);
-       uwrite(fd_map, &mcsec, sizeof(mcsec));
-
-       uwrite(fd_map, mloc0, (void *)mlocp - (void *)mloc0);
-       uwrite(fd_map, mrel0, (void *)mrelp - (void *)mrel0);
-
-       ehdr->e_shoff = w(new_e_shoff);
-       ehdr->e_shnum = w2(2 + w2(ehdr->e_shnum));  /* {.rel,}__mcount_loc */
-       ulseek(fd_map, 0, SEEK_SET);
-       uwrite(fd_map, ehdr, sizeof(*ehdr));
-}
-
-/*
- * append64 and append32 (and other analogous pairs) could be templated
- * using C++, but the complexity is high.  (For an example, look at p_elf.h
- * in the source for UPX, http://upx.sourceforge.net)  So: remember to make
- * the corresponding change in the routine for the other size.
- */
-static void append64(Elf64_Ehdr *const ehdr,
-                    Elf64_Shdr *const shstr,
-                    uint64_t const *const mloc0,
-                    uint64_t const *const mlocp,
-                    Elf64_Rel const *const mrel0,
-                    Elf64_Rel const *const mrelp,
-                    unsigned int const rel_entsize,
-                    unsigned int const symsec_sh_link)
-{
-       /* Begin constructing output file */
-       Elf64_Shdr mcsec;
-       char const *mc_name = (sizeof(Elf64_Rela) == rel_entsize)
-               ? ".rela__mcount_loc"
-               :  ".rel__mcount_loc";
-       unsigned const old_shnum = w2(ehdr->e_shnum);
-       uint64_t const old_shoff = w8(ehdr->e_shoff);
-       uint64_t const old_shstr_sh_size   = w8(shstr->sh_size);
-       uint64_t const old_shstr_sh_offset = w8(shstr->sh_offset);
-       uint64_t t = 1 + strlen(mc_name) + w8(shstr->sh_size);
-       uint64_t new_e_shoff;
-
-       shstr->sh_size = w8(t);
-       shstr->sh_offset = w8(sb.st_size);
-       t += sb.st_size;
-       t += (7u & -t);  /* 8-byte align */
-       new_e_shoff = t;
-
-       /* body for new shstrtab */
-       ulseek(fd_map, sb.st_size, SEEK_SET);
-       uwrite(fd_map, old_shstr_sh_offset + (void *)ehdr, old_shstr_sh_size);
-       uwrite(fd_map, mc_name, 1 + strlen(mc_name));
-
-       /* old(modified) Elf64_Shdr table, 8-byte aligned */
-       ulseek(fd_map, t, SEEK_SET);
-       t += sizeof(Elf64_Shdr) * old_shnum;
-       uwrite(fd_map, old_shoff + (void *)ehdr,
-               sizeof(Elf64_Shdr) * old_shnum);
-
-       /* new sections __mcount_loc and .rel__mcount_loc */
-       t += 2*sizeof(mcsec);
-       mcsec.sh_name = w((sizeof(Elf64_Rela) == rel_entsize) + strlen(".rel")
-               + old_shstr_sh_size);
-       mcsec.sh_type = w(SHT_PROGBITS);
-       mcsec.sh_flags = w8(SHF_ALLOC);
-       mcsec.sh_addr = 0;
-       mcsec.sh_offset = w8(t);
-       mcsec.sh_size = w8((void *)mlocp - (void *)mloc0);
-       mcsec.sh_link = 0;
-       mcsec.sh_info = 0;
-       mcsec.sh_addralign = w8(8);
-       mcsec.sh_entsize = w8(8);
-       uwrite(fd_map, &mcsec, sizeof(mcsec));
-
-       mcsec.sh_name = w(old_shstr_sh_size);
-       mcsec.sh_type = (sizeof(Elf64_Rela) == rel_entsize)
-               ? w(SHT_RELA)
-               : w(SHT_REL);
-       mcsec.sh_flags = 0;
-       mcsec.sh_addr = 0;
-       mcsec.sh_offset = w8((void *)mlocp - (void *)mloc0 + t);
-       mcsec.sh_size   = w8((void *)mrelp - (void *)mrel0);
-       mcsec.sh_link = w(symsec_sh_link);
-       mcsec.sh_info = w(old_shnum);
-       mcsec.sh_addralign = w8(8);
-       mcsec.sh_entsize = w8(rel_entsize);
-       uwrite(fd_map, &mcsec, sizeof(mcsec));
-
-       uwrite(fd_map, mloc0, (void *)mlocp - (void *)mloc0);
-       uwrite(fd_map, mrel0, (void *)mrelp - (void *)mrel0);
-
-       ehdr->e_shoff = w8(new_e_shoff);
-       ehdr->e_shnum = w2(2 + w2(ehdr->e_shnum));  /* {.rel,}__mcount_loc */
-       ulseek(fd_map, 0, SEEK_SET);
-       uwrite(fd_map, ehdr, sizeof(*ehdr));
-}
-
-/*
- * Look at the relocations in order to find the calls to mcount.
- * Accumulate the section offsets that are found, and their relocation info,
- * onto the end of the existing arrays.
- */
-static uint32_t *sift32_rel_mcount(uint32_t *mlocp,
-                                  unsigned const offbase,
-                                  Elf32_Rel **const mrelpp,
-                                  Elf32_Shdr const *const relhdr,
-                                  Elf32_Ehdr const *const ehdr,
-                                  unsigned const recsym,
-                                  uint32_t const recval,
-                                  unsigned const reltype)
-{
-       uint32_t *const mloc0 = mlocp;
-       Elf32_Rel *mrelp = *mrelpp;
-       Elf32_Shdr *const shdr0 = (Elf32_Shdr *)(w(ehdr->e_shoff)
-               + (void *)ehdr);
-       unsigned const symsec_sh_link = w(relhdr->sh_link);
-       Elf32_Shdr const *const symsec = &shdr0[symsec_sh_link];
-       Elf32_Sym const *const sym0 = (Elf32_Sym const *)(w(symsec->sh_offset)
-               + (void *)ehdr);
-
-       Elf32_Shdr const *const strsec = &shdr0[w(symsec->sh_link)];
-       char const *const str0 = (char const *)(w(strsec->sh_offset)
-               + (void *)ehdr);
-
-       Elf32_Rel const *const rel0 = (Elf32_Rel const *)(w(relhdr->sh_offset)
-               + (void *)ehdr);
-       unsigned rel_entsize = w(relhdr->sh_entsize);
-       unsigned const nrel = w(relhdr->sh_size) / rel_entsize;
-       Elf32_Rel const *relp = rel0;
-
-       unsigned mcountsym = 0;
-       unsigned t;
-
-       for (t = nrel; t; --t) {
-               if (!mcountsym) {
-                       Elf32_Sym const *const symp =
-                               &sym0[ELF32_R_SYM(w(relp->r_info))];
-
-                       if (0 == strcmp((('_' == gpfx) ? "_mcount" : "mcount"),
-                                       &str0[w(symp->st_name)]))
-                               mcountsym = ELF32_R_SYM(w(relp->r_info));
-               }
-               if (mcountsym == ELF32_R_SYM(w(relp->r_info))) {
-                       uint32_t const addend = w(w(relp->r_offset) - recval);
-                       mrelp->r_offset = w(offbase
-                               + ((void *)mlocp - (void *)mloc0));
-                       mrelp->r_info = w(ELF32_R_INFO(recsym, reltype));
-                       if (sizeof(Elf32_Rela) == rel_entsize) {
-                               ((Elf32_Rela *)mrelp)->r_addend = addend;
-                               *mlocp++ = 0;
-                       } else
-                               *mlocp++ = addend;
-
-                       mrelp = (Elf32_Rel *)(rel_entsize + (void *)mrelp);
-               }
-               relp = (Elf32_Rel const *)(rel_entsize + (void *)relp);
-       }
-       *mrelpp = mrelp;
-       return mlocp;
-}
-
-static uint64_t *sift64_rel_mcount(uint64_t *mlocp,
-                                  unsigned const offbase,
-                                  Elf64_Rel **const mrelpp,
-                                  Elf64_Shdr const *const relhdr,
-                                  Elf64_Ehdr const *const ehdr,
-                                  unsigned const recsym,
-                                  uint64_t const recval,
-                                  unsigned const reltype)
-{
-       uint64_t *const mloc0 = mlocp;
-       Elf64_Rel *mrelp = *mrelpp;
-       Elf64_Shdr *const shdr0 = (Elf64_Shdr *)(w8(ehdr->e_shoff)
-               + (void *)ehdr);
-       unsigned const symsec_sh_link = w(relhdr->sh_link);
-       Elf64_Shdr const *const symsec = &shdr0[symsec_sh_link];
-       Elf64_Sym const *const sym0 = (Elf64_Sym const *)(w8(symsec->sh_offset)
-               + (void *)ehdr);
-
-       Elf64_Shdr const *const strsec = &shdr0[w(symsec->sh_link)];
-       char const *const str0 = (char const *)(w8(strsec->sh_offset)
-               + (void *)ehdr);
-
-       Elf64_Rel const *const rel0 = (Elf64_Rel const *)(w8(relhdr->sh_offset)
-               + (void *)ehdr);
-       unsigned rel_entsize = w8(relhdr->sh_entsize);
-       unsigned const nrel = w8(relhdr->sh_size) / rel_entsize;
-       Elf64_Rel const *relp = rel0;
-
-       unsigned mcountsym = 0;
-       unsigned t;
-
-       for (t = nrel; 0 != t; --t) {
-               if (!mcountsym) {
-                       Elf64_Sym const *const symp =
-                               &sym0[ELF64_R_SYM(w8(relp->r_info))];
-                       char const *symname = &str0[w(symp->st_name)];
-
-                       if ('.' == symname[0])
-                               ++symname;  /* ppc64 hack */
-                       if (0 == strcmp((('_' == gpfx) ? "_mcount" : "mcount"),
-                                       symname))
-                               mcountsym = ELF64_R_SYM(w8(relp->r_info));
-               }
-
-               if (mcountsym == ELF64_R_SYM(w8(relp->r_info))) {
-                       uint64_t const addend = w8(w8(relp->r_offset) - recval);
-
-                       mrelp->r_offset = w8(offbase
-                               + ((void *)mlocp - (void *)mloc0));
-                       mrelp->r_info = w8(ELF64_R_INFO(recsym, reltype));
-                       if (sizeof(Elf64_Rela) == rel_entsize) {
-                               ((Elf64_Rela *)mrelp)->r_addend = addend;
-                               *mlocp++ = 0;
-                       } else
-                               *mlocp++ = addend;
-
-                       mrelp = (Elf64_Rel *)(rel_entsize + (void *)mrelp);
-               }
-               relp = (Elf64_Rel const *)(rel_entsize + (void *)relp);
-       }
-       *mrelpp = mrelp;
-
-       return mlocp;
-}
-
-/*
- * Find a symbol in the given section, to be used as the base for relocating
- * the table of offsets of calls to mcount.  A local or global symbol suffices,
- * but avoid a Weak symbol because it may be overridden; the change in value
- * would invalidate the relocations of the offsets of the calls to mcount.
- * Often the found symbol will be the unnamed local symbol generated by
- * GNU 'as' for the start of each section.  For example:
- *    Num:    Value  Size Type    Bind   Vis      Ndx Name
- *      2: 00000000     0 SECTION LOCAL  DEFAULT    1
+/* 32 bit and 64 bit are very similar */
+#include "recordmcount.h"
+#define RECORD_MCOUNT_64
+#include "recordmcount.h"
+
+/* 64-bit EM_MIPS has weird ELF64_Rela.r_info.
+ * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
+ * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40]
+ * to imply the order of the members; the spec does not say so.
+ *     typedef unsigned char Elf64_Byte;
+ * fails on MIPS64 because their <elf.h> already has it!
  */
-static unsigned find32_secsym_ndx(unsigned const txtndx,
-                                 char const *const txtname,
-                                 uint32_t *const recvalp,
-                                 Elf32_Shdr const *const symhdr,
-                                 Elf32_Ehdr const *const ehdr)
-{
-       Elf32_Sym const *const sym0 = (Elf32_Sym const *)(w(symhdr->sh_offset)
-               + (void *)ehdr);
-       unsigned const nsym = w(symhdr->sh_size) / w(symhdr->sh_entsize);
-       Elf32_Sym const *symp;
-       unsigned t;
-
-       for (symp = sym0, t = nsym; t; --t, ++symp) {
-               unsigned int const st_bind = ELF32_ST_BIND(symp->st_info);
-
-               if (txtndx == w2(symp->st_shndx)
-                       /* avoid STB_WEAK */
-                   && (STB_LOCAL == st_bind || STB_GLOBAL == st_bind)) {
-                       *recvalp = w(symp->st_value);
-                       return symp - sym0;
-               }
-       }
-       fprintf(stderr, "Cannot find symbol for section %d: %s.\n",
-               txtndx, txtname);
-       fail_file();
-}
 
-static unsigned find64_secsym_ndx(unsigned const txtndx,
-                                 char const *const txtname,
-                                 uint64_t *const recvalp,
-                                 Elf64_Shdr const *const symhdr,
-                                 Elf64_Ehdr const *const ehdr)
-{
-       Elf64_Sym const *const sym0 = (Elf64_Sym const *)(w8(symhdr->sh_offset)
-               + (void *)ehdr);
-       unsigned const nsym = w8(symhdr->sh_size) / w8(symhdr->sh_entsize);
-       Elf64_Sym const *symp;
-       unsigned t;
-
-       for (symp = sym0, t = nsym; t; --t, ++symp) {
-               unsigned int const st_bind = ELF64_ST_BIND(symp->st_info);
-
-               if (txtndx == w2(symp->st_shndx)
-                       /* avoid STB_WEAK */
-                   && (STB_LOCAL == st_bind || STB_GLOBAL == st_bind)) {
-                       *recvalp = w8(symp->st_value);
-                       return symp - sym0;
-               }
-       }
-       fprintf(stderr, "Cannot find symbol for section %d: %s.\n",
-               txtndx, txtname);
-       fail_file();
-}
-
-/*
- * Evade ISO C restriction: no declaration after statement in
- * has32_rel_mcount.
- */
-static char const *
-__has32_rel_mcount(Elf32_Shdr const *const relhdr,  /* is SHT_REL or SHT_RELA */
-                  Elf32_Shdr const *const shdr0,
-                  char const *const shstrtab,
-                  char const *const fname)
-{
-       /* .sh_info depends on .sh_type == SHT_REL[,A] */
-       Elf32_Shdr const *const txthdr = &shdr0[w(relhdr->sh_info)];
-       char const *const txtname = &shstrtab[w(txthdr->sh_name)];
-
-       if (0 == strcmp("__mcount_loc", txtname)) {
-               fprintf(stderr, "warning: __mcount_loc already exists: %s\n",
-                       fname);
-               succeed_file();
-       }
-       if (SHT_PROGBITS != w(txthdr->sh_type) ||
-           !is_mcounted_section_name(txtname))
-               return NULL;
-       return txtname;
-}
-
-static char const *has32_rel_mcount(Elf32_Shdr const *const relhdr,
-                                   Elf32_Shdr const *const shdr0,
-                                   char const *const shstrtab,
-                                   char const *const fname)
-{
-       if (SHT_REL  != w(relhdr->sh_type) && SHT_RELA != w(relhdr->sh_type))
-               return NULL;
-       return __has32_rel_mcount(relhdr, shdr0, shstrtab, fname);
-}
-
-static char const *__has64_rel_mcount(Elf64_Shdr const *const relhdr,
-                                     Elf64_Shdr const *const shdr0,
-                                     char const *const shstrtab,
-                                     char const *const fname)
-{
-       /* .sh_info depends on .sh_type == SHT_REL[,A] */
-       Elf64_Shdr const *const txthdr = &shdr0[w(relhdr->sh_info)];
-       char const *const txtname = &shstrtab[w(txthdr->sh_name)];
-
-       if (0 == strcmp("__mcount_loc", txtname)) {
-               fprintf(stderr, "warning: __mcount_loc already exists: %s\n",
-                       fname);
-               succeed_file();
-       }
-       if (SHT_PROGBITS != w(txthdr->sh_type) ||
-           !is_mcounted_section_name(txtname))
-               return NULL;
-       return txtname;
-}
-
-static char const *has64_rel_mcount(Elf64_Shdr const *const relhdr,
-                                   Elf64_Shdr const *const shdr0,
-                                   char const *const shstrtab,
-                                   char const *const fname)
-{
-       if (SHT_REL  != w(relhdr->sh_type) && SHT_RELA != w(relhdr->sh_type))
-               return NULL;
-       return __has64_rel_mcount(relhdr, shdr0, shstrtab, fname);
-}
-
-static unsigned tot32_relsize(Elf32_Shdr const *const shdr0,
-                             unsigned nhdr,
-                             const char *const shstrtab,
-                             const char *const fname)
-{
-       unsigned totrelsz = 0;
-       Elf32_Shdr const *shdrp = shdr0;
-       for (; 0 != nhdr; --nhdr, ++shdrp) {
-               if (has32_rel_mcount(shdrp, shdr0, shstrtab, fname))
-                       totrelsz += w(shdrp->sh_size);
-       }
-       return totrelsz;
-}
-
-static unsigned tot64_relsize(Elf64_Shdr const *const shdr0,
-                             unsigned nhdr,
-                             const char *const shstrtab,
-                             const char *const fname)
-{
-       unsigned totrelsz = 0;
-       Elf64_Shdr const *shdrp = shdr0;
-
-       for (; nhdr; --nhdr, ++shdrp) {
-               if (has64_rel_mcount(shdrp, shdr0, shstrtab, fname))
-                       totrelsz += w8(shdrp->sh_size);
-       }
-       return totrelsz;
-}
+typedef uint8_t myElf64_Byte;          /* Type for a 8-bit quantity.  */
+
+union mips_r_info {
+       Elf64_Xword r_info;
+       struct {
+               Elf64_Word r_sym;               /* Symbol index.  */
+               myElf64_Byte r_ssym;            /* Special symbol.  */
+               myElf64_Byte r_type3;           /* Third relocation.  */
+               myElf64_Byte r_type2;           /* Second relocation.  */
+               myElf64_Byte r_type;            /* First relocation.  */
+       } r_mips;
+};
 
-/* Overall supervision for Elf32 ET_REL file. */
-static void
-do32(Elf32_Ehdr *const ehdr, char const *const fname, unsigned const reltype)
+static uint64_t MIPS64_r_sym(Elf64_Rel const *rp)
 {
-       Elf32_Shdr *const shdr0 = (Elf32_Shdr *)(w(ehdr->e_shoff)
-               + (void *)ehdr);
-       unsigned const nhdr = w2(ehdr->e_shnum);
-       Elf32_Shdr *const shstr = &shdr0[w2(ehdr->e_shstrndx)];
-       char const *const shstrtab = (char const *)(w(shstr->sh_offset)
-               + (void *)ehdr);
-
-       Elf32_Shdr const *relhdr;
-       unsigned k;
-
-       /* Upper bound on space: assume all relevant relocs are for mcount. */
-       unsigned const totrelsz = tot32_relsize(shdr0, nhdr, shstrtab, fname);
-       Elf32_Rel *const mrel0 = umalloc(totrelsz);
-       Elf32_Rel *      mrelp = mrel0;
-
-       /* 2*sizeof(address) <= sizeof(Elf32_Rel) */
-       uint32_t *const mloc0 = umalloc(totrelsz>>1);
-       uint32_t *      mlocp = mloc0;
-
-       unsigned rel_entsize = 0;
-       unsigned symsec_sh_link = 0;
-
-       for (relhdr = shdr0, k = nhdr; k; --k, ++relhdr) {
-               char const *const txtname = has32_rel_mcount(relhdr, shdr0,
-                       shstrtab, fname);
-               if (txtname) {
-                       uint32_t recval = 0;
-                       unsigned const recsym = find32_secsym_ndx(
-                               w(relhdr->sh_info), txtname, &recval,
-                               &shdr0[symsec_sh_link = w(relhdr->sh_link)],
-                               ehdr);
-
-                       rel_entsize = w(relhdr->sh_entsize);
-                       mlocp = sift32_rel_mcount(mlocp,
-                               (void *)mlocp - (void *)mloc0, &mrelp,
-                               relhdr, ehdr, recsym, recval, reltype);
-               }
-       }
-       if (mloc0 != mlocp) {
-               append32(ehdr, shstr, mloc0, mlocp, mrel0, mrelp,
-                       rel_entsize, symsec_sh_link);
-       }
-       free(mrel0);
-       free(mloc0);
+       return w(((union mips_r_info){ .r_info = rp->r_info }).r_mips.r_sym);
 }
 
-static void
-do64(Elf64_Ehdr *const ehdr, char const *const fname, unsigned const reltype)
+static void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)
 {
-       Elf64_Shdr *const shdr0 = (Elf64_Shdr *)(w8(ehdr->e_shoff)
-               + (void *)ehdr);
-       unsigned const nhdr = w2(ehdr->e_shnum);
-       Elf64_Shdr *const shstr = &shdr0[w2(ehdr->e_shstrndx)];
-       char const *const shstrtab = (char const *)(w8(shstr->sh_offset)
-               + (void *)ehdr);
-
-       Elf64_Shdr const *relhdr;
-       unsigned k;
-
-       /* Upper bound on space: assume all relevant relocs are for mcount. */
-       unsigned const totrelsz = tot64_relsize(shdr0, nhdr, shstrtab, fname);
-       Elf64_Rel *const mrel0 = umalloc(totrelsz);
-       Elf64_Rel *      mrelp = mrel0;
-
-       /* 2*sizeof(address) <= sizeof(Elf64_Rel) */
-       uint64_t *const mloc0 = umalloc(totrelsz>>1);
-       uint64_t *      mlocp = mloc0;
-
-       unsigned rel_entsize = 0;
-       unsigned symsec_sh_link = 0;
-
-       for ((relhdr = shdr0), k = nhdr; k; --k, ++relhdr) {
-               char const *const txtname = has64_rel_mcount(relhdr, shdr0,
-                       shstrtab, fname);
-               if (txtname) {
-                       uint64_t recval = 0;
-                       unsigned const recsym = find64_secsym_ndx(
-                               w(relhdr->sh_info), txtname, &recval,
-                               &shdr0[symsec_sh_link = w(relhdr->sh_link)],
-                               ehdr);
-
-                       rel_entsize = w8(relhdr->sh_entsize);
-                       mlocp = sift64_rel_mcount(mlocp,
-                               (void *)mlocp - (void *)mloc0, &mrelp,
-                               relhdr, ehdr, recsym, recval, reltype);
-               }
-       }
-       if (mloc0 != mlocp) {
-               append64(ehdr, shstr, mloc0, mlocp, mrel0, mrelp,
-                       rel_entsize, symsec_sh_link);
-       }
-       free(mrel0);
-       free(mloc0);
+       rp->r_info = ((union mips_r_info){
+               .r_mips = { .r_sym = w(sym), .r_type = type }
+       }).r_info;
 }
 
 static void
@@ -808,6 +301,7 @@ do_file(char const *const fname)
        case EM_386:     reltype = R_386_32;                   break;
        case EM_ARM:     reltype = R_ARM_ABS32;                break;
        case EM_IA_64:   reltype = R_IA64_IMM64;   gpfx = '_'; break;
+       case EM_MIPS:    /* reltype: e_class    */ gpfx = '_'; break;
        case EM_PPC:     reltype = R_PPC_ADDR32;   gpfx = '_'; break;
        case EM_PPC64:   reltype = R_PPC64_ADDR64; gpfx = '_'; break;
        case EM_S390:    /* reltype: e_class    */ gpfx = '_'; break;
@@ -831,6 +325,10 @@ do_file(char const *const fname)
                }
                if (EM_S390 == w2(ehdr->e_machine))
                        reltype = R_390_32;
+               if (EM_MIPS == w2(ehdr->e_machine)) {
+                       reltype = R_MIPS_32;
+                       is_fake_mcount32 = MIPS32_is_fake_mcount;
+               }
                do32(ehdr, fname, reltype);
        } break;
        case ELFCLASS64: {
@@ -843,6 +341,12 @@ do_file(char const *const fname)
                }
                if (EM_S390 == w2(ghdr->e_machine))
                        reltype = R_390_64;
+               if (EM_MIPS == w2(ghdr->e_machine)) {
+                       reltype = R_MIPS_64;
+                       Elf64_r_sym = MIPS64_r_sym;
+                       Elf64_r_info = MIPS64_r_info;
+                       is_fake_mcount64 = MIPS64_is_fake_mcount;
+               }
                do64(ghdr, fname, reltype);
        } break;
        }  /* end switch */
@@ -853,12 +357,30 @@ do_file(char const *const fname)
 int
 main(int argc, char const *argv[])
 {
+       const char ftrace[] = "kernel/trace/ftrace.o";
+       int ftrace_size = sizeof(ftrace) - 1;
        int n_error = 0;  /* gcc-4.3.0 false positive complaint */
-       if (argc <= 1)
+
+       if (argc <= 1) {
                fprintf(stderr, "usage: recordmcount file.o...\n");
-       else  /* Process each file in turn, allowing deep failure. */
+               return 0;
+       }
+
+       /* Process each file in turn, allowing deep failure. */
        for (--argc, ++argv; 0 < argc; --argc, ++argv) {
                int const sjval = setjmp(jmpenv);
+               int len;
+
+               /*
+                * The file kernel/trace/ftrace.o references the mcount
+                * function but does not call it. Since ftrace.o should
+                * not be traced anyway, we just skip it.
+                */
+               len = strlen(argv[0]);
+               if (len >= ftrace_size &&
+                   strcmp(argv[0] + (len - ftrace_size), ftrace) == 0)
+                       continue;
+
                switch (sjval) {
                default: {
                        fprintf(stderr, "internal error: %s\n", argv[0]);