Skip to content

Commit

Permalink
Added fatelf-extract utility.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Sep 24, 2009
1 parent 8f8f0cb commit 6ad3c13
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 5 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -6,6 +6,7 @@ ADD_DEFINITIONS(-Wall -Werror)

ADD_EXECUTABLE(fatelf-glue utils/fatelf-glue.c utils/fatelf-utils.c)
ADD_EXECUTABLE(fatelf-info utils/fatelf-info.c utils/fatelf-utils.c)
ADD_EXECUTABLE(fatelf-extract utils/fatelf-extract.c utils/fatelf-utils.c)

# end of CMakeLists.txt ...

6 changes: 6 additions & 0 deletions test/test.sh
Expand Up @@ -31,6 +31,12 @@ mv hello.so hello-amd64.so
./fatelf-info ./hello.so
./fatelf-info ./hello-dlopen

# fatelf-extract tests
./fatelf-extract ./extract-x86 ./hello 0
diff --brief ./hello-x86 ./extract-x86
./fatelf-extract ./extract-amd64 ./hello x86_64:sysv:osabiver0:le:64bit
diff --brief ./hello-amd64 ./extract-amd64

# file(1) tests.
file ./hello
file ./hello.so
Expand Down
43 changes: 43 additions & 0 deletions utils/fatelf-extract.c
@@ -0,0 +1,43 @@
/**
* FatELF; support multiple ELF binaries in one file.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/

#define FATELF_UTILS 1
#include "fatelf-utils.h"

static int fatelf_extract(const char *out, const char *fname,
const char *target)
{
const int fd = xopen(fname, O_RDONLY, 0755);
FATELF_header *header = xread_fatelf_header(fname, fd);
const int recidx = xfind_fatelf_record(header, target);
const int outfd = xopen(out, O_WRONLY | O_CREAT | O_TRUNC, 0755);
const FATELF_record *rec = &header->records[recidx];

unlink_on_xfail = out;

xcopyfile_range(fname, fd, out, outfd, rec->offset, rec->size);
xclose(out, outfd);
xclose(fname, fd);
free(header);

unlink_on_xfail = NULL;

return 0; // success.
} // fatelf_extract


int main(int argc, const char **argv)
{
xfatelf_init(argc, argv);
if (argc != 4) // this could stand to use getopt(), later.
xfail("USAGE: %s <out> <in> <target>", argv[0]);
return fatelf_extract(argv[1], argv[2], argv[3]);
} // main

// end of fatelf-extract.c ...

177 changes: 172 additions & 5 deletions utils/fatelf-utils.c
Expand Up @@ -43,6 +43,15 @@ void *xmalloc(const size_t len)
} // xmalloc


// Allocate a copy of (str), xfail() on allocation failure.
char *xstrdup(const char *str)
{
char *retval = (char *) xmalloc(strlen(str) + 1);
strcpy(retval, str);
return retval;
} // xstrdup


// xfail() on error.
int xopen(const char *fname, const int flags, const int perms)
{
Expand Down Expand Up @@ -106,25 +115,47 @@ void xlseek(const char *fname, const int fd,
} // xlseek


static uint8_t copybuf[256 * 1024];

// xfail() on error.
uint64_t xcopyfile(const char *in, const int infd,
const char *out, const int outfd)
{
// !!! FIXME: use sendfile() on Linux (if it'll handle non-socket fd's).
static uint8_t buf[256 * 1024];
uint64_t retval = 0;
ssize_t rc = 0;
xlseek(in, infd, 0, SEEK_SET);
while ( (rc = xread(in, infd, buf, sizeof (buf), 0)) > 0 )
while ( (rc = xread(in, infd, copybuf, sizeof (copybuf), 0)) > 0 )
{
xwrite(out, outfd, buf, rc);
xwrite(out, outfd, copybuf, rc);
retval += (uint64_t) rc;
} // while

return retval;
} // xcopyfile


static inline uint64_t minui64(const uint64_t a, const uint64_t b)
{
return (a < b) ? a : b;
} // minui64


void xcopyfile_range(const char *in, const int infd,
const char *out, const int outfd,
const uint64_t offset, const uint64_t size)
{
uint64_t remaining = size;
xlseek(in, infd, (off_t) offset, SEEK_SET);
while (remaining)
{
const size_t cpysize = minui64(remaining, sizeof (copybuf));
xread(in, infd, copybuf, cpysize, 1);
xwrite(out, outfd, copybuf, cpysize);
remaining -= (uint64_t) cpysize;
} // while
} // xcopyfile_range


void xread_elf_header(const char *fname, const int fd, FATELF_record *record)
{
const uint8_t magic[4] = { 0x7F, 0x45, 0x4C, 0x46 };
Expand Down Expand Up @@ -475,7 +506,7 @@ static const fatelf_osabi_info osabis[] =
{ 13, "openvms", "OpenVMS" },
{ 14, "nsk", "Hewlett-Packard Non-Stop Kernel" },
{ 15, "aros", "Amiga Research OS" },
{ 97, "arm", "ARM" },
{ 97, "armabi", "ARM" },
{ 255, "standalone", "Standalone application" },
};

Expand Down Expand Up @@ -536,6 +567,142 @@ const fatelf_osabi_info *get_osabi_by_name(const char *name)
} // get_osabi_by_name


static int parse_abi_version_string(const char *str)
{
long num = 0;
char *endptr = NULL;
const char *prefix = "osabiver";
const size_t prefix_len = 8;
assert(strlen(prefix) == prefix_len);
if (strncmp(str, prefix, prefix_len) != 0)
return -1;

str += prefix_len;
num = strtol(str, &endptr, 0);
return ( ((endptr == str) || (*endptr != '\0')) ? -1 : ((int) num) );
} // parse_abi_version_string


static int xfind_fatelf_record_by_fields(const FATELF_header *header,
const char *target)
{
char *buf = xstrdup(target);
const fatelf_osabi_info *osabi = NULL;
const fatelf_machine_info *machine = NULL;
FATELF_record rec;
int want_machine = 0;
int want_osabi = 0;
int want_osabiver = 0;
int want_wordsize = 0;
int want_byteorder = 0;
int abiver = 0;
char *str = buf;
char *ptr = buf;
int retval = -1;
int i = 0;

while (1)
{
const char ch = *ptr;
if ((ch == ':') || (ch == '\0'))
{
*ptr = '\0';

if (ptr == str)
{
// no-op for empty string.
} // if
else if ((strcmp(str,"be")==0) || (strcmp(str,"bigendian")==0))
{
want_byteorder = 1;
rec.byte_order = FATELF_BIGENDIAN;
} // if
else if ((strcmp(str,"le")==0) || (strcmp(str,"littleendian")==0))
{
want_byteorder = 1;
rec.byte_order = FATELF_LITTLEENDIAN;
} // else if
else if (strcmp(str,"32bit") == 0)
{
want_wordsize = 1;
rec.word_size = FATELF_32BITS;
} // else if
else if (strcmp(str,"64bit") == 0)
{
want_wordsize = 1;
rec.word_size = FATELF_64BITS;
} // else if
else if ((machine = get_machine_by_name(str)) != NULL)
{
want_machine = 1;
rec.machine = machine->id;
} // else if
else if ((osabi = get_osabi_by_name(str)) != NULL)
{
want_osabi = 1;
rec.osabi = osabi->id;
} // else if
else if ((abiver = parse_abi_version_string(str)) != -1)
{
want_osabiver = 1;
rec.osabi_version = (uint8_t) abiver;
} // else if
else
{
xfail("Unknown target '%s'", str);
} // else

if (ch == '\0')
break; // we're done.

str = ptr + 1;
} // if

ptr++;
} // while

free(buf);

for (i = 0; i < ((int) header->num_records); i++)
{
const FATELF_record *prec = &header->records[i];
if ((want_machine) && (rec.machine != prec->machine))
continue;
else if ((want_osabi) && (rec.osabi != prec->osabi))
continue;
else if ((want_osabiver) && (rec.osabi_version != prec->osabi_version))
continue;
else if ((want_wordsize) && (rec.word_size != prec->word_size))
continue;
else if ((want_byteorder) && (rec.byte_order != prec->byte_order))
continue;

if (retval != -1)
xfail("Ambiguous target '%s'", target);
retval = i;
} // for

return retval;
} // xfind_fatelf_record_by_fields


int xfind_fatelf_record(const FATELF_header *header, const char *target)
{
char *endptr = NULL;
const long num = strtol(target, &endptr, 0);

if ((endptr != target) && (*endptr == '\0')) // a numeric index?
{
const long recs = (long) header->num_records;
if ((num < 0) || (num > recs))
xfail("No record #%ld in FatELF header (max %d)", num, (int) recs);
return (int) num;
} // if

return xfind_fatelf_record_by_fields(header, target);
} // xfind_fatelf_record


void xfatelf_init(int argc, const char **argv)
{
memset(zerobuf, '\0', sizeof (zerobuf)); // just in case.
Expand Down
12 changes: 12 additions & 0 deletions utils/fatelf-utils.h
Expand Up @@ -52,6 +52,9 @@ void xfail(const char *fmt, ...);
// Memory is guaranteed to be initialized to zero.
void *xmalloc(const size_t len);

// Allocate a copy of (str), xfail() on allocation failure.
char *xstrdup(const char *str);

// These all xfail() on error and handle EINTR for you.
int xopen(const char *fname, const int flags, const int perms);
ssize_t xread(const char *fname, const int fd, void *buf,
Expand All @@ -68,6 +71,11 @@ void xwrite_zeros(const char *fname, const int fd, size_t len);
uint64_t xcopyfile(const char *in, const int infd,
const char *out, const int outfd);

// copy file from infd to current offset in outfd, for size bytes.
void xcopyfile_range(const char *in, const int infd,
const char *out, const int outfd,
const uint64_t offset, const uint64_t size);

// read the parts of an ELF header we care about.
void xread_elf_header(const char *fname, const int fd, FATELF_record *rec);

Expand All @@ -90,6 +98,10 @@ const fatelf_machine_info *get_machine_by_name(const char *name);
const fatelf_osabi_info *get_osabi_by_id(const uint8_t id);
const fatelf_osabi_info *get_osabi_by_name(const char *name);

// Find the desired record in the FatELF header, based on a string in
// various formats.
int xfind_fatelf_record(const FATELF_header *header, const char *target);

// Call this at the start of main().
void xfatelf_init(int argc, const char **argv);

Expand Down

0 comments on commit 6ad3c13

Please sign in to comment.