Last active
December 4, 2022 09:02
-
-
Save probonopd/a490ba3401b5ef7b881d5e603fa20c93 to your computer and use it in GitHub Desktop.
Calculate the size of an ELF file on disk based on the information in its header
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Compile with: | |
gcc elfsize.c -o elfsize | |
Example: | |
ls -l 126584 | |
Calculation using the values also reported by readelf -h: | |
Start of section headers e_shoff 124728 | |
Size of section headers e_shentsize 64 | |
Number of section headers e_shnum 29 | |
e_shoff + ( e_shentsize * e_shnum ) = 126584 | |
*/ | |
#include <elf.h> | |
#include <byteswap.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <fcntl.h> | |
typedef Elf32_Nhdr Elf_Nhdr; | |
static char *fname; | |
static Elf64_Ehdr ehdr; | |
static Elf64_Phdr *phdr; | |
#if __BYTE_ORDER == __LITTLE_ENDIAN | |
#define ELFDATANATIVE ELFDATA2LSB | |
#elif __BYTE_ORDER == __BIG_ENDIAN | |
#define ELFDATANATIVE ELFDATA2MSB | |
#else | |
#error "Unknown machine endian" | |
#endif | |
static uint16_t file16_to_cpu(uint16_t val) | |
{ | |
if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | |
val = bswap_16(val); | |
return val; | |
} | |
static uint32_t file32_to_cpu(uint32_t val) | |
{ | |
if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | |
val = bswap_32(val); | |
return val; | |
} | |
static uint64_t file64_to_cpu(uint64_t val) | |
{ | |
if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | |
val = bswap_64(val); | |
return val; | |
} | |
static long unsigned int read_elf32(int fd) | |
{ | |
Elf32_Ehdr ehdr32; | |
ssize_t ret, i; | |
ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); | |
if (ret < 0 || (size_t)ret != sizeof(ehdr)) { | |
fprintf(stderr, "Read of ELF header from %s failed: %s\n", | |
fname, strerror(errno)); | |
exit(10); | |
} | |
ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); | |
ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); | |
ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); | |
return(ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum)); | |
} | |
static long unsigned int read_elf64(int fd) | |
{ | |
Elf64_Ehdr ehdr64; | |
ssize_t ret, i; | |
ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); | |
if (ret < 0 || (size_t)ret != sizeof(ehdr)) { | |
fprintf(stderr, "Read of ELF header from %s failed: %s\n", | |
fname, strerror(errno)); | |
exit(10); | |
} | |
ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); | |
ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); | |
ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); | |
return(ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum)); | |
} | |
long unsigned int get_elf_size(char *fname) | |
/* TODO, FIXME: This assumes that the section header table (SHT) is | |
the last part of the ELF. This is usually the case but | |
it could also be that the last section is the last part | |
of the ELF. This should be checked for. | |
*/ | |
{ | |
ssize_t ret; | |
int fd; | |
long unsigned int size = 0; | |
fd = open(fname, O_RDONLY); | |
if (fd < 0) { | |
fprintf(stderr, "Cannot open %s: %s\n", | |
fname, strerror(errno)); | |
return(1); | |
} | |
ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); | |
if (ret != EI_NIDENT) { | |
fprintf(stderr, "Read of e_ident from %s failed: %s\n", | |
fname, strerror(errno)); | |
return(1); | |
} | |
if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && | |
(ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) | |
{ | |
fprintf(stderr, "Unkown ELF data order %u\n", | |
ehdr.e_ident[EI_DATA]); | |
return(1); | |
} | |
if(ehdr.e_ident[EI_CLASS] == ELFCLASS32) { | |
size = read_elf32(fd); | |
} else if(ehdr.e_ident[EI_CLASS] == ELFCLASS64) { | |
size = read_elf64(fd); | |
} else { | |
fprintf(stderr, "Unknown ELF class %u\n", ehdr.e_ident[EI_CLASS]); | |
return(1); | |
} | |
close(fd); | |
return size; | |
} | |
int main(int argc, char **argv) | |
{ | |
ssize_t ret; | |
int fd; | |
if (argc != 2) { | |
fprintf(stderr, "Usage: %s <ELF>\n", argv[0]); | |
return 1; | |
} | |
fname = argv[1]; | |
long unsigned int size = get_elf_size(fname); | |
fprintf(stderr, "Estimated ELF size on disk: %lu bytes \n", size); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Compile on FreeBSD with: | |
clang elfsize_v2.c -o elfsize | |
*/ | |
#include <elf.h> | |
#include <sys/endian.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <fcntl.h> | |
// Define ELF_Nhdr type | |
typedef Elf32_Nhdr Elf_Nhdr; | |
// Declare global variables | |
static char *fname; | |
static Elf64_Ehdr ehdr; | |
static Elf64_Phdr *phdr; | |
// Define ELFDATANATIVE macro | |
#if __BYTE_ORDER == __LITTLE_ENDIAN | |
#define ELFDATANATIVE ELFDATA2LSB | |
#elif __BYTE_ORDER == __BIG_ENDIAN | |
#define ELFDATANATIVE ELFDATA2MSB | |
#else | |
#error "Unknown machine endian" | |
#endif | |
// Declare utility functions | |
static uint16_t file16_to_cpu(uint16_t val); | |
static uint32_t file32_to_cpu(uint32_t val); | |
static uint64_t file64_to_cpu(uint64_t val); | |
static long unsigned int read_elf32(int fd); | |
static long unsigned int read_elf64(int fd); | |
// Define file16_to_cpu function | |
static uint16_t file16_to_cpu(uint16_t val) | |
{ | |
if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | |
val = (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) ? le16toh(val) : be16toh(val); | |
return val; | |
} | |
// Define file32_to_cpu function | |
static uint32_t file32_to_cpu(uint32_t val) | |
{ | |
if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | |
val = (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) ? le32toh(val) : be32toh(val); | |
return val; | |
} | |
// Define file64_to_cpu function | |
static uint64_t file64_to_cpu(uint64_t val) | |
{ | |
if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) | |
val = (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) ? le64toh(val) : be64toh(val); | |
return val; | |
} | |
// Define read_elf32 function | |
static long unsigned int read_elf32(int fd) | |
{ | |
Elf32_Ehdr ehdr32; | |
ssize_t ret, i; | |
// Read ELF header (32-bit) | |
ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); | |
if (ret < 0 || (size_t)ret != sizeof(ehdr)) { | |
fprintf(stderr, "Read of ELF header from %s failed: %s\n", fname, strerror(errno)); | |
exit(10); | |
} | |
// Convert values from file endianness to host endianness | |
ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); | |
ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); | |
ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); | |
// Calculate size of ELF file | |
return ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum); | |
} | |
// Define read_elf64 function | |
static long unsigned int read_elf64(int fd) | |
{ | |
Elf64_Ehdr ehdr64; | |
ssize_t ret, i; | |
// Read ELF header (64-bit) | |
ret = pread (fd, &ehdr64, sizeof(ehdr64), 0); | |
if (ret < 0 || (size_t)ret != sizeof(ehdr)) { | |
fprintf(stderr, "Read of ELF header from %s failed: %s\n", fname, strerror(errno)); | |
exit(10); | |
} | |
// Convert values from file endianness to host endianness | |
ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); | |
ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); | |
ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); | |
// Calculate size of ELF file | |
return ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum); | |
} | |
// Define get_elf_size function | |
long unsigned int get_elf_size(char *fname) | |
/* TODO, FIXME: This assumes that the section header table (SHT) is | |
the last part of the ELF. This is usually the case but | |
it could also be that the last section is the last part | |
of the ELF. This should be checked for. | |
*/ | |
{ | |
ssize_t ret; | |
int fd; | |
long unsigned int size = 0; | |
// Open ELF file | |
fd = open(fname, O_RDONLY); | |
if (fd < 0) { | |
fprintf(stderr, "Cannot open %s: %s\n", fname, strerror(errno)); | |
return 0; | |
} | |
// Read ELF magic number | |
ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); | |
if (ret != EI_NIDENT) { | |
fprintf(stderr, "Read of e_ident from %s failed: %s\n", fname, strerror(errno)); | |
return 0; | |
} | |
// Check ELF data order | |
if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) { | |
fprintf(stderr, "Unkown ELF data order %u\n", ehdr.e_ident[EI_DATA]); | |
return 0; | |
} | |
// Read ELF header | |
if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) { | |
size = read_elf32(fd); | |
} else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { | |
size = read_elf64(fd); | |
} else { | |
fprintf(stderr, "Unknown ELF class %u\n", ehdr.e_ident[EI_CLASS]); | |
return 0; | |
} | |
// Close ELF file | |
close(fd); | |
// Return size of ELF file | |
return size; | |
} | |
int main(int argc, char **argv) | |
{ | |
// Check for -h or --help argument | |
if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { | |
// Print usage information | |
printf("Usage: %s <ELF>\n", argv[0]); | |
printf("Estimates the size of the given ELF file on disk.\n"); | |
return 0; | |
} | |
// Check that a filename was provided as an argument | |
if (argc != 2) { | |
fprintf(stderr, "Error: No ELF file specified.\n"); | |
fprintf(stderr, "Use -h or --help for usage information.\n"); | |
return 1; | |
} | |
// Store filename for use by other functions | |
fname = argv[1]; | |
// Check if file exists | |
if (access(fname, F_OK) == -1) { | |
fprintf(stderr, "Error: File '%s' does not exist.\n", fname); | |
return 1; | |
} | |
// Get size of ELF file | |
long unsigned int size = get_elf_size(fname); | |
// Print estimated size of ELF file on disk | |
fprintf(stderr, "Estimated ELF size on disk: %lu bytes \n", size); | |
// Return success | |
return 0; | |
} |
Yes, I am really interested in on-disk size rather than in-memory size. Unfortunately I don't know the answer to your other questions.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@probonopd can you explain why file size is diff than proc/pid/maps?