mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 20:47:44 +00:00
8d3525293e
This updates our in-tree copy of libvpx to the v1.3.0 git tag (2e88f2f2ec777259bda1714e72f1ecd2519bceb5) libvpx 1.3.0 adds support for VP9. VP9 support is built but not yet exposed with this commit. Our update.sh script is replaced with update.py that can update the build system to a given git commit. - checkout out upstream git - create platform dependend config files - add/remove changed libvpx files - update moz.build - warn about new build categories in libvpx
828 lines
25 KiB
C
828 lines
25 KiB
C
/*
|
|
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "vpx_config.h"
|
|
#include "vpx/vpx_integer.h"
|
|
|
|
typedef enum {
|
|
OUTPUT_FMT_PLAIN,
|
|
OUTPUT_FMT_RVDS,
|
|
OUTPUT_FMT_GAS,
|
|
} output_fmt_t;
|
|
|
|
int log_msg(const char *fmt, ...) {
|
|
int res;
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
res = vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
return res;
|
|
}
|
|
|
|
#if defined(__GNUC__) && __GNUC__
|
|
#if defined(__MACH__)
|
|
|
|
#include <mach-o/loader.h>
|
|
#include <mach-o/nlist.h>
|
|
|
|
int print_macho_equ(output_fmt_t mode, uint8_t* name, int val) {
|
|
switch (mode) {
|
|
case OUTPUT_FMT_RVDS:
|
|
printf("%-40s EQU %5d\n", name, val);
|
|
return 0;
|
|
case OUTPUT_FMT_GAS:
|
|
printf(".set %-40s, %5d\n", name, val);
|
|
return 0;
|
|
default:
|
|
log_msg("Unsupported mode: %d", mode);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int parse_macho(uint8_t *base_buf, size_t sz, output_fmt_t mode) {
|
|
int i, j;
|
|
struct mach_header header;
|
|
uint8_t *buf = base_buf;
|
|
int base_data_section = 0;
|
|
int bits = 0;
|
|
|
|
/* We can read in mach_header for 32 and 64 bit architectures
|
|
* because it's identical to mach_header_64 except for the last
|
|
* element (uint32_t reserved), which we don't use. Then, when
|
|
* we know which architecture we're looking at, increment buf
|
|
* appropriately.
|
|
*/
|
|
memcpy(&header, buf, sizeof(struct mach_header));
|
|
|
|
if (header.magic == MH_MAGIC) {
|
|
if (header.cputype == CPU_TYPE_ARM
|
|
|| header.cputype == CPU_TYPE_X86) {
|
|
bits = 32;
|
|
buf += sizeof(struct mach_header);
|
|
} else {
|
|
log_msg("Bad cputype for object file. Currently only tested for CPU_TYPE_[ARM|X86].\n");
|
|
goto bail;
|
|
}
|
|
} else if (header.magic == MH_MAGIC_64) {
|
|
if (header.cputype == CPU_TYPE_X86_64) {
|
|
bits = 64;
|
|
buf += sizeof(struct mach_header_64);
|
|
} else {
|
|
log_msg("Bad cputype for object file. Currently only tested for CPU_TYPE_X86_64.\n");
|
|
goto bail;
|
|
}
|
|
} else {
|
|
log_msg("Bad magic number for object file. 0x%x or 0x%x expected, 0x%x found.\n",
|
|
MH_MAGIC, MH_MAGIC_64, header.magic);
|
|
goto bail;
|
|
}
|
|
|
|
if (header.filetype != MH_OBJECT) {
|
|
log_msg("Bad filetype for object file. Currently only tested for MH_OBJECT.\n");
|
|
goto bail;
|
|
}
|
|
|
|
for (i = 0; i < header.ncmds; i++) {
|
|
struct load_command lc;
|
|
|
|
memcpy(&lc, buf, sizeof(struct load_command));
|
|
|
|
if (lc.cmd == LC_SEGMENT) {
|
|
uint8_t *seg_buf = buf;
|
|
struct section s;
|
|
struct segment_command seg_c;
|
|
|
|
memcpy(&seg_c, seg_buf, sizeof(struct segment_command));
|
|
seg_buf += sizeof(struct segment_command);
|
|
|
|
/* Although each section is given it's own offset, nlist.n_value
|
|
* references the offset of the first section. This isn't
|
|
* apparent without debug information because the offset of the
|
|
* data section is the same as the first section. However, with
|
|
* debug sections mixed in, the offset of the debug section
|
|
* increases but n_value still references the first section.
|
|
*/
|
|
if (seg_c.nsects < 1) {
|
|
log_msg("Not enough sections\n");
|
|
goto bail;
|
|
}
|
|
|
|
memcpy(&s, seg_buf, sizeof(struct section));
|
|
base_data_section = s.offset;
|
|
} else if (lc.cmd == LC_SEGMENT_64) {
|
|
uint8_t *seg_buf = buf;
|
|
struct section_64 s;
|
|
struct segment_command_64 seg_c;
|
|
|
|
memcpy(&seg_c, seg_buf, sizeof(struct segment_command_64));
|
|
seg_buf += sizeof(struct segment_command_64);
|
|
|
|
/* Explanation in LG_SEGMENT */
|
|
if (seg_c.nsects < 1) {
|
|
log_msg("Not enough sections\n");
|
|
goto bail;
|
|
}
|
|
|
|
memcpy(&s, seg_buf, sizeof(struct section_64));
|
|
base_data_section = s.offset;
|
|
} else if (lc.cmd == LC_SYMTAB) {
|
|
if (base_data_section != 0) {
|
|
struct symtab_command sc;
|
|
uint8_t *sym_buf = base_buf;
|
|
uint8_t *str_buf = base_buf;
|
|
|
|
memcpy(&sc, buf, sizeof(struct symtab_command));
|
|
|
|
if (sc.cmdsize != sizeof(struct symtab_command)) {
|
|
log_msg("Can't find symbol table!\n");
|
|
goto bail;
|
|
}
|
|
|
|
sym_buf += sc.symoff;
|
|
str_buf += sc.stroff;
|
|
|
|
for (j = 0; j < sc.nsyms; j++) {
|
|
/* Location of string is cacluated each time from the
|
|
* start of the string buffer. On darwin the symbols
|
|
* are prefixed by "_", so we bump the pointer by 1.
|
|
* The target value is defined as an int in *_asm_*_offsets.c,
|
|
* which is 4 bytes on all targets we currently use.
|
|
*/
|
|
if (bits == 32) {
|
|
struct nlist nl;
|
|
int val;
|
|
|
|
memcpy(&nl, sym_buf, sizeof(struct nlist));
|
|
sym_buf += sizeof(struct nlist);
|
|
|
|
memcpy(&val, base_buf + base_data_section + nl.n_value,
|
|
sizeof(val));
|
|
print_macho_equ(mode, str_buf + nl.n_un.n_strx + 1, val);
|
|
} else { /* if (bits == 64) */
|
|
struct nlist_64 nl;
|
|
int val;
|
|
|
|
memcpy(&nl, sym_buf, sizeof(struct nlist_64));
|
|
sym_buf += sizeof(struct nlist_64);
|
|
|
|
memcpy(&val, base_buf + base_data_section + nl.n_value,
|
|
sizeof(val));
|
|
print_macho_equ(mode, str_buf + nl.n_un.n_strx + 1, val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
buf += lc.cmdsize;
|
|
}
|
|
|
|
return 0;
|
|
bail:
|
|
return 1;
|
|
|
|
}
|
|
|
|
#elif defined(__ELF__)
|
|
#include "elf.h"
|
|
|
|
#define COPY_STRUCT(dst, buf, ofst, sz) do {\
|
|
if(ofst + sizeof((*(dst))) > sz) goto bail;\
|
|
memcpy(dst, buf+ofst, sizeof((*(dst))));\
|
|
} while(0)
|
|
|
|
#define ENDIAN_ASSIGN(val, memb) do {\
|
|
if(!elf->le_data) {log_msg("Big Endian data not supported yet!\n");goto bail;}\
|
|
(val) = (memb);\
|
|
} while(0)
|
|
|
|
#define ENDIAN_ASSIGN_IN_PLACE(memb) do {\
|
|
ENDIAN_ASSIGN(memb, memb);\
|
|
} while(0)
|
|
|
|
typedef struct {
|
|
uint8_t *buf; /* Buffer containing ELF data */
|
|
size_t sz; /* Buffer size */
|
|
int le_data; /* Data is little-endian */
|
|
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
|
|
int bits; /* 32 or 64 */
|
|
Elf32_Ehdr hdr32;
|
|
Elf64_Ehdr hdr64;
|
|
} elf_obj_t;
|
|
|
|
int parse_elf_header(elf_obj_t *elf) {
|
|
int res;
|
|
/* Verify ELF Magic numbers */
|
|
COPY_STRUCT(&elf->e_ident, elf->buf, 0, elf->sz);
|
|
res = elf->e_ident[EI_MAG0] == ELFMAG0;
|
|
res &= elf->e_ident[EI_MAG1] == ELFMAG1;
|
|
res &= elf->e_ident[EI_MAG2] == ELFMAG2;
|
|
res &= elf->e_ident[EI_MAG3] == ELFMAG3;
|
|
res &= elf->e_ident[EI_CLASS] == ELFCLASS32
|
|
|| elf->e_ident[EI_CLASS] == ELFCLASS64;
|
|
res &= elf->e_ident[EI_DATA] == ELFDATA2LSB;
|
|
|
|
if (!res) goto bail;
|
|
|
|
elf->le_data = elf->e_ident[EI_DATA] == ELFDATA2LSB;
|
|
|
|
/* Read in relevant values */
|
|
if (elf->e_ident[EI_CLASS] == ELFCLASS32) {
|
|
elf->bits = 32;
|
|
COPY_STRUCT(&elf->hdr32, elf->buf, 0, elf->sz);
|
|
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_type);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_machine);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_version);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_entry);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_phoff);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_shoff);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_flags);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_ehsize);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_phentsize);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_phnum);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_shentsize);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_shnum);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr32.e_shstrndx);
|
|
} else { /* if (elf->e_ident[EI_CLASS] == ELFCLASS64) */
|
|
elf->bits = 64;
|
|
COPY_STRUCT(&elf->hdr64, elf->buf, 0, elf->sz);
|
|
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_type);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_machine);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_version);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_entry);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_phoff);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_shoff);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_flags);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_ehsize);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_phentsize);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_phnum);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_shentsize);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_shnum);
|
|
ENDIAN_ASSIGN_IN_PLACE(elf->hdr64.e_shstrndx);
|
|
}
|
|
|
|
return 0;
|
|
bail:
|
|
log_msg("Failed to parse ELF file header");
|
|
return 1;
|
|
}
|
|
|
|
int parse_elf_section(elf_obj_t *elf, int idx, Elf32_Shdr *hdr32, Elf64_Shdr *hdr64) {
|
|
if (hdr32) {
|
|
if (idx >= elf->hdr32.e_shnum)
|
|
goto bail;
|
|
|
|
COPY_STRUCT(hdr32, elf->buf, elf->hdr32.e_shoff + idx * elf->hdr32.e_shentsize,
|
|
elf->sz);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_name);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_type);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_flags);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_addr);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_offset);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_size);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_link);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_info);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_addralign);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr32->sh_entsize);
|
|
} else { /* if (hdr64) */
|
|
if (idx >= elf->hdr64.e_shnum)
|
|
goto bail;
|
|
|
|
COPY_STRUCT(hdr64, elf->buf, elf->hdr64.e_shoff + idx * elf->hdr64.e_shentsize,
|
|
elf->sz);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_name);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_type);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_flags);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_addr);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_offset);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_size);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_link);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_info);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_addralign);
|
|
ENDIAN_ASSIGN_IN_PLACE(hdr64->sh_entsize);
|
|
}
|
|
|
|
return 0;
|
|
bail:
|
|
return 1;
|
|
}
|
|
|
|
char *parse_elf_string_table(elf_obj_t *elf, int s_idx, int idx) {
|
|
if (elf->bits == 32) {
|
|
Elf32_Shdr shdr;
|
|
|
|
if (parse_elf_section(elf, s_idx, &shdr, NULL)) {
|
|
log_msg("Failed to parse ELF string table: section %d, index %d\n",
|
|
s_idx, idx);
|
|
return "";
|
|
}
|
|
|
|
return (char *)(elf->buf + shdr.sh_offset + idx);
|
|
} else { /* if (elf->bits == 64) */
|
|
Elf64_Shdr shdr;
|
|
|
|
if (parse_elf_section(elf, s_idx, NULL, &shdr)) {
|
|
log_msg("Failed to parse ELF string table: section %d, index %d\n",
|
|
s_idx, idx);
|
|
return "";
|
|
}
|
|
|
|
return (char *)(elf->buf + shdr.sh_offset + idx);
|
|
}
|
|
}
|
|
|
|
int parse_elf_symbol(elf_obj_t *elf, unsigned int ofst, Elf32_Sym *sym32, Elf64_Sym *sym64) {
|
|
if (sym32) {
|
|
COPY_STRUCT(sym32, elf->buf, ofst, elf->sz);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym32->st_name);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym32->st_value);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym32->st_size);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym32->st_info);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym32->st_other);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym32->st_shndx);
|
|
} else { /* if (sym64) */
|
|
COPY_STRUCT(sym64, elf->buf, ofst, elf->sz);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym64->st_name);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym64->st_value);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym64->st_size);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym64->st_info);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym64->st_other);
|
|
ENDIAN_ASSIGN_IN_PLACE(sym64->st_shndx);
|
|
}
|
|
return 0;
|
|
bail:
|
|
return 1;
|
|
}
|
|
|
|
int parse_elf(uint8_t *buf, size_t sz, output_fmt_t mode) {
|
|
elf_obj_t elf;
|
|
unsigned int ofst;
|
|
int i;
|
|
Elf32_Off strtab_off32;
|
|
Elf64_Off strtab_off64; /* save String Table offset for later use */
|
|
|
|
memset(&elf, 0, sizeof(elf));
|
|
elf.buf = buf;
|
|
elf.sz = sz;
|
|
|
|
/* Parse Header */
|
|
if (parse_elf_header(&elf))
|
|
goto bail;
|
|
|
|
if (elf.bits == 32) {
|
|
Elf32_Shdr shdr;
|
|
for (i = 0; i < elf.hdr32.e_shnum; i++) {
|
|
parse_elf_section(&elf, i, &shdr, NULL);
|
|
|
|
if (shdr.sh_type == SHT_STRTAB) {
|
|
char strtsb_name[128];
|
|
|
|
strcpy(strtsb_name, (char *)(elf.buf + shdr.sh_offset + shdr.sh_name));
|
|
|
|
if (!(strcmp(strtsb_name, ".shstrtab"))) {
|
|
/* log_msg("found section: %s\n", strtsb_name); */
|
|
strtab_off32 = shdr.sh_offset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else { /* if (elf.bits == 64) */
|
|
Elf64_Shdr shdr;
|
|
for (i = 0; i < elf.hdr64.e_shnum; i++) {
|
|
parse_elf_section(&elf, i, NULL, &shdr);
|
|
|
|
if (shdr.sh_type == SHT_STRTAB) {
|
|
char strtsb_name[128];
|
|
|
|
strcpy(strtsb_name, (char *)(elf.buf + shdr.sh_offset + shdr.sh_name));
|
|
|
|
if (!(strcmp(strtsb_name, ".shstrtab"))) {
|
|
/* log_msg("found section: %s\n", strtsb_name); */
|
|
strtab_off64 = shdr.sh_offset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Parse all Symbol Tables */
|
|
if (elf.bits == 32) {
|
|
Elf32_Shdr shdr;
|
|
for (i = 0; i < elf.hdr32.e_shnum; i++) {
|
|
parse_elf_section(&elf, i, &shdr, NULL);
|
|
|
|
if (shdr.sh_type == SHT_SYMTAB) {
|
|
for (ofst = shdr.sh_offset;
|
|
ofst < shdr.sh_offset + shdr.sh_size;
|
|
ofst += shdr.sh_entsize) {
|
|
Elf32_Sym sym;
|
|
|
|
parse_elf_symbol(&elf, ofst, &sym, NULL);
|
|
|
|
/* For all OBJECTS (data objects), extract the value from the
|
|
* proper data segment.
|
|
*/
|
|
/* if (ELF32_ST_TYPE(sym.st_info) == STT_OBJECT && sym.st_name)
|
|
log_msg("found data object %s\n",
|
|
parse_elf_string_table(&elf,
|
|
shdr.sh_link,
|
|
sym.st_name));
|
|
*/
|
|
|
|
if (ELF32_ST_TYPE(sym.st_info) == STT_OBJECT
|
|
&& sym.st_size == 4) {
|
|
Elf32_Shdr dhdr;
|
|
int val = 0;
|
|
char section_name[128];
|
|
|
|
parse_elf_section(&elf, sym.st_shndx, &dhdr, NULL);
|
|
|
|
/* For explanition - refer to _MSC_VER version of code */
|
|
strcpy(section_name, (char *)(elf.buf + strtab_off32 + dhdr.sh_name));
|
|
/* log_msg("Section_name: %s, Section_type: %d\n", section_name, dhdr.sh_type); */
|
|
|
|
if (strcmp(section_name, ".bss")) {
|
|
if (sizeof(val) != sym.st_size) {
|
|
/* The target value is declared as an int in
|
|
* *_asm_*_offsets.c, which is 4 bytes on all
|
|
* targets we currently use. Complain loudly if
|
|
* this is not true.
|
|
*/
|
|
log_msg("Symbol size is wrong\n");
|
|
goto bail;
|
|
}
|
|
|
|
memcpy(&val,
|
|
elf.buf + dhdr.sh_offset + sym.st_value,
|
|
sym.st_size);
|
|
}
|
|
|
|
if (!elf.le_data) {
|
|
log_msg("Big Endian data not supported yet!\n");
|
|
goto bail;
|
|
}
|
|
|
|
switch (mode) {
|
|
case OUTPUT_FMT_RVDS:
|
|
printf("%-40s EQU %5d\n",
|
|
parse_elf_string_table(&elf,
|
|
shdr.sh_link,
|
|
sym.st_name),
|
|
val);
|
|
break;
|
|
case OUTPUT_FMT_GAS:
|
|
printf(".equ %-40s, %5d\n",
|
|
parse_elf_string_table(&elf,
|
|
shdr.sh_link,
|
|
sym.st_name),
|
|
val);
|
|
break;
|
|
default:
|
|
printf("%s = %d\n",
|
|
parse_elf_string_table(&elf,
|
|
shdr.sh_link,
|
|
sym.st_name),
|
|
val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else { /* if (elf.bits == 64) */
|
|
Elf64_Shdr shdr;
|
|
for (i = 0; i < elf.hdr64.e_shnum; i++) {
|
|
parse_elf_section(&elf, i, NULL, &shdr);
|
|
|
|
if (shdr.sh_type == SHT_SYMTAB) {
|
|
for (ofst = shdr.sh_offset;
|
|
ofst < shdr.sh_offset + shdr.sh_size;
|
|
ofst += shdr.sh_entsize) {
|
|
Elf64_Sym sym;
|
|
|
|
parse_elf_symbol(&elf, ofst, NULL, &sym);
|
|
|
|
/* For all OBJECTS (data objects), extract the value from the
|
|
* proper data segment.
|
|
*/
|
|
/* if (ELF64_ST_TYPE(sym.st_info) == STT_OBJECT && sym.st_name)
|
|
log_msg("found data object %s\n",
|
|
parse_elf_string_table(&elf,
|
|
shdr.sh_link,
|
|
sym.st_name));
|
|
*/
|
|
|
|
if (ELF64_ST_TYPE(sym.st_info) == STT_OBJECT
|
|
&& sym.st_size == 4) {
|
|
Elf64_Shdr dhdr;
|
|
int val = 0;
|
|
char section_name[128];
|
|
|
|
parse_elf_section(&elf, sym.st_shndx, NULL, &dhdr);
|
|
|
|
/* For explanition - refer to _MSC_VER version of code */
|
|
strcpy(section_name, (char *)(elf.buf + strtab_off64 + dhdr.sh_name));
|
|
/* log_msg("Section_name: %s, Section_type: %d\n", section_name, dhdr.sh_type); */
|
|
|
|
if ((strcmp(section_name, ".bss"))) {
|
|
if (sizeof(val) != sym.st_size) {
|
|
/* The target value is declared as an int in
|
|
* *_asm_*_offsets.c, which is 4 bytes on all
|
|
* targets we currently use. Complain loudly if
|
|
* this is not true.
|
|
*/
|
|
log_msg("Symbol size is wrong\n");
|
|
goto bail;
|
|
}
|
|
|
|
memcpy(&val,
|
|
elf.buf + dhdr.sh_offset + sym.st_value,
|
|
sym.st_size);
|
|
}
|
|
|
|
if (!elf.le_data) {
|
|
log_msg("Big Endian data not supported yet!\n");
|
|
goto bail;
|
|
}
|
|
|
|
switch (mode) {
|
|
case OUTPUT_FMT_RVDS:
|
|
printf("%-40s EQU %5d\n",
|
|
parse_elf_string_table(&elf,
|
|
shdr.sh_link,
|
|
sym.st_name),
|
|
val);
|
|
break;
|
|
case OUTPUT_FMT_GAS:
|
|
printf(".equ %-40s, %5d\n",
|
|
parse_elf_string_table(&elf,
|
|
shdr.sh_link,
|
|
sym.st_name),
|
|
val);
|
|
break;
|
|
default:
|
|
printf("%s = %d\n",
|
|
parse_elf_string_table(&elf,
|
|
shdr.sh_link,
|
|
sym.st_name),
|
|
val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode == OUTPUT_FMT_RVDS)
|
|
printf(" END\n");
|
|
|
|
return 0;
|
|
bail:
|
|
log_msg("Parse error: File does not appear to be valid ELF32 or ELF64\n");
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
#endif /* defined(__GNUC__) && __GNUC__ */
|
|
|
|
|
|
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__)
|
|
/* See "Microsoft Portable Executable and Common Object File Format Specification"
|
|
for reference.
|
|
*/
|
|
#define get_le32(x) ((*(x)) | (*(x+1)) << 8 |(*(x+2)) << 16 | (*(x+3)) << 24 )
|
|
#define get_le16(x) ((*(x)) | (*(x+1)) << 8)
|
|
|
|
int parse_coff(uint8_t *buf, size_t sz) {
|
|
unsigned int nsections, symtab_ptr, symtab_sz, strtab_ptr;
|
|
unsigned int sectionrawdata_ptr;
|
|
unsigned int i;
|
|
uint8_t *ptr;
|
|
uint32_t symoffset;
|
|
|
|
char **sectionlist; // this array holds all section names in their correct order.
|
|
// it is used to check if the symbol is in .bss or .rdata section.
|
|
|
|
nsections = get_le16(buf + 2);
|
|
symtab_ptr = get_le32(buf + 8);
|
|
symtab_sz = get_le32(buf + 12);
|
|
strtab_ptr = symtab_ptr + symtab_sz * 18;
|
|
|
|
if (nsections > 96) {
|
|
log_msg("Too many sections\n");
|
|
return 1;
|
|
}
|
|
|
|
sectionlist = malloc(nsections * sizeof(sectionlist));
|
|
|
|
if (sectionlist == NULL) {
|
|
log_msg("Allocating first level of section list failed\n");
|
|
return 1;
|
|
}
|
|
|
|
// log_msg("COFF: Found %u symbols in %u sections.\n", symtab_sz, nsections);
|
|
|
|
/*
|
|
The size of optional header is always zero for an obj file. So, the section header
|
|
follows the file header immediately.
|
|
*/
|
|
|
|
ptr = buf + 20; // section header
|
|
|
|
for (i = 0; i < nsections; i++) {
|
|
char sectionname[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
strncpy(sectionname, ptr, 8);
|
|
// log_msg("COFF: Parsing section %s\n",sectionname);
|
|
|
|
sectionlist[i] = malloc(strlen(sectionname) + 1);
|
|
|
|
if (sectionlist[i] == NULL) {
|
|
log_msg("Allocating storage for %s failed\n", sectionname);
|
|
goto bail;
|
|
}
|
|
strcpy(sectionlist[i], sectionname);
|
|
|
|
if (!strcmp(sectionname, ".rdata")) sectionrawdata_ptr = get_le32(ptr + 20);
|
|
|
|
ptr += 40;
|
|
}
|
|
|
|
// log_msg("COFF: Symbol table at offset %u\n", symtab_ptr);
|
|
// log_msg("COFF: raw data pointer ofset for section .rdata is %u\n", sectionrawdata_ptr);
|
|
|
|
/* The compiler puts the data with non-zero offset in .rdata section, but puts the data with
|
|
zero offset in .bss section. So, if the data in in .bss section, set offset=0.
|
|
Note from Wiki: In an object module compiled from C, the bss section contains
|
|
the local variables (but not functions) that were declared with the static keyword,
|
|
except for those with non-zero initial values. (In C, static variables are initialized
|
|
to zero by default.) It also contains the non-local (both extern and static) variables
|
|
that are also initialized to zero (either explicitly or by default).
|
|
*/
|
|
// move to symbol table
|
|
/* COFF symbol table:
|
|
offset field
|
|
0 Name(*)
|
|
8 Value
|
|
12 SectionNumber
|
|
14 Type
|
|
16 StorageClass
|
|
17 NumberOfAuxSymbols
|
|
*/
|
|
ptr = buf + symtab_ptr;
|
|
|
|
for (i = 0; i < symtab_sz; i++) {
|
|
int16_t section = get_le16(ptr + 12); // section number
|
|
|
|
if (section > 0 && ptr[16] == 2) {
|
|
// if(section > 0 && ptr[16] == 3 && get_le32(ptr+8)) {
|
|
|
|
if (get_le32(ptr)) {
|
|
char name[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
strncpy(name, ptr, 8);
|
|
// log_msg("COFF: Parsing symbol %s\n",name);
|
|
/* The 64bit Windows compiler doesn't prefix with an _.
|
|
* Check what's there, and bump if necessary
|
|
*/
|
|
if (name[0] == '_')
|
|
printf("%-40s EQU ", name + 1);
|
|
else
|
|
printf("%-40s EQU ", name);
|
|
} else {
|
|
// log_msg("COFF: Parsing symbol %s\n",
|
|
// buf + strtab_ptr + get_le32(ptr+4));
|
|
if ((buf + strtab_ptr + get_le32(ptr + 4))[0] == '_')
|
|
printf("%-40s EQU ",
|
|
buf + strtab_ptr + get_le32(ptr + 4) + 1);
|
|
else
|
|
printf("%-40s EQU ", buf + strtab_ptr + get_le32(ptr + 4));
|
|
}
|
|
|
|
if (!(strcmp(sectionlist[section - 1], ".bss"))) {
|
|
symoffset = 0;
|
|
} else {
|
|
symoffset = get_le32(buf + sectionrawdata_ptr + get_le32(ptr + 8));
|
|
}
|
|
|
|
// log_msg(" Section: %d\n",section);
|
|
// log_msg(" Class: %d\n",ptr[16]);
|
|
// log_msg(" Address: %u\n",get_le32(ptr+8));
|
|
// log_msg(" Offset: %u\n", symoffset);
|
|
|
|
printf("%5d\n", symoffset);
|
|
}
|
|
|
|
ptr += 18;
|
|
}
|
|
|
|
printf(" END\n");
|
|
|
|
for (i = 0; i < nsections; i++) {
|
|
free(sectionlist[i]);
|
|
}
|
|
|
|
free(sectionlist);
|
|
|
|
return 0;
|
|
bail:
|
|
|
|
for (i = 0; i < nsections; i++) {
|
|
free(sectionlist[i]);
|
|
}
|
|
|
|
free(sectionlist);
|
|
|
|
return 1;
|
|
}
|
|
#endif /* defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__) */
|
|
|
|
int main(int argc, char **argv) {
|
|
output_fmt_t mode = OUTPUT_FMT_PLAIN;
|
|
const char *f;
|
|
uint8_t *file_buf;
|
|
int res;
|
|
FILE *fp;
|
|
long int file_size;
|
|
|
|
if (argc < 2 || argc > 3) {
|
|
fprintf(stderr, "Usage: %s [output format] <obj file>\n\n", argv[0]);
|
|
fprintf(stderr, " <obj file>\tobject file to parse\n");
|
|
fprintf(stderr, "Output Formats:\n");
|
|
fprintf(stderr, " gas - compatible with GNU assembler\n");
|
|
fprintf(stderr, " rvds - compatible with armasm\n");
|
|
goto bail;
|
|
}
|
|
|
|
f = argv[2];
|
|
|
|
if (!strcmp(argv[1], "rvds"))
|
|
mode = OUTPUT_FMT_RVDS;
|
|
else if (!strcmp(argv[1], "gas"))
|
|
mode = OUTPUT_FMT_GAS;
|
|
else
|
|
f = argv[1];
|
|
|
|
fp = fopen(f, "rb");
|
|
|
|
if (!fp) {
|
|
perror("Unable to open file");
|
|
goto bail;
|
|
}
|
|
|
|
if (fseek(fp, 0, SEEK_END)) {
|
|
perror("stat");
|
|
goto bail;
|
|
}
|
|
|
|
file_size = ftell(fp);
|
|
file_buf = malloc(file_size);
|
|
|
|
if (!file_buf) {
|
|
perror("malloc");
|
|
goto bail;
|
|
}
|
|
|
|
rewind(fp);
|
|
|
|
if (fread(file_buf, sizeof(char), file_size, fp) != file_size) {
|
|
perror("read");
|
|
goto bail;
|
|
}
|
|
|
|
if (fclose(fp)) {
|
|
perror("close");
|
|
goto bail;
|
|
}
|
|
|
|
#if defined(__GNUC__) && __GNUC__
|
|
#if defined(__MACH__)
|
|
res = parse_macho(file_buf, file_size, mode);
|
|
#elif defined(__ELF__)
|
|
res = parse_elf(file_buf, file_size, mode);
|
|
#endif
|
|
#endif
|
|
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__)
|
|
res = parse_coff(file_buf, file_size);
|
|
#endif
|
|
|
|
free(file_buf);
|
|
|
|
if (!res)
|
|
return EXIT_SUCCESS;
|
|
|
|
bail:
|
|
return EXIT_FAILURE;
|
|
}
|