image: Allow variable sized data directories

The PE/COFF spec allows variable-sized data directories, which reduce
the size of the optional header. While GNU ld always produces
maximum-sized headers, the kernel's EFI_STUB code generates a smaller
header size, which causes the image parsing code to abort.

This change allows variable-sized optional headers, but checks for at
least enough of an optional header to contain a CERT_TABLE data
directory entry.

We also rename struct image's aouthdr to opthdr, as it contains more
than just the a.out fields.

Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
This commit is contained in:
Jeremy Kerr 2012-09-28 09:56:48 +08:00
parent cf747fcca3
commit 274d4df0ff
2 changed files with 43 additions and 27 deletions

View file

@ -85,45 +85,47 @@ static uint16_t __pehdr_u16(char field[])
* - data_dir
* - checksum
*
* These functions require image->aouthdr to be set by the caller.
* These functions require image->opthdr to be set by the caller.
*/
static int image_pecoff_parse_32(struct image *image)
{
if (image->aouthdr.aout_32->standard.magic[0] != 0x0b ||
image->aouthdr.aout_32->standard.magic[1] != 0x01) {
if (image->opthdr.opt_32->standard.magic[0] != 0x0b ||
image->opthdr.opt_32->standard.magic[1] != 0x01) {
fprintf(stderr, "Invalid a.out machine type\n");
return -1;
}
image->aouthdr_size = sizeof(*image->aouthdr.aout_32);
image->opthdr_min_size = sizeof(*image->opthdr.opt_32) -
sizeof(image->opthdr.opt_32->DataDirectory);
image->file_alignment =
pehdr_u32(image->aouthdr.aout_32->FileAlignment);
pehdr_u32(image->opthdr.opt_32->FileAlignment);
image->header_size =
pehdr_u32(image->aouthdr.aout_32->SizeOfHeaders);
pehdr_u32(image->opthdr.opt_32->SizeOfHeaders);
image->data_dir = (void *)image->aouthdr.aout_32->DataDirectory;
image->checksum = (uint32_t *)image->aouthdr.aout_32->CheckSum;
image->data_dir = (void *)image->opthdr.opt_32->DataDirectory;
image->checksum = (uint32_t *)image->opthdr.opt_32->CheckSum;
return 0;
}
static int image_pecoff_parse_64(struct image *image)
{
if (image->aouthdr.aout_64->standard.magic[0] != 0x0b ||
image->aouthdr.aout_64->standard.magic[1] != 0x02) {
if (image->opthdr.opt_64->standard.magic[0] != 0x0b ||
image->opthdr.opt_64->standard.magic[1] != 0x02) {
fprintf(stderr, "Invalid a.out machine type\n");
return -1;
}
image->aouthdr_size = sizeof(*image->aouthdr.aout_64);
image->opthdr_min_size = sizeof(*image->opthdr.opt_64) -
sizeof(image->opthdr.opt_64->DataDirectory);
image->file_alignment =
pehdr_u32(image->aouthdr.aout_64->FileAlignment);
pehdr_u32(image->opthdr.opt_64->FileAlignment);
image->header_size =
pehdr_u32(image->aouthdr.aout_64->SizeOfHeaders);
pehdr_u32(image->opthdr.opt_64->SizeOfHeaders);
image->data_dir = (void *)image->aouthdr.aout_64->DataDirectory;
image->checksum = (uint32_t *)image->aouthdr.aout_64->CheckSum;
image->data_dir = (void *)image->opthdr.opt_64->DataDirectory;
image->checksum = (uint32_t *)image->opthdr.opt_64->CheckSum;
return 0;
}
@ -132,10 +134,10 @@ static int image_pecoff_parse(struct image *image)
struct cert_table_header *cert_table;
char nt_sig[] = {'P', 'E', 0, 0};
size_t size = image->size;
int rc, cert_table_offset;
void *buf = image->buf;
uint16_t magic;
uint32_t addr;
int rc;
/* sanity checks */
if (size < sizeof(*image->doshdr)) {
@ -170,7 +172,7 @@ static int image_pecoff_parse(struct image *image)
}
/* a.out header directly follows PE header */
image->aouthdr.addr = image->pehdr + 1;
image->opthdr.addr = image->pehdr + 1;
magic = pehdr_u16(image->pehdr->f_magic);
if (magic == IMAGE_FILE_MACHINE_AMD64) {
@ -189,16 +191,26 @@ static int image_pecoff_parse(struct image *image)
return -1;
}
/* we have the data_dir now, from parsing the a.out header */
image->data_dir_sigtable = &image->data_dir[DATA_DIR_CERT_TABLE];
/* the optional header has a variable size, as the data directory
* has a variable number of entries. Ensure that the we have enough
* space to include the security directory entry */
image->opthdr_size = pehdr_u16(image->pehdr->f_opthdr);
cert_table_offset = sizeof(*image->data_dir) *
(DATA_DIR_CERT_TABLE + 1);
if (pehdr_u16(image->pehdr->f_opthdr) != image->aouthdr_size) {
fprintf(stderr, "Invalid a.out header size\n");
if (image->opthdr_size < image->opthdr_min_size + cert_table_offset) {
fprintf(stderr, "PE opt header too small (%d bytes) to contain "
"a suitable data directory (need %d bytes)\n",
image->opthdr_size,
image->opthdr_min_size + cert_table_offset);
return -1;
}
image->data_dir_sigtable = &image->data_dir[DATA_DIR_CERT_TABLE];
if (image->size < sizeof(*image->doshdr) + sizeof(*image->pehdr)
+ image->aouthdr_size) {
+ image->opthdr_size) {
fprintf(stderr, "file is too small for a.out header\n");
return -1;
}
@ -222,7 +234,7 @@ static int image_pecoff_parse(struct image *image)
}
image->sections = pehdr_u16(image->pehdr->f_nscns);
image->scnhdr = image->aouthdr.addr + image->aouthdr_size;
image->scnhdr = image->opthdr.addr + image->opthdr_size;
return 0;
}

View file

@ -58,11 +58,15 @@ struct image {
struct external_PEI_DOS_hdr *doshdr;
struct external_PEI_IMAGE_hdr *pehdr;
union {
PEPAOUTHDR *aout_64;
PEAOUTHDR *aout_32;
PEPAOUTHDR *opt_64;
PEAOUTHDR *opt_32;
void *addr;
} aouthdr;
unsigned int aouthdr_size;
} opthdr;
/* size of a minimal opthdr for this machine, without data
* directories */
unsigned int opthdr_min_size;
/* size of the opthdr as specified by the image */
unsigned int opthdr_size;
struct data_dir_entry *data_dir;
struct data_dir_entry *data_dir_sigtable;
struct external_scnhdr *scnhdr;