#!/usr/bin/env l1 // attempt to mimic readelf C program @include @define align(l, sz) { @local res; res = l; if (l % sz) res = l + (sz - (l % sz)); return res; } @define abi2str(elfdom, abinum) { @local s; s = "UNKNOWN/unhandled"; switch(abinum) { case elfdom`ELFOSABI_SYSV: s = "UNIX - System V"; break; case elfdom`ELFOSABI_HPUX: s = "UNIX - HPUX"; break; case elfdom`ELFOSABI_NETBSD: s = "UNIX - NetBSD"; break; case elfdom`ELFOSABI_LINUX: s = "UNIX - Linux"; break; case elfdom`ELFOSABI_SOLARIS: s = "UNIX - Solaris"; break; case elfdom`ELFOSABI_AIX: s = "UNIX - AIX"; break; case elfdom`ELFOSABI_IRIX: s = "UNIX - IRIX"; break; case elfdom`ELFOSABI_FREEBSD: s = "UNIX - FreeBSD"; break; case elfdom`ELFOSABI_TRU64: s = "UNIX - Tru64"; break; case elfdom`ELFOSABI_MODESTO: s = "Modesto"; break; case elfdom`ELFOSABI_OPENBSD: s = "UNIX - OpenBSD"; break; case elfdom`ELFOSABI_ARM: s = "ARM"; break; case elfdom`ELFOSABI_STANDALONE: s = "Standalone"; break; } return s; } @define type2str(elfdom, etype) { @local t; t = ["None", "REL (Relocatable file)", "EXEC (Executable file)", "DYN (Shared object file)", "CORE (Core file)"]; if (etype > length(t) - 1) return "UNKNOWN"; return listref(t, etype); } @define sectype2str(elfdom, stype) { @local s; s = "UNKNOWN"; switch(stype) { case elfdom`SHT_NULL: s = "NULL"; break; case elfdom`SHT_PROGBITS: s = "PROGBITS"; break; case elfdom`SHT_SYMTAB: s = "SYMTAB"; break; case elfdom`SHT_STRTAB: s = "STRTAB"; break; case elfdom`SHT_RELA: s = "RELA"; break; case elfdom`SHT_HASH: s = "HASH"; break; case elfdom`SHT_DYNAMIC: s = "DYNAMIC"; break; case elfdom`SHT_NOTE: s = "NOTE"; break; case elfdom`SHT_NOBITS: s = "NOBITS"; break; case elfdom`SHT_REL: s = "REL"; break; case elfdom`SHT_SHLIB: s = "SHLIB"; break; case elfdom`SHT_DYNSYM: s = "DYNSYM"; break; case elfdom`SHT_INIT_ARRAY: s = "INIT_ARRAY"; break; case elfdom`SHT_FINI_ARRAY: s = "FINI_ARRAY"; break; case elfdom`SHT_PREINIT_ARRAY: s = "PREINIT_ARRAY"; break; case elfdom`SHT_GROUP: s = "GROUP"; break; case elfdom`SHT_SYMTAB_SHNDX: s = "SYMTAB_SHNDX"; break; case elfdom`SHT_NUM: s = "NUM"; break; case elfdom`SHT_LOOS: s = "LOOS"; break; case elfdom`SHT_GNU_HASH: s = "GNU_HASH"; break; case elfdom`SHT_GNU_LIBLIST: s = "GNU_LIBLIST"; break; case elfdom`SHT_CHECKSUM: s = "CHECKSUM"; break; case elfdom`SHT_LOSUNW: s = "LOSUNW"; break; case elfdom`SHT_SUNW_move: s = "SUNW_move"; break; case elfdom`SHT_SUNW_COMDAT: s = "SUNW_COMDAT"; break; case elfdom`SHT_SUNW_syminfo: s = "SUNW_syminfo"; break; case elfdom`SHT_GNU_verdef: s = "GNU_verdef"; break; case elfdom`SHT_GNU_verneed: s = "VERNEED"; break; case elfdom`SHT_GNU_versym: s = "VERSYM"; break; case elfdom`SHT_HISUNW: s = "HISUNW"; break; case elfdom`SHT_HIOS: s = "HIOS"; break; case elfdom`SHT_LOPROC: s = "LOPROC"; break; case elfdom`SHT_HIPROC: s = "HIPROC"; break; case elfdom`SHT_LOUSER: s = "LOUSER"; break; case elfdom`SHT_HIUSER: s = "HIUSER"; break; } return s; } @define ptype2str(elfdom, p) { @local s; s = "UNKNOWN"; switch(p) { case elfdom`PT_NULL: s = "NULL"; break; case elfdom`PT_LOAD: s = "LOAD"; break; case elfdom`PT_DYNAMIC: s = "DYNAMIC"; break; case elfdom`PT_INTERP: s = "INTERP"; break; case elfdom`PT_NOTE: s = "NOTE"; break; case elfdom`PT_SHLIB: s = "SHLIB"; break; case elfdom`PT_PHDR: s = "PHDR"; break; case elfdom`PT_TLS: s = "TLS"; break; case elfdom`PT_NUM: s = "NUM"; break; case elfdom`PT_LOOS: s = "LOOS"; break; case elfdom`PT_GNU_EH_FRAME: s = "GNU_EH_FRAME"; break; case elfdom`PT_GNU_STACK: s = "GNU_STACK"; break; case elfdom`PT_GNU_RELRO: s = "GNU_RELRO"; break; case elfdom`PT_LOSUNW: s = "LOSUNW"; break; case elfdom`PT_SUNWBSS: s = "SUNWBSS"; break; case elfdom`PT_SUNWSTACK: s = "SUNWSTACK"; break; case elfdom`PT_HISUNW: s = "HISUNW"; break; case elfdom`PT_HIOS: s = "HIOS"; break; case elfdom`PT_LOPROC: s = "LOPROC"; break; case elfdom`PT_HIPROC: s = "HIPROC"; break; } return s; } @define machine2str(elfdom, emachine) { @local s; s = "UNKNOWN"; switch(emachine) { case elfdom`EM_386: s = "Intel 80386"; break; case elfdom`EM_X86_64: s = "Advanced Micro Devices X86-64"; break; } return s; } @define flags2str(elfdom, shflags) { @local s, cur; s = mkstr(4); cur = 0; if (shflags & elfdom`SHF_WRITE) { strput(s, cur++, "W"); } if (shflags & elfdom`SHF_ALLOC) { strput(s, cur++, "A"); } if (shflags & elfdom`SHF_EXECINSTR) { strput(s, cur++, "X"); } return s; } @define pflags2str(elfdom, pflags) { @local s; s = " "; if (pflags & elfdom`PF_X) { strput(s, 2, "E"); } if (pflags & elfdom`PF_W) { strput(s, 1, "W"); } if (pflags & elfdom`PF_R) { strput(s, 0, "R"); } return s; } @define symtype2str(elfdom, v) { @local s; s = "UNKNOWN"; switch(v) { case elfdom`STT_NOTYPE: s = "NOTYPE"; break; case elfdom`STT_OBJECT: s = "OBJECT"; break; case elfdom`STT_FUNC: s = "FUNC"; break; case elfdom`STT_SECTION: s = "SECTION"; break; case elfdom`STT_FILE: s = "FILE"; break; case elfdom`STT_COMMON: s = "COMMON"; break; case elfdom`STT_TLS: s = "TLS"; break; case elfdom`STT_NUM: s = "NUM"; break; case elfdom`STT_LOOS: s = "LOOS"; break; case elfdom`STT_HIOS: s = "HIOS"; break; case elfdom`STT_LOPROC: s = "LOPROC"; break; case elfdom`STT_HIPROC: s = "HIPROC"; break; } return s; } @define symvis2str(elfdom, v) { @local l; l = ["DEFAULT", "INTERNAL", "HIDDEN", "PROTECTED"]; return listref(l, v); } @define symidx2str(elfdom, v) { @local s; switch(v) { case elfdom`SHN_UNDEF: s = "UND"; break; case elfdom`SHN_ABS: s = "ABS"; break; /* case elfdom`SHN_LORESERVE: */ /* s = "LORESERVE"; */ /* break; */ /* case elfdom`SHN_LOPROC: */ /* s = "LOPROC"; */ /* break; */ /* case elfdom`SHN_BEFORE: */ /* s = "BEFORE"; */ /* break; */ /* case elfdom`SHN_AFTER: */ /* s = "AFTER"; */ /* break; */ /* case elfdom`SHN_HIPROC: */ /* s = "HIPROC"; */ /* break; */ /* case elfdom`SHN_LOOS: */ /* s = "LOOS"; */ /* break; */ /* case elfdom`SHN_HIOS: */ /* s = "HIOS"; */ /* break; */ /* case elfdom`SHN_COMMON: */ /* s = "COMMON"; */ /* break; */ /* case elfdom`SHN_XINDEX: */ /* s = "XINDEX"; */ /* break; */ /* case elfdom`SHN_HIRESERVE: */ /* s = "HIRESERVE"; */ /* break; */ default: s = sprintfa("%3d", v); } return s; } @define symbind2str(elfdom, symbind) { @local s; s = "UNKNOWN"; switch(symbind) { case elfdom`STB_LOCAL: s = "LOCAL"; break; case elfdom`STB_GLOBAL: s = "GLOBAL"; break; case elfdom`STB_WEAK: s = "WEAK"; break; case elfdom`STB_NUM: s = "NUM"; break; case elfdom`STB_LOOS: s = "LOOS"; break; case elfdom`STB_HIOS: s = "HIOS"; break; case elfdom`STB_LOPROC: s = "LOPROC"; break; case elfdom`STB_HIPROC: s = "HIPROC"; break; } return s; } @define ntype2str(elfdom, ntype) { @local s; switch(ntype) { case elfdom`NT_PRSTATUS: s = "NT_PRSTATUS (prstatus structure)"; break; case elfdom`NT_FPREGSET: s = "NT_FPREGSET (floating point registers)"; break; case elfdom`NT_PRPSINFO: s = "NT_PRPSINFO (prpsinfo structure)"; break; case elfdom`NT_PRXREG: s = "NT_PRXREG"; break; case elfdom`NT_TASKSTRUCT: s = "NT_TASKSTRUCT"; break; case elfdom`NT_PLATFORM: s = "NT_PLATFORM"; break; case elfdom`NT_AUXV: s = "NT_AUXV (auxiliary vector)"; break; case elfdom`NT_GWINDOWS: s = "NT_GWINDOWS"; break; case elfdom`NT_ASRS: s = "NT_ASRS"; break; case elfdom`NT_PSTATUS: s = "NT_PSTATUS"; break; case elfdom`NT_PSINFO: s = "NT_PSINFO"; break; case elfdom`NT_PRCRED: s = "NT_PRCRED"; break; case elfdom`NT_UTSNAME: s = "NT_UTSNAME"; break; case elfdom`NT_LWPSTATUS: s = "NT_LWPSTATUS"; break; case elfdom`NT_LWPSINFO: s = "NT_LWPSINFO"; break; case elfdom`NT_PRFPXREG: s = "NT_PRFPXREG"; break; case elfdom`NT_VERSION: s = "NT_VERSION"; break; case elfdom`NT_PRXFPREG: s = "NT_PRXFPREG (user_xfpregs structure)"; break; case elfdom`NT_PPC_VMX: s = "NT_PPC_VMX"; break; default: s = sprintfa("UNKNOWN: 0x%x", ntype); } return s; } @define print_fileheader(elfdom) { @local e, i, elftype; if (sizeof (nsptr (elfdom.ns)) == 4) { e = (elfdom`Elf32_Ehdr *){elfdom} 0; elftype = "ELF32"; } else { e = (elfdom`Elf64_Ehdr *){elfdom} 0; elftype = "ELF64"; } printf("ELF Header:\n"); printf(" Magic: "); for (i = 0; i < 16; i++) { printf("%.2x ", e->e_ident[i]); } printf("\n"); printf(" Class: %s\n", elftype); printf(" Data: %s\n", e->e_ident[e`EI_DATA] == e`ELFDATANONE ? "NONE" : e->e_ident[e`EI_DATA] == e`ELFDATA2LSB ? "2's complement, little endian" : "2's complement, big endian"); printf(" Version: %d %s\n", e->e_ident[e`EI_VERSION], e->e_ident[e`EI_VERSION] == 1 ? "(current)" : ""); printf(" OS/ABI: %s\n", abi2str(e, e->e_ident[e`EI_OSABI])); printf(" ABI Version: %d\n", e->e_ident[e`EI_ABIVERSION]); printf(" Type: %s\n", type2str(e, e->e_type)); printf(" Machine: %s\n", machine2str(e, e->e_machine)); printf(" Version: 0x%x\n", e->e_version); printf(" Entry point address: 0x%x\n", e->e_entry); printf(" Start of program headers: %d (bytes into file)\n", e->e_phoff); printf(" Start of section headers: %d (bytes into file)\n", e->e_shoff); printf(" Flags: 0x%x\n", e->e_flags); printf(" Size of this header: %d (bytes)\n", e->e_ehsize); printf(" Size of program headers: %d (bytes)\n", e->e_phentsize); printf(" Number of program headers: %d\n", e->e_phnum); printf(" Size of section headers: %d (bytes)\n", e->e_shentsize); printf(" Number of section headers: %d\n", e->e_shnum); printf(" Section header string table index: %d\n", e->e_shstrndx); } @define print_secheaders32(elfdom, e, sarray) { @local st, i; st = (char *) sarray[e->e_shstrndx].sh_offset; printf("There are %d section headers, starting at offset 0x%x:\n", e->e_shnum, e->e_shoff); printf("\n"); printf("Section Headers:\n"); printf(" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n"); for (i = 0; i < e->e_shnum; i++) { @local s; s = sarray + i; printf(" [%2d] %-17s %-15s %.8x %.6x %.6x %.2x %3s %2d %3d %2d\n", i, &st[s->sh_name], sectype2str(elfdom, s->sh_type), s->sh_addr, s->sh_offset, s->sh_size, s->sh_entsize, flags2str(elfdom, s->sh_flags), s->sh_link, s->sh_info, s->sh_addralign); } } @define print_secheaders64(elfdom, e, sarray) { @local st, i; st = (char *) sarray[e->e_shstrndx].sh_offset; printf("There are %d section headers, starting at offset 0x%x\n:", e->e_shnum, e->e_shoff); printf("\n"); printf("Section Headers:\n"); printf(" [Nr] Name Type Address Offset\n"); printf(" Size EntSize Flags Link Info Align\n"); for (i = 0; i < e->e_shnum; i++) { @local s; s = sarray + i; printf(" [%2d] %-16s %-16s %.16x %.8x\n", i, &st[s->sh_name], sectype2str(elfdom, s->sh_type), s->sh_addr, s->sh_offset); printf(" %.16x %.16x %8s%1d%6d %1d\n", s->sh_size, s->sh_entsize, flags2str(elfdom, s->sh_flags), s->sh_link, s->sh_info, s->sh_addralign); } } @define print_secheaders(elfdom) { @local e, sarray; if (sizeof (nsptr (elfdom.ns)) == 4) { e = (elfdom`Elf32_Ehdr *){elfdom} 0; sarray = (elfdom`Elf32_Shdr *) e->e_shoff; print_secheaders32(elfdom, e, sarray); } else { e = (elfdom`Elf64_Ehdr *){elfdom} 0; sarray = (elfdom`Elf64_Shdr *) e->e_shoff; print_secheaders64(elfdom, e, sarray); } printf("Key to Flags:\n"); printf(" W (write), A (alloc), X (execute), M (merge), S (strings)\n"); printf(" I (info), L (link order), G (group), x (unknown)\n"); printf(" O (extra OS processing required) o (OS specific), p (processor specific)\n"); } @define print_progheaders(elfdom) { @local e, pharray, p, i, is32; if (sizeof (nsptr (elfdom.ns)) == 4) { e = (elfdom`Elf32_Ehdr *){elfdom} 0; pharray = (elfdom`Elf32_Phdr *) e->e_phoff; is32 = 1; } else { e = (elfdom`Elf64_Ehdr *){elfdom} 0; pharray = (elfdom`Elf64_Phdr *) e->e_phoff; is32 = 0; } printf("\n"); printf("Program Headers:\n"); if (is32) printf(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n"); else { printf(" Type Offset VirtAddr PhysAddr\n"); printf(" FileSiz MemSiz Flags Align\n"); } for (i = 0; i < e->e_phnum; i++) { p = pharray + i; if (is32) { printf(" %-14s 0x%.6x 0x%.8x 0x%.8x 0x%.5x 0x%.5x %3s 0x%x\n", ptype2str(elfdom, p->p_type), p->p_offset, p->p_vaddr, p->p_paddr, p->p_filesz, p->p_memsz, pflags2str(elfdom, p->p_flags), p->p_align); } else { printf(" %-14s 0x%.16x 0x%.16x 0x%.16x\n", ptype2str(elfdom, p->p_type), p->p_offset, p->p_vaddr, p->p_paddr); printf(" 0x%.16x 0x%.16x %-6s %x\n", p->p_filesz, p->p_memsz, pflags2str(elfdom, p->p_flags), p->p_align); } if (p->p_type == elfdom`PT_INTERP) { printf(" [Requesting program interpreter: %s]\n", (char *){elfdom}p->p_offset); } } printf("\n"); printf(" Section to Segment mapping: UNIMPLEMENTED\n"); // printf(" Segment Sections...\n"); } @define print_symbols(elfdom) { @local e, shbase, strbase, symbase, sec, sym, i, st, is32; if (sizeof (nsptr (elfdom.ns)) == 4) { e = (elfdom`Elf32_Ehdr *){elfdom} 0; shbase = (elfdom`Elf32_Shdr *) (e->e_shoff); is32 = 1; } else { e = (elfdom`Elf64_Ehdr *){elfdom} 0; shbase = (elfdom`Elf64_Shdr *) (e->e_shoff); is32 = 0; } st = (char *) shbase[e->e_shstrndx].sh_offset; for (i = 0; i < e->e_shnum; i++) { sec = &shbase[i]; if (sec->sh_type == elfdom`SHT_SYMTAB || sec->sh_type == elfdom`SHT_DYNSYM) { @local symcount, strind, j; if (is32) { symbase = (elfdom`Elf32_Sym *)sec->sh_offset; symcount = sec->sh_size / (sizeof (elfdom`Elf32_Sym)); } else { symbase = (elfdom`Elf64_Sym *)sec->sh_offset; symcount = sec->sh_size / (sizeof (elfdom`Elf64_Sym)); } strind = sec->sh_link; strbase = (unsigned char *) shbase[strind].sh_offset; printf("\n"); printf("Symbol table '%s' contains %d entries:\n", &st[sec->sh_name], symcount); if (is32) printf(" Num: Value Size Type Bind Vis Ndx Name\n"); else printf(" Num: Value Size Type Bind Vis Ndx Name\n"); for (j = 0; j < symcount; j++) { @local name; if (is32) { sym = (elfdom`Elf32_Sym *)(&symbase[j]); printf(" %3d: %.8x %5d %-7s %-6s %-8s %3s %s\n", j, sym->st_value, sym->st_size, symtype2str(elfdom, ELF64_ST_TYPE(sym->st_info)), symbind2str(elfdom, ELF64_ST_BIND(sym->st_info)), symvis2str(elfdom, ELF64_ST_VISIBILITY(sym->st_other)), symidx2str(elfdom, sym->st_shndx), &strbase[sym->st_name]); } else { sym = (elfdom`Elf64_Sym *)(&symbase[j]); printf(" %3d: %.16x %5d %-7s %-6s %-8s %3s %s\n", j, sym->st_value, sym->st_size, symtype2str(elfdom, ELF64_ST_TYPE(sym->st_info)), symbind2str(elfdom, ELF64_ST_BIND(sym->st_info)), symvis2str(elfdom, ELF64_ST_VISIBILITY(sym->st_other)), symidx2str(elfdom, sym->st_shndx), &strbase[sym->st_name]); } } } } } // prints first PT_NOTES section it finds @define print_notes(elfdom) { @local e, pharray, p, i, is32; if (sizeof (nsptr (elfdom.ns)) == 4) { e = (elfdom`Elf32_Ehdr *){elfdom} 0; pharray = (elfdom`Elf32_Phdr *) e->e_phoff; is32 = 1; } else { e = (elfdom`Elf64_Ehdr *){elfdom} 0; pharray = (elfdom`Elf64_Phdr *) e->e_phoff; is32 = 0; } for (i = 0; i < e->e_phnum; i++) { p = pharray + i; if (p->p_type == elfdom`PT_NOTE) { @local cur; printf("\n"); printf("Notes at offset 0x%.8x with length 0x%.8x:\n", p->p_offset, p->p_filesz); printf(" Owner Data size Description\n"); cur = p->p_offset; while (cur < p->p_offset + p->p_filesz) { @local nhdr; if (is32) { nhdr = (elfdom`Elf32_Nhdr *) cur; } else { nhdr = (elfdom`Elf64_Nhdr *) cur; } printf(" %s\t\t0x%.8x\t%s\n", (char *)(cur + sizeof(*nhdr)), nhdr->n_descsz, ntype2str(elfdom, nhdr->n_type)); /* printf("nhdr at 0x%x (0x%x, 0x%x, 0x%x), %s\n", cur, */ /* nhdr->n_namesz, nhdr->n_descsz, nhdr->n_type, */ /* ); */ cur = cur + sizeof(*nhdr) + align(nhdr->n_namesz, 4) + align(nhdr->n_descsz, 4); } break; } } } @define usage() { printf("Usage: l1 -e readelf.cqct elf-file(s)\n" "Display information about the contents of ELF format files\n" "Options are:\n" " -h --file-header Display the ELF file header\n" " -l --program-headers Display the program headers\n" " --segments An alias for --program-headers\n" " -S --section-headers Display the sections' header\n" " --sections An alias for --section-headers\n" " -e --headers Equivalent to: -h -l -S\n" " -s --syms Display the symbol table\n" " --symbols An alias for --syms\n" " -n --notes Display the core notes (if present)\n" " -v --version Display the version number of readelf\n" ); } @define main(args) { @local cur; @local elfdom, infile; @local do_secheaders, do_fileheader, do_progheaders, do_syms; infile = nil; do_secheaders = 0; do_fileheader = 0; do_progheaders = 0; do_syms = 0; do_notes = 0; if (length(args) == 0) { printf("args is %d\n", length(args)); usage(); return 0; } cur = 0; while (cur < length(args)) { switch(listref(args, cur)) { case "-e": do_fileheader = 1; do_secheaders = 1; do_progheaders = 1; break; case "-h": case "--file-header": do_fileheader = 1; break; case "-H": case "--help": usage(); return 0; case "-l": case "--program-headers": case "--segments": do_progheaders = 1; break; case "-S": case "--section-headers": case "--sections": do_secheaders = 1; break; case "-s": case "--syms": case "--symbols": do_syms = 1; break; case "-n": do_notes = 1; break; case "-v": case "--version": printf("readelf.cqct version 0.1\n"); return 0; default: if (substr(listref(args, cur), 0, 1) == "-") { printf("Unknown option %s\n", listref(args, cur)); return -1; } else { infile = listref(args, cur); } } cur++; } if (infile == nil) { printf("readelf: Warning: Nothing to do.\n"); usage(); return 0; } elfdom = mkelfrec(mapfile(infile)).elf; if (do_fileheader) print_fileheader(elfdom); if (do_secheaders) print_secheaders(elfdom); if (do_progheaders) { if (!do_fileheader) { @local e; if (sizeof (nsptr (elfdom.ns)) == 4) { e = (elfdom`Elf32_Ehdr *){elfdom} 0; } else { e = (elfdom`Elf64_Ehdr *){elfdom} 0; } printf("\nElf file type is %s\n", type2str(e, e->e_type)); printf("Entry point 0x%x\n", e->e_entry); printf("There are %d program headers, starting at offset %d\n", e->e_phnum, e->e_phoff); } print_progheaders(elfdom); } if (do_syms) print_symbols(elfdom); if (do_notes) print_notes(elfdom); return 0; } return main(args);