UNPKG

elflib

Version:
285 lines (284 loc) 9.88 kB
import * as ELF from './types/index.js'; import { HelperDataView } from './reader.js'; import { divide, toNumberSafe } from './biginthelpers.js'; import { decode } from './encoding.js'; import { RPL } from './rplsections.js'; //const MAX_SECTION_LOAD_SIZE = 0x1000000; export function getString(strings, index) { if (!Object.keys(strings).length) return '<compressed>'; let str = strings[index]; if (!str) { // both GCC and clang have a tendency to // point to the middle of a string if the // end part is what's needed for (const key in strings) { const kv = parseInt(key); if (kv < index) { const ss = strings[kv]; if (kv + ss.length > index) { str = ss.substr(index - kv); break; } } } } return str || '<error>'; } export function readStringSection(section) { const tmp = section.data; const strings = {}; let ix = 0; for (let i = 0; i < section.size; i++) { if (tmp[i] === 0) { const slen = i - ix; if (slen > 0) strings[ix] = decode(tmp, ix, slen); ix = i + 1; } } return strings; } export function readSymbolsSection(section, endian, bits) { const num = divide(section.size, section.entSize); const symbols = []; const view = HelperDataView(new DataView(section.data.buffer), endian === ELF.Endian.Big); let ix = 0; for (let i = 0; i < num; i++) { let name, info, other, shndx, value, size; if (bits === 32) { name = view.readUInt32(ix); ix += 4; value = view.readUInt32(ix); ix += 4; size = view.readUInt32(ix); ix += 4; info = view.readUInt8(ix); ix += 1; other = view.readUInt8(ix); ix += 1; shndx = view.readUInt16(ix); ix += 2; } else { name = view.readUInt32(ix); ix += 4; info = view.readUInt8(ix); ix += 1; other = view.readUInt8(ix); ix += 1; shndx = view.readUInt16(ix); ix += 2; value = view.readUInt64(ix); ix += 8; size = Number(view.readUInt64(ix)); ix += 8; } //const type = info & 0xF; //const binding = info >> 4; //const visibility = other & 3; let symbol = new ELF.Symbol(section.index); symbol.nameOffset = name; symbol.info = info; symbol.other = other; symbol.shndx = shndx; symbol.value = value; symbol.size = size; symbols[i] = symbol; } return symbols; } export function readRelocationSection(section, endian, bits) { const num = toNumberSafe(divide(section.size, section.entSize)); const relocations = new Array(num); const view = HelperDataView(new DataView(section.data.buffer), endian === ELF.Endian.Big); let ix = 0; for (let i = 0; i < num; i++) { let addr; let info; let addend; if (bits === 32) { addr = view.readUInt32(ix); ix += 4; info = view.readUInt32(ix); ix += 4; if (section.type === ELF.SectionType.Rela) addend = view.readSInt32(ix); ix += 4; //symbolIndex = info >> 8; //? 0x######00 //type = info & 0xFF; //? 0x000000## } else { addr = view.readUInt64(ix); ix += 8; info = view.readUInt64(ix); ix += 8; if (section.type === ELF.SectionType.Rela) addend = view.readSInt64(ix); ix += 8; //symbolIndex = toNumberSafe(info >> BigInt(32)); //? 0x########00000000 //type = toNumberSafe(info & BigInt(0xFFFFFFFF)); //? 0x00000000######## } let relocation = new ELF.Relocation(); relocation.addr = addr; relocation.info = info; relocation.addend = addend; relocations[i] = relocation; } return relocations; } export async function readSectionHeaderEntries(fh, elfheader, readSymbolData) { const sh_entsize = elfheader.sectionHeadersEntrySize; const sh_num = elfheader.sectionHeadersEntryCount; if (sh_num === 0) return []; const result = new Array(sh_num); for (let i = 0; i < sh_num; i++) { const view = HelperDataView(await fh.view(sh_entsize, Number(elfheader.sectionHeadersOffset) + i * Number(sh_entsize)), elfheader.endian === ELF.Endian.Big); const name = view.readUInt32(0); const type = view.readUInt32(4); let ix = 8; let flags, addr, offset, size, link, info, addralign, entsize; if (elfheader.bits === 32) { flags = view.readUInt32(ix); ix += 4; addr = view.readUInt32(ix); ix += 4; offset = view.readUInt32(ix); ix += 4; size = view.readUInt32(ix); ix += 4; link = view.readUInt32(ix); ix += 4; info = view.readUInt32(ix); ix += 4; addralign = view.readUInt32(ix); ix += 4; entsize = view.readUInt32(ix); ix += 4; } else { flags = toNumberSafe(view.readUInt64(ix)); ix += 8; addr = view.readUInt64(ix); ix += 8; offset = toNumberSafe(view.readUInt64(ix)); ix += 8; size = toNumberSafe(view.readUInt64(ix)); ix += 8; link = view.readUInt32(ix); ix += 4; info = view.readUInt32(ix); ix += 4; addralign = toNumberSafe(view.readUInt64(ix)); ix += 8; entsize = toNumberSafe(view.readUInt64(ix)); ix += 8; } let section = new ELF.Section(); section.data = offset === 0 ? new Uint8Array(0) : new Uint8Array(view.buffer.slice(offset, offset + size)); section.index = i; section.nameOffset = name; section.type = type; section.flags = flags; section.addr = addr; section.offset = offset; section.size = size; section.link = link; section.info = info; section.addrAlign = addralign; section.entSize = entsize; result[i] = section; } // process special sections for (const section of result) { if (isStringSection(section)) { if (section.flags & ELF.SectionFlags.Compressed) section.strings = {}; else section.strings = readStringSection(section); } if (isSymbolSection(section)) { if (section.flags & ELF.SectionFlags.Compressed) section.symbols = []; else section.symbols = readSymbolsSection(section, elfheader.endian, elfheader.bits); } if (isRelocationSection(section)) { if (section.flags & ELF.SectionFlags.Compressed) section.relocations = []; else section.relocations = readRelocationSection(section, elfheader.endian, elfheader.bits); } if (elfheader.type === ELF.Type.RPL && elfheader.bits === 32) { if (RPL.isFileInfoSection(section)) section.fileinfo = RPL.readFileInfoSection(section, elfheader.endian); } } return result; } export function isStringSection(section) { return section.type === ELF.SectionType.StrTab; } export function isSymbolSection(section) { return section.type === ELF.SectionType.DynSym || section.type === ELF.SectionType.SymTab; } export function isRelocationSection(section) { return section.type === ELF.SectionType.Rel || section.type === ELF.SectionType.Rela; } export function parseSymInfo(symInfo) { return { symidx: symInfo >> 8, type: symInfo & 0xFF }; } export function packSymInfo(symIdx, type) { return Number('0x' + symIdx.toString(16).padStart(6, '0') + type.toString(16).padStart(2, '0')); } export function packStringSection(section, size) { if (size <= 1) return Buffer.alloc(1); if (size === 2) return Buffer.alloc(2); const buf = Buffer.alloc(size); let ix = 1; for (let key in section.strings) { const str = section.strings[key]; if (buf[ix] !== 0) throw new Error(`Failed to pack ELF because of corrupt string section of index ${section.index}:\n` + `\tString '${str}' at .strtab offset 0x${Number(key).toString(16).toUpperCase()} is overlapped by the previous string which is too long.`); ix += buf.write(str + '\0', ix); } return buf; } export function packSymbolSection(section, size) { const buf = Buffer.alloc(size); let ix = 0; for (let symbol of section.symbols) { buf.writeUInt32BE(symbol.nameOffset, ix); ix += 4; buf.writeUInt32BE(Number(symbol.value), ix); ix += 4; buf.writeUInt32BE(symbol.size, ix); ix += 4; buf.writeUInt8(symbol.info, ix); ix += 1; buf.writeUInt8(symbol.other, ix); ix += 1; buf.writeUInt16BE(symbol.shndx, ix); ix += 2; } return buf; } export function packRelocationSection(section, size) { const buf = Buffer.alloc(size); let ix = 0; for (let rel of section.relocations) { buf.writeUInt32BE(Number(rel.addr), ix); ix += 4; buf.writeUInt32BE(Number(rel.info), ix); ix += 4; if (rel.addend !== undefined) buf.writeInt32BE(Number(rel.addend), ix); ix += 4; } return buf; }