267 lines
7.7 KiB
C
267 lines
7.7 KiB
C
#define _POSIX_C_SOURCE 200112L
|
|
|
|
#include <elf.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
|
|
#ifndef align_up
|
|
#define align_up(num, align) (((num) + ((align)-1)) & ~((align)-1))
|
|
#endif
|
|
|
|
static const char* const usage =
|
|
"usage: blob-object-elf <output file> <section name> <symbol name> <blob file>\n";
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc < 5) {
|
|
fprintf(stderr, "not enough arguments\n%s", usage);
|
|
return 1;
|
|
}
|
|
if (argc > 5) {
|
|
fprintf(stderr, "too many arguments\n%s", usage);
|
|
return 1;
|
|
}
|
|
|
|
char* output_file_name = argv[1];
|
|
char* section_name = argv[2];
|
|
size_t section_name_size = strlen(section_name) + 1;
|
|
char* symbol_name = argv[3];
|
|
size_t symbol_name_size = strlen(symbol_name) + 1;
|
|
char* blob_file_name = argv[4];
|
|
|
|
int blob_fd = open(blob_file_name, O_RDONLY);
|
|
if (blob_fd < 0) {
|
|
fprintf(
|
|
stderr,
|
|
"failed to open blob file %s: %s\n%s",
|
|
output_file_name,
|
|
strerror(errno),
|
|
usage
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
off_t blob_size = lseek(blob_fd, 0, SEEK_END);
|
|
if (blob_size == (off_t) -1) {
|
|
fprintf(
|
|
stderr,
|
|
"failed to get size of blob file: %s\n",
|
|
strerror(errno)
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
void* blob =
|
|
mmap((void*) -1, blob_size, PROT_READ, MAP_PRIVATE, blob_fd, 0);
|
|
if (blob == (void*) -1) {
|
|
fprintf(stderr, "failed to mmap blob file: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
close(blob_fd);
|
|
|
|
// only write is needed, but mmap requires O_RDWR
|
|
int output_fd = open(output_file_name, O_RDWR | O_CREAT | O_TRUNC);
|
|
if (output_fd < 0) {
|
|
fprintf(
|
|
stderr,
|
|
"failed to open output file %s: %s\n%s",
|
|
output_file_name,
|
|
strerror(errno),
|
|
usage
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Pre-calculate file layout. It looks like this:
|
|
// * ELF header
|
|
// * Section headers:
|
|
// * .shstrtab
|
|
// * .strtab
|
|
// * .symtab
|
|
// * <blob-section>
|
|
// * .shstrtab data
|
|
// * .strtab data
|
|
// * .symtab data
|
|
// * <blob>_size
|
|
// * <blob>
|
|
//
|
|
|
|
char* shstrtab_fixed_data = "\0.shstrtab\0.strtab\0.symtab\0";
|
|
// offsets into .shstrtab
|
|
size_t shstrtab_shstrtab_off = 1;
|
|
size_t shstrtab_strtab_off = 11;
|
|
size_t shstrtab_symtab_off = 19;
|
|
size_t shstrtab_blob_off = 28;
|
|
size_t shstrtab_fixed_data_size = 28;
|
|
size_t shstrtab_data_size = shstrtab_fixed_data_size + section_name_size;
|
|
|
|
// offsets into .strtab
|
|
size_t strtab_name_off = 1;
|
|
size_t strtab_size_off = strtab_name_off + symbol_name_size;
|
|
size_t strtab_end_off = strtab_size_off + symbol_name_size + 5;
|
|
|
|
// three symbols: the zero symbol, the blob, and the blob size
|
|
size_t symtab_data_size = 3 * sizeof(Elf64_Sym);
|
|
|
|
// the absolute offsets into the file of each section as described above
|
|
Elf64_Off ehdr_off = 0;
|
|
Elf64_Off shtab_off = ehdr_off + sizeof(Elf64_Ehdr);
|
|
Elf64_Off shstrtab_hdr_off = shtab_off + sizeof(Elf64_Shdr);
|
|
Elf64_Off strtab_hdr_off = shstrtab_hdr_off + sizeof(Elf64_Shdr);
|
|
Elf64_Off symtab_hdr_off = strtab_hdr_off + sizeof(Elf64_Shdr);
|
|
Elf64_Off blob_hdr_off = symtab_hdr_off + sizeof(Elf64_Shdr);
|
|
Elf64_Off shstrtab_data_off = blob_hdr_off + sizeof(Elf64_Shdr);
|
|
Elf64_Off strtab_data_off = shstrtab_data_off + shstrtab_data_size;
|
|
Elf64_Off strtab_data_name_off = strtab_data_off + strtab_name_off;
|
|
Elf64_Off strtab_data_size_off = strtab_data_off + strtab_size_off;
|
|
Elf64_Off symtab_data_off = strtab_data_off + strtab_end_off;
|
|
Elf64_Off blob_size_off = symtab_data_off + symtab_data_size;
|
|
Elf64_Off blob_data_off = blob_size_off + sizeof(uint64_t);
|
|
Elf64_Off file_end_off = blob_data_off + blob_size;
|
|
|
|
if (ftruncate(output_fd, file_end_off) != 0) {
|
|
fprintf(
|
|
stderr,
|
|
"failed to truncate output file to size: %s\n",
|
|
strerror(errno)
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
void* data =
|
|
mmap((void*) -1, file_end_off, PROT_WRITE, MAP_SHARED, output_fd, 0);
|
|
if (data == (void*) -1) {
|
|
fprintf(
|
|
stderr,
|
|
"failed to mmap output file: %s\n",
|
|
strerror(errno)
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
close(output_fd);
|
|
|
|
*(Elf64_Ehdr*) (data + ehdr_off) = (Elf64_Ehdr) {
|
|
.e_ident = { 0x7F, 'E', 'L', 'F', ELFCLASS64, ELFDATA2LSB, EV_CURRENT },
|
|
.e_type = ET_REL,
|
|
.e_machine = EM_X86_64,
|
|
.e_version = EV_CURRENT,
|
|
.e_entry = 0,
|
|
.e_phoff = 0,
|
|
.e_shoff = shtab_off,
|
|
.e_flags = 0,
|
|
.e_ehsize = sizeof(Elf64_Ehdr),
|
|
.e_phentsize = 0,
|
|
.e_phnum = 0,
|
|
.e_shentsize = sizeof(Elf64_Shdr),
|
|
.e_shnum = 5,
|
|
.e_shstrndx = 1,
|
|
};
|
|
|
|
*(Elf64_Shdr*) (data + shstrtab_hdr_off) = (Elf64_Shdr) {
|
|
.sh_name = shstrtab_shstrtab_off,
|
|
.sh_type = SHT_STRTAB,
|
|
.sh_flags = 0,
|
|
.sh_addr = 0,
|
|
.sh_offset = shstrtab_data_off,
|
|
.sh_size = shstrtab_data_size,
|
|
.sh_link = 0,
|
|
.sh_info = 0,
|
|
.sh_addralign = 0,
|
|
.sh_entsize = 0,
|
|
};
|
|
|
|
*(Elf64_Shdr*) (data + strtab_hdr_off) = (Elf64_Shdr) {
|
|
.sh_name = shstrtab_strtab_off,
|
|
.sh_type = SHT_STRTAB,
|
|
.sh_flags = 0,
|
|
.sh_addr = 0,
|
|
.sh_offset = strtab_data_off,
|
|
.sh_size = strtab_end_off,
|
|
.sh_link = 0,
|
|
.sh_info = 0,
|
|
.sh_addralign = 0,
|
|
.sh_entsize = 0,
|
|
};
|
|
|
|
*(Elf64_Shdr*) (data + symtab_hdr_off) = (Elf64_Shdr) {
|
|
.sh_name = shstrtab_symtab_off,
|
|
.sh_type = SHT_SYMTAB,
|
|
.sh_flags = 0,
|
|
.sh_addr = 0,
|
|
.sh_offset = symtab_data_off,
|
|
.sh_size = symtab_data_size,
|
|
.sh_link = 2,
|
|
.sh_info = 1,
|
|
.sh_addralign = 0,
|
|
.sh_entsize = sizeof(Elf64_Sym),
|
|
};
|
|
|
|
*(Elf64_Shdr*) (data + blob_hdr_off) = (Elf64_Shdr) {
|
|
.sh_name = shstrtab_blob_off,
|
|
.sh_type = SHT_PROGBITS,
|
|
.sh_flags = SHF_ALLOC,
|
|
.sh_addr = 0,
|
|
.sh_offset = blob_size_off,
|
|
.sh_size = sizeof(uint64_t) + blob_size,
|
|
.sh_link = 0,
|
|
.sh_info = 0,
|
|
.sh_addralign = 0,
|
|
.sh_entsize = 0,
|
|
};
|
|
|
|
memcpy(
|
|
data + shstrtab_data_off,
|
|
shstrtab_fixed_data,
|
|
shstrtab_fixed_data_size
|
|
);
|
|
memcpy(
|
|
data + shstrtab_data_off + shstrtab_fixed_data_size,
|
|
section_name,
|
|
section_name_size
|
|
);
|
|
|
|
memcpy(data + strtab_data_name_off, symbol_name, symbol_name_size);
|
|
memcpy(
|
|
data + strtab_data_size_off,
|
|
symbol_name,
|
|
symbol_name_size - 1
|
|
);
|
|
memcpy(
|
|
data + strtab_data_size_off + symbol_name_size - 1,
|
|
"_size",
|
|
5
|
|
);
|
|
|
|
*(Elf64_Sym*) (data + symtab_data_off + sizeof(Elf64_Sym)) = (Elf64_Sym) {
|
|
.st_name = strtab_name_off,
|
|
.st_info = ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT),
|
|
.st_other = ELF64_ST_VISIBILITY(STV_DEFAULT),
|
|
.st_shndx = 4,
|
|
.st_value = sizeof(uint64_t),
|
|
.st_size = blob_size,
|
|
};
|
|
|
|
// I wanted to make this an absolute symbol,
|
|
// but it segfaulted for whatever reason, probably relocation-related.
|
|
*(Elf64_Sym*) (data + symtab_data_off + 2 * sizeof(Elf64_Sym)) = (Elf64_Sym) {
|
|
.st_name = strtab_size_off,
|
|
.st_info = ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT),
|
|
.st_other = ELF64_ST_VISIBILITY(STV_DEFAULT),
|
|
.st_shndx = 4,
|
|
.st_value = 0,
|
|
.st_size = sizeof(uint64_t),
|
|
};
|
|
|
|
*(uint64_t*) (data + blob_size_off) = blob_size;
|
|
memcpy(data + blob_data_off, blob, blob_size);
|
|
|
|
return 0;
|
|
}
|