/* Copyright 2013-2014 Free Software Foundation, Inc. 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 . */ #include #include #include #include #include typedef struct { unsigned char e_ident[16]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; uint32_t e_entry; uint32_t e_phoff; uint32_t e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; } Elf32_Ehdr; typedef struct { unsigned char e_ident[16]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; uint64_t e_entry; uint64_t e_phoff; uint64_t e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; } Elf64_Ehdr; typedef struct { uint32_t p_type; uint32_t p_offset; uint32_t p_vaddr; uint32_t p_paddr; uint32_t p_filesz; uint32_t p_memsz; uint32_t p_flags; uint32_t p_align; } Elf32_Phdr; typedef struct { uint32_t p_type; uint32_t p_flags; uint64_t p_offset; uint64_t p_vaddr; uint64_t p_paddr; uint64_t p_filesz; uint64_t p_memsz; uint64_t p_align; } Elf64_Phdr; struct elfbuf { const char *path; unsigned char *buf; size_t len; enum { ELFCLASS32 = 1, ELFCLASS64 = 2 } ei_class; }; #define ELFBUF_EHDR_LEN(elf) \ ((elf)->ei_class == ELFCLASS32 ? sizeof (Elf32_Ehdr) : \ sizeof (Elf64_Ehdr)) #define ELFBUF_EHDR(elf, memb) \ ((elf)->ei_class == ELFCLASS32 ? \ ((Elf32_Ehdr *) (elf)->buf)->memb \ : ((Elf64_Ehdr *) (elf)->buf)->memb) #define ELFBUF_PHDR_LEN(elf) \ ((elf)->ei_class == ELFCLASS32 ? sizeof (Elf32_Phdr) : \ sizeof (Elf64_Phdr)) #define ELFBUF_PHDR(elf, idx, memb) \ ((elf)->ei_class == ELFCLASS32 ? \ ((Elf32_Phdr *) &(elf)->buf[((Elf32_Ehdr *)(elf)->buf) \ ->e_phoff])[idx].memb \ : ((Elf64_Phdr *) &(elf)->buf[((Elf64_Ehdr *)(elf)->buf) \ ->e_phoff])[idx].memb) static void exit_with_msg(const char *fmt, ...) { va_list ap; fflush (stdout); va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); if (errno) { fputs (": ", stderr); perror (NULL); } else fputc ('\n', stderr); exit (1); } static void read_file (unsigned char **buf_ptr, size_t *len_ptr, FILE *fp) { size_t len = 0; size_t size = 1024; size_t chunk; unsigned char *buf = malloc (size); while ((chunk = fread (buf + len, 1, size - len, fp)) == size - len) { len = size; size *= 2; buf = realloc (buf, size); } len += chunk; *buf_ptr = buf; *len_ptr = len; } static void write_file (unsigned char *buf, size_t len, FILE *fp) { fwrite (buf, 1, len, fp); } static void elfbuf_init_from_file (struct elfbuf *elf, const char *path) { FILE *fp = fopen (path, "rb"); unsigned char *buf; size_t len; if (fp == NULL) exit_with_msg ("%s", path); read_file (&buf, &len, fp); fclose (fp); /* Validate ELF identification. */ if (len < 16 || buf[0] != 0x7f || buf[1] != 0x45 || buf[2] != 0x4c || buf[3] != 0x46 || buf[4] < 1 || buf[4] > 2 || buf[5] < 1 || buf[5] > 2) exit_with_msg ("%s: unsupported or invalid ELF file", path); elf->path = path; elf->buf = buf; elf->len = len; elf->ei_class = buf[4]; if (ELFBUF_EHDR_LEN (elf) > len || ELFBUF_EHDR (elf, e_phoff) > len || ELFBUF_EHDR (elf, e_phnum) > ((len - ELFBUF_EHDR (elf, e_phoff)) / ELFBUF_PHDR_LEN (elf)) ) exit_with_msg ("%s: unexpected end of data", path); if (ELFBUF_EHDR (elf, e_phentsize) != ELFBUF_PHDR_LEN (elf)) exit_with_msg ("%s: inconsistent ELF header", path); } static void elfbuf_write_to_file (struct elfbuf *elf, const char *path) { FILE *fp = fopen (path, "wb"); if (fp == NULL) exit_with_msg ("%s", path); write_file (elf->buf, elf->len, fp); fclose (fp); } /* In the auxv note starting at OFFSET with size LEN, mask the hwcap field using the HWCAP_MASK. */ static void elfbuf_handle_auxv (struct elfbuf *elf, size_t offset, size_t len, unsigned long hwcap_mask) { size_t i; uint32_t *auxv32 = (uint32_t *) (elf->buf + offset); uint64_t *auxv64 = (uint64_t *) auxv32; size_t entry_size = elf->ei_class == ELFCLASS32 ? sizeof (auxv32[0]) : sizeof (auxv64[0]); for (i = 0; i < len / entry_size; i++) { uint64_t auxv_type = elf->ei_class == ELFCLASS32 ? auxv32[2 * i] : auxv64[2 * i]; if (auxv_type == 0) break; if (auxv_type != 16) continue; if (elf->ei_class == ELFCLASS32) auxv32[2 * i + 1] &= (uint32_t) hwcap_mask; else auxv64[2 * i + 1] &= (uint64_t) hwcap_mask; } } /* In the note segment starting at OFFSET with size LEN, make notes with type NOTE_TYPE unrecognizable by GDB. Also, mask the hwcap field of any auxv notes using the HWCAP_MASK. */ static void elfbuf_handle_note_segment (struct elfbuf *elf, size_t offset, size_t len, unsigned note_type, unsigned long hwcap_mask) { size_t pos = 0; while (pos + 12 < len) { uint32_t *note = (uint32_t *) (elf->buf + offset + pos); size_t desc_pos = pos + 12 + ((note[0] + 3) & ~3); size_t next_pos = desc_pos + ((note[1] + 3) & ~3); if (desc_pos > len || next_pos > len) exit_with_msg ("%s: corrupt notes data", elf->path); if (note[2] == note_type) note[2] |= 0xff000000; else if (note[2] == 6 && hwcap_mask != 0) elfbuf_handle_auxv (elf, offset + desc_pos, note[1], hwcap_mask); pos = next_pos; } } static void elfbuf_handle_core_notes (struct elfbuf *elf, unsigned note_type, unsigned long hwcap_mask) { unsigned ph_idx; if (ELFBUF_EHDR (elf, e_type) != 4) exit_with_msg ("%s: not a core file", elf->path); /* Iterate over program headers. */ for (ph_idx = 0; ph_idx != ELFBUF_EHDR (elf, e_phnum); ph_idx++) { size_t offset = ELFBUF_PHDR (elf, ph_idx, p_offset); size_t filesz = ELFBUF_PHDR (elf, ph_idx, p_filesz); if (offset > elf->len || filesz > elf->len - offset) exit_with_msg ("%s: unexpected end of data", elf->path); /* Deal with NOTE segments only. */ if (ELFBUF_PHDR (elf, ph_idx, p_type) != 4) continue; elfbuf_handle_note_segment (elf, offset, filesz, note_type, hwcap_mask); } } int main (int argc, char *argv[]) { unsigned note_type; unsigned long hwcap_mask = 0; struct elfbuf elf; if (argc < 4) { abort (); } if (sscanf (argv[3], "%u", ¬e_type) != 1) exit_with_msg ("%s: bad command line arguments\n", argv[0]); if (argc >= 5) { if (sscanf (argv[4], "%lu", &hwcap_mask) != 1) exit_with_msg ("%s: bad command line arguments\n", argv[0]); } elfbuf_init_from_file (&elf, argv[1]); elfbuf_handle_core_notes (&elf, note_type, hwcap_mask); elfbuf_write_to_file (&elf, argv[2]); return 0; }