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; | |
} |
@probonopd can you explain why file size is diff than proc/pid/maps?
0xf2e25000-0xf30fc000 r-xp 305399 /root/cs/cstrike/dlls/cs.so
0xf30fc000-0xf30fd000 r--p 305399 /root/cs/cstrike/dlls/cs.so
0xf30fd000-0xf3124000 rw-p 305399 /root/cs/cstrike/dlls/cs.so
0xf3124000-0xf31e3000 rw-p 0
0xf3124000-0xf2e25000 = 0x2FF000 3141632
0xf30fd000-0xf2e25000 = 0x2D8000 2981888
filesize on disk 3138496
3141632 - 3138496 = 0xc40
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
keep in mind, that file size different than memory page size, idk why, but sometimes it's less like 0xc40 or 0xc81 or something like that...