Skip to content

Instantly share code, notes, and snippets.

@probonopd
Last active December 4, 2022 09:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save probonopd/a490ba3401b5ef7b881d5e603fa20c93 to your computer and use it in GitHub Desktop.
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
/*
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;
}
/*
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;
}
@afwn90cj93201nixr2e1re
Copy link

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...

@afwn90cj93201nixr2e1re
Copy link

afwn90cj93201nixr2e1re commented Mar 31, 2020

@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

@probonopd
Copy link
Author

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