Path: blob/main/contrib/elftoolchain/libpe/libpe_coff.c
39478 views
/*-1* Copyright (c) 2015 Kai Wang2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <assert.h>28#include <errno.h>29#include <stdlib.h>30#include <string.h>31#include <time.h>32#include <unistd.h>3334#include "_libpe.h"3536ELFTC_VCSID("$Id: libpe_coff.c 3326 2016-01-16 17:46:17Z kaiwang27 $");3738int39libpe_parse_coff_header(PE *pe, char *hdr)40{41char tmp[128];42PE_CoffHdr *ch;43PE_OptHdr *oh;44PE_DataDir *dd;45unsigned p, r, s;46int i;4748if ((ch = malloc(sizeof(PE_CoffHdr))) == NULL) {49errno = ENOMEM;50return (-1);51}5253PE_READ16(hdr, ch->ch_machine);54PE_READ16(hdr, ch->ch_nsec);55PE_READ32(hdr, ch->ch_timestamp);56PE_READ32(hdr, ch->ch_symptr);57PE_READ32(hdr, ch->ch_nsym);58PE_READ16(hdr, ch->ch_optsize);59PE_READ16(hdr, ch->ch_char);6061pe->pe_ch = ch;6263/*64* The Optional header is omitted for object files.65*/66if (ch->ch_optsize == 0)67return (libpe_parse_section_headers(pe));6869if ((oh = calloc(1, sizeof(PE_OptHdr))) == NULL) {70errno = ENOMEM;71return (-1);72}73pe->pe_oh = oh;7475#define READ_OPT(n) \76do { \77/* \78* Since the Optional Header size is variable, we must \79* check if the requested read size will overrun the \80* remaining header bytes. \81*/ \82if (p + (n) > ch->ch_optsize) { \83/* Consume the "extra" bytes */ \84r = ch->ch_optsize - p; \85if (read(pe->pe_fd, tmp, r) != (ssize_t) r) { \86pe->pe_flags |= LIBPE_F_BAD_SEC_HEADER;\87return (0); \88} \89return (libpe_parse_section_headers(pe)); \90} \91if (read(pe->pe_fd, tmp, (n)) != (ssize_t) (n)) { \92pe->pe_flags |= LIBPE_F_BAD_OPT_HEADER; \93return (0); \94} \95p += (n); \96} while (0)97#define READ_OPT8(v) do { READ_OPT(1); (v) = *tmp; } while(0)98#define READ_OPT16(v) do { READ_OPT(2); (v) = le16dec(tmp); } while(0)99#define READ_OPT32(v) do { READ_OPT(4); (v) = le32dec(tmp); } while(0)100#define READ_OPT64(v) do { READ_OPT(8); (v) = le64dec(tmp); } while(0)101102/*103* Read in the Optional header. Size of some fields are depending104* on the PE format specified by the oh_magic field. (PE32 or PE32+)105*/106107p = 0;108READ_OPT16(oh->oh_magic);109if (oh->oh_magic == PE_FORMAT_32P)110pe->pe_obj = PE_O_PE32P;111READ_OPT8(oh->oh_ldvermajor);112READ_OPT8(oh->oh_ldverminor);113READ_OPT32(oh->oh_textsize);114READ_OPT32(oh->oh_datasize);115READ_OPT32(oh->oh_bsssize);116READ_OPT32(oh->oh_entry);117READ_OPT32(oh->oh_textbase);118if (oh->oh_magic != PE_FORMAT_32P) {119READ_OPT32(oh->oh_database);120READ_OPT32(oh->oh_imgbase);121} else122READ_OPT64(oh->oh_imgbase);123READ_OPT32(oh->oh_secalign);124READ_OPT32(oh->oh_filealign);125READ_OPT16(oh->oh_osvermajor);126READ_OPT16(oh->oh_osverminor);127READ_OPT16(oh->oh_imgvermajor);128READ_OPT16(oh->oh_imgverminor);129READ_OPT16(oh->oh_subvermajor);130READ_OPT16(oh->oh_subverminor);131READ_OPT32(oh->oh_win32ver);132READ_OPT32(oh->oh_imgsize);133READ_OPT32(oh->oh_hdrsize);134READ_OPT32(oh->oh_checksum);135READ_OPT16(oh->oh_subsystem);136READ_OPT16(oh->oh_dllchar);137if (oh->oh_magic != PE_FORMAT_32P) {138READ_OPT32(oh->oh_stacksizer);139READ_OPT32(oh->oh_stacksizec);140READ_OPT32(oh->oh_heapsizer);141READ_OPT32(oh->oh_heapsizec);142} else {143READ_OPT64(oh->oh_stacksizer);144READ_OPT64(oh->oh_stacksizec);145READ_OPT64(oh->oh_heapsizer);146READ_OPT64(oh->oh_heapsizec);147}148READ_OPT32(oh->oh_ldrflags);149READ_OPT32(oh->oh_ndatadir);150151/*152* Read in the Data Directories.153*/154155if (oh->oh_ndatadir > 0) {156if ((dd = calloc(1, sizeof(PE_DataDir))) == NULL) {157errno = ENOMEM;158return (-1);159}160pe->pe_dd = dd;161162dd->dd_total = oh->oh_ndatadir < PE_DD_MAX ? oh->oh_ndatadir :163PE_DD_MAX;164165for (i = 0; (uint32_t) i < dd->dd_total; i++) {166READ_OPT32(dd->dd_e[i].de_addr);167READ_OPT32(dd->dd_e[i].de_size);168}169}170171/* Consume the remaining bytes in the Optional header, if any. */172if (ch->ch_optsize > p) {173r = ch->ch_optsize - p;174for (; r > 0; r -= s) {175s = r > sizeof(tmp) ? sizeof(tmp) : r;176if (read(pe->pe_fd, tmp, s) != (ssize_t) s) {177pe->pe_flags |= LIBPE_F_BAD_SEC_HEADER;178return (0);179}180}181}182183return (libpe_parse_section_headers(pe));184}185186off_t187libpe_write_pe_header(PE *pe, off_t off)188{189char tmp[4];190191if (pe->pe_cmd == PE_C_RDWR &&192(pe->pe_flags & LIBPE_F_BAD_PE_HEADER) == 0) {193assert(pe->pe_dh != NULL);194off = lseek(pe->pe_fd, (off_t) pe->pe_dh->dh_lfanew + 4,195SEEK_SET);196return (off);197}198199/*200* PE Header should to be aligned on 8-byte boundary according to201* the PE/COFF specification.202*/203if ((off = libpe_align(pe, off, 8)) < 0)204return (-1);205206le32enc(tmp, PE_SIGNATURE);207if (write(pe->pe_fd, tmp, sizeof(tmp)) != (ssize_t) sizeof(tmp)) {208errno = EIO;209return (-1);210}211212off += 4;213214pe->pe_flags &= ~LIBPE_F_BAD_PE_HEADER;215216/* Trigger rewrite for the following headers. */217pe->pe_flags |= LIBPE_F_DIRTY_COFF_HEADER;218pe->pe_flags |= LIBPE_F_DIRTY_OPT_HEADER;219220return (off);221}222223off_t224libpe_write_coff_header(PE *pe, off_t off)225{226char tmp[128], *hdr;227PE_CoffHdr *ch;228PE_DataDir *dd;229PE_OptHdr *oh;230PE_Scn *ps;231PE_SecHdr *sh;232unsigned p;233uint32_t reloc_rva, reloc_sz;234int i, reloc;235236reloc = 0;237reloc_rva = reloc_sz = 0;238239if (pe->pe_cmd == PE_C_RDWR) {240assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0);241242if ((pe->pe_flags & LIBPE_F_DIRTY_COFF_HEADER) == 0 &&243(pe->pe_flags & LIBPE_F_BAD_COFF_HEADER) == 0) {244if (lseek(pe->pe_fd, (off_t) sizeof(PE_CoffHdr),245SEEK_CUR) < 0) {246errno = EIO;247return (-1);248}249off += sizeof(PE_CoffHdr);250assert(pe->pe_ch != NULL);251ch = pe->pe_ch;252goto coff_done;253}254255/* lseek(2) to the offset of the COFF header. */256if (lseek(pe->pe_fd, off, SEEK_SET) < 0) {257errno = EIO;258return (-1);259}260}261262if (pe->pe_ch == NULL) {263if ((ch = calloc(1, sizeof(PE_CoffHdr))) == NULL) {264errno = ENOMEM;265return (-1);266}267pe->pe_ch = ch;268269/*270* Default value for ch_machine if not provided by the271* application.272*/273if (pe->pe_obj == PE_O_PE32P)274ch->ch_machine = IMAGE_FILE_MACHINE_AMD64;275else276ch->ch_machine = IMAGE_FILE_MACHINE_I386;277278} else279ch = pe->pe_ch;280281if (!ch->ch_timestamp)282ch->ch_timestamp = time(NULL);283284if (pe->pe_obj == PE_O_PE32) {285if (!ch->ch_optsize)286ch->ch_optsize = PE_COFF_OPT_SIZE_32;287ch->ch_char |= IMAGE_FILE_EXECUTABLE_IMAGE |288IMAGE_FILE_32BIT_MACHINE;289} else if (pe->pe_obj == PE_O_PE32P) {290if (!ch->ch_optsize)291ch->ch_optsize = PE_COFF_OPT_SIZE_32P;292ch->ch_char |= IMAGE_FILE_EXECUTABLE_IMAGE |293IMAGE_FILE_LARGE_ADDRESS_AWARE;294} else295ch->ch_optsize = 0;296297/*298* COFF line number is deprecated by the PE/COFF299* specification. COFF symbol table is deprecated300* for executables.301*/302ch->ch_char |= IMAGE_FILE_LINE_NUMS_STRIPPED;303if (pe->pe_obj == PE_O_PE32 || pe->pe_obj == PE_O_PE32P)304ch->ch_char |= IMAGE_FILE_LOCAL_SYMS_STRIPPED;305306ch->ch_nsec = pe->pe_nscn;307308STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) {309sh = &ps->ps_sh;310311if (ps->ps_ndx == 0xFFFFFFFFU) {312ch->ch_symptr = sh->sh_rawptr;313ch->ch_nsym = pe->pe_nsym;314}315316if (pe->pe_obj == PE_O_PE32 || pe->pe_obj == PE_O_PE32P) {317if (ps->ps_ndx == (0xFFFF0000 | PE_DD_BASERELOC) ||318strncmp(sh->sh_name, ".reloc", strlen(".reloc")) ==3190) {320reloc = 1;321reloc_rva = sh->sh_addr;322reloc_sz = sh->sh_virtsize;323}324}325}326327if (!reloc)328ch->ch_char |= IMAGE_FILE_RELOCS_STRIPPED;329330if (pe->pe_flags & LIBPE_F_BAD_OPT_HEADER) {331if (pe->pe_obj == PE_O_PE32)332ch->ch_optsize = PE_COFF_OPT_SIZE_32;333else if (pe->pe_obj == PE_O_PE32P)334ch->ch_optsize = PE_COFF_OPT_SIZE_32P;335else336ch->ch_optsize = 0;337}338339/*340* Write the COFF header.341*/342hdr = tmp;343PE_WRITE16(hdr, ch->ch_machine);344PE_WRITE16(hdr, ch->ch_nsec);345PE_WRITE32(hdr, ch->ch_timestamp);346PE_WRITE32(hdr, ch->ch_symptr);347PE_WRITE32(hdr, ch->ch_nsym);348PE_WRITE16(hdr, ch->ch_optsize);349PE_WRITE16(hdr, ch->ch_char);350if (write(pe->pe_fd, tmp, sizeof(PE_CoffHdr)) !=351(ssize_t) sizeof(PE_CoffHdr)) {352errno = EIO;353return (-1);354}355356coff_done:357off += sizeof(PE_CoffHdr);358pe->pe_flags &= ~LIBPE_F_DIRTY_COFF_HEADER;359pe->pe_flags &= ~LIBPE_F_BAD_COFF_HEADER;360pe->pe_flags |= LIBPE_F_DIRTY_SEC_HEADER;361362if (ch->ch_optsize == 0)363return (off);364365/*366* Write the Optional header.367*/368369if (pe->pe_cmd == PE_C_RDWR) {370if ((pe->pe_flags & LIBPE_F_DIRTY_OPT_HEADER) == 0 &&371(pe->pe_flags & LIBPE_F_BAD_OPT_HEADER) == 0) {372if (lseek(pe->pe_fd, (off_t) ch->ch_optsize,373SEEK_CUR) < 0) {374errno = EIO;375return (-1);376}377off += ch->ch_optsize;378return (off);379}380381}382383if (pe->pe_oh == NULL) {384if ((oh = calloc(1, sizeof(PE_OptHdr))) == NULL) {385errno = ENOMEM;386return (-1);387}388pe->pe_oh = oh;389} else390oh = pe->pe_oh;391392if (pe->pe_obj == PE_O_PE32)393oh->oh_magic = PE_FORMAT_32;394else395oh->oh_magic = PE_FORMAT_32P;396397/*398* LinkerVersion should not be less than 2.5, which will cause399* Windows to complain the executable is invalid in some case.400* By default we set LinkerVersion to 2.22 (binutils 2.22)401*/402if (!oh->oh_ldvermajor && !oh->oh_ldverminor) {403oh->oh_ldvermajor = 2;404oh->oh_ldverminor = 22;405}406407/*408* The library always tries to write out all 16 data directories409* but the actual data dir written will depend on ch_optsize.410*/411oh->oh_ndatadir = PE_DD_MAX;412413if (!oh->oh_filealign)414oh->oh_filealign = 0x200;415if (!oh->oh_secalign)416oh->oh_secalign = 0x1000;417oh->oh_hdrsize = roundup(off + ch->ch_optsize + pe->pe_nscn *418sizeof(PE_SecHdr), oh->oh_filealign);419oh->oh_imgsize = roundup(pe->pe_rvamax, oh->oh_secalign);420421#define WRITE_OPT(n) \422do { \423/* \424* Since the Optional Header size is variable, we must \425* check if the requested write size will overrun the \426* remaining header bytes. \427*/ \428if (p + (n) > ch->ch_optsize) { \429/* Pad the "extra" bytes */ \430if (libpe_pad(pe, ch->ch_optsize - p) < 0) { \431errno = EIO; \432return (-1); \433} \434goto opt_done; \435} \436if (write(pe->pe_fd, tmp, (n)) != (ssize_t) (n)) { \437errno = EIO; \438return (-1); \439} \440p += (n); \441} while (0)442#define WRITE_OPT8(v) do { *tmp = (v); WRITE_OPT(1); } while(0)443#define WRITE_OPT16(v) do { le16enc(tmp, (v)); WRITE_OPT(2); } while(0)444#define WRITE_OPT32(v) do { le32enc(tmp, (v)); WRITE_OPT(4); } while(0)445#define WRITE_OPT64(v) do { le64enc(tmp, (v)); WRITE_OPT(8); } while(0)446447p = 0;448WRITE_OPT16(oh->oh_magic);449if (oh->oh_magic == PE_FORMAT_32P)450pe->pe_obj = PE_O_PE32P;451WRITE_OPT8(oh->oh_ldvermajor);452WRITE_OPT8(oh->oh_ldverminor);453WRITE_OPT32(oh->oh_textsize);454WRITE_OPT32(oh->oh_datasize);455WRITE_OPT32(oh->oh_bsssize);456WRITE_OPT32(oh->oh_entry);457WRITE_OPT32(oh->oh_textbase);458if (oh->oh_magic != PE_FORMAT_32P) {459WRITE_OPT32(oh->oh_database);460WRITE_OPT32(oh->oh_imgbase);461} else462WRITE_OPT64(oh->oh_imgbase);463WRITE_OPT32(oh->oh_secalign);464WRITE_OPT32(oh->oh_filealign);465WRITE_OPT16(oh->oh_osvermajor);466WRITE_OPT16(oh->oh_osverminor);467WRITE_OPT16(oh->oh_imgvermajor);468WRITE_OPT16(oh->oh_imgverminor);469WRITE_OPT16(oh->oh_subvermajor);470WRITE_OPT16(oh->oh_subverminor);471WRITE_OPT32(oh->oh_win32ver);472WRITE_OPT32(oh->oh_imgsize);473WRITE_OPT32(oh->oh_hdrsize);474WRITE_OPT32(oh->oh_checksum);475WRITE_OPT16(oh->oh_subsystem);476WRITE_OPT16(oh->oh_dllchar);477if (oh->oh_magic != PE_FORMAT_32P) {478WRITE_OPT32(oh->oh_stacksizer);479WRITE_OPT32(oh->oh_stacksizec);480WRITE_OPT32(oh->oh_heapsizer);481WRITE_OPT32(oh->oh_heapsizec);482} else {483WRITE_OPT64(oh->oh_stacksizer);484WRITE_OPT64(oh->oh_stacksizec);485WRITE_OPT64(oh->oh_heapsizer);486WRITE_OPT64(oh->oh_heapsizec);487}488WRITE_OPT32(oh->oh_ldrflags);489WRITE_OPT32(oh->oh_ndatadir);490491/*492* Write the Data Directories.493*/494495if (oh->oh_ndatadir > 0) {496if (pe->pe_dd == NULL) {497if ((dd = calloc(1, sizeof(PE_DataDir))) == NULL) {498errno = ENOMEM;499return (-1);500}501pe->pe_dd = dd;502dd->dd_total = PE_DD_MAX;503} else504dd = pe->pe_dd;505506assert(oh->oh_ndatadir <= PE_DD_MAX);507508if (reloc) {509dd->dd_e[PE_DD_BASERELOC].de_addr = reloc_rva;510dd->dd_e[PE_DD_BASERELOC].de_size = reloc_sz;511}512513for (i = 0; (uint32_t) i < dd->dd_total; i++) {514WRITE_OPT32(dd->dd_e[i].de_addr);515WRITE_OPT32(dd->dd_e[i].de_size);516}517}518519/* Pad the remaining bytes in the Optional header, if any. */520if (ch->ch_optsize > p) {521if (libpe_pad(pe, ch->ch_optsize - p) < 0) {522errno = EIO;523return (-1);524}525}526527opt_done:528off += ch->ch_optsize;529pe->pe_flags &= ~LIBPE_F_DIRTY_OPT_HEADER;530pe->pe_flags &= ~LIBPE_F_BAD_OPT_HEADER;531pe->pe_flags |= LIBPE_F_DIRTY_SEC_HEADER;532533return (off);534}535536537