#include #include #include #include #include #include #include #include namespace Elfpatcher { ProgramHeaderLayoutBuilder::ProgramHeaderLayoutBuilder( std::shared_ptr segmentFilter, std::shared_ptr byteWriter ) : _segmentFilter(std::move(segmentFilter)) , _byteWriter(std::move(byteWriter)) { } void ProgramHeaderLayoutBuilder::_writeProgramHeader(std::vector& buf, std::size_t offset, const Domain::ProgramHeader& ph) const { _byteWriter->WriteU32(buf, offset + kPhdrTypeOffset, ph.Type); _byteWriter->WriteU32(buf, offset + kPhdrFlagsOffset, ph.Flags); _byteWriter->WriteU64(buf, offset + kPhdrOffsetOffset, ph.Offset); _byteWriter->WriteU64(buf, offset + kPhdrVaddrOffset, ph.MappedAddress); _byteWriter->WriteU64(buf, offset + kPhdrPaddrOffset, ph.PhysicalAddress); _byteWriter->WriteU64(buf, offset + kPhdrFileSizeOffset, ph.FileSize); _byteWriter->WriteU64(buf, offset + kPhdrMemSizeOffset, ph.MemorySize); _byteWriter->WriteU64(buf, offset + kPhdrAlignOffset, ph.Alignment); } Domain::ProgramHeader ProgramHeaderLayoutBuilder::_makeLoadHeader(std::uint64_t offset, std::uint64_t vaddr, std::uint64_t size) const { Domain::ProgramHeader ph{}; ph.Type = PT_LOAD; ph.Flags = PF_R | PF_W | PF_X; ph.Offset = offset; ph.MappedAddress = vaddr; ph.PhysicalAddress = vaddr; ph.FileSize = size; ph.MemorySize = size; ph.Alignment = kDefaultLoadAlignment; return ph; } Domain::ProgramHeader ProgramHeaderLayoutBuilder::_makeHeaderBlockLoad(std::uint64_t vaddr, std::uint64_t size, std::uint64_t align) const { Domain::ProgramHeader ph{}; ph.Type = PT_LOAD; ph.Flags = PF_R; ph.Offset = 0; ph.MappedAddress = vaddr; ph.PhysicalAddress = vaddr; ph.FileSize = size; ph.MemorySize = size; ph.Alignment = align; return ph; } Domain::ProgramHeader ProgramHeaderLayoutBuilder::_makePhdrHeader(std::uint64_t offset, std::uint64_t vaddr, std::uint64_t size) const { Domain::ProgramHeader ph{}; ph.Type = PT_PHDR; ph.Flags = PF_R; ph.Offset = offset; ph.MappedAddress = vaddr; ph.PhysicalAddress = vaddr; ph.FileSize = size; ph.MemorySize = size; ph.Alignment = kPhdrHeaderAlignment; return ph; } Domain::ProgramHeader ProgramHeaderLayoutBuilder::_makeDynamicHeader(std::uint64_t offset, std::uint64_t vaddr, std::uint64_t size) const { Domain::ProgramHeader ph{}; ph.Type = PT_DYNAMIC; ph.Flags = PF_R | PF_W; ph.Offset = offset; ph.MappedAddress = vaddr; ph.PhysicalAddress = vaddr; ph.FileSize = size; ph.MemorySize = size; ph.Alignment = kDynamicHeaderAlignment; return ph; } Domain::ProgramHeader ProgramHeaderLayoutBuilder::_makeInterpHeader(std::uint64_t offset, std::uint64_t vaddr, std::uint64_t size) const { Domain::ProgramHeader ph{}; ph.Type = PT_INTERP; ph.Flags = PF_R; ph.Offset = offset; ph.MappedAddress = vaddr; ph.PhysicalAddress = vaddr; ph.FileSize = size; ph.MemorySize = size; ph.Alignment = kInterpHeaderAlignment; return ph; } std::uint32_t ProgramHeaderLayoutBuilder::_fixLoadFlags(std::uint32_t originalFlags) const { if (originalFlags == 0) return PF_R | PF_W | PF_X; return originalFlags | PF_R; } std::uint64_t ProgramHeaderLayoutBuilder::ComputeExtraBlockVaddr( const std::vector& originalHeaders, std::uint64_t extraBlockOffset ) const { bool foundLoad = false; std::uint64_t highestVaddrEnd = 0; for (const auto& ph : originalHeaders) { if (_segmentFilter->ShouldSkip(ph)) continue; if (ph.Type != PT_LOAD) continue; foundLoad = true; if (ph.MemorySize > std::numeric_limits::max() - ph.MappedAddress) throw Domain::RelinkerException("Load segment virtual range overflowed"); const std::uint64_t vaddrEnd = ph.MappedAddress + ph.MemorySize; if (vaddrEnd > highestVaddrEnd) highestVaddrEnd = vaddrEnd; } if (!foundLoad) throw Domain::RelinkerException("No kept PT_LOAD segment to anchor the extra block against"); const std::uint64_t align = kDefaultLoadAlignment; const std::uint64_t offsetRemainder = extraBlockOffset & (align - 1); const std::uint64_t alignedFloor = highestVaddrEnd & ~(align - 1); std::uint64_t vaddr = alignedFloor | offsetRemainder; if (vaddr < highestVaddrEnd) vaddr += align; if (vaddr < highestVaddrEnd) throw Domain::RelinkerException("Extra block vaddr computation overflowed"); return vaddr; } std::uint16_t ProgramHeaderLayoutBuilder::WriteLayout( std::vector& buf, const ProgramHeaderLayoutRequest& request ) const { std::vector kept; std::size_t frameCount = 0; for (const auto& ph : request.OriginalHeaders) { if (_segmentFilter->ShouldSkip(ph)) continue; kept.push_back(ph); if (ph.Type == PT_GNU_EH_FRAME) frameCount++; } const bool keepFrames = kept.size() + kSyntheticProgramHeaderCount <= request.PhNum; if (!keepFrames && frameCount > 0) { throw Domain::RelinkerException("No free program header slot for PT_GNU_EH_FRAME"); } _sortLoadHeaders(kept); const std::size_t neededPh = kept.size() + kSyntheticProgramHeaderCount; if (neededPh > request.PhNum) throw Domain::RelinkerException( "Not enough program header slots: need " + std::to_string(neededPh) + ", available " + std::to_string(request.PhNum)); const auto checkedEnd = [](std::uint64_t start, std::uint64_t size) { if (size > std::numeric_limits::max() - start) throw Domain::RelinkerException("Program header layout range overflowed"); return start + size; }; if (request.PhEntSize < 56) throw Domain::RelinkerException("Program header entry is too small"); const std::uint64_t headerBlockSize = checkedEnd(request.PhOff, static_cast(neededPh) * request.PhEntSize); if (headerBlockSize > buf.size()) throw Domain::RelinkerException("Program header table exceeds the output file"); if (request.ExtraBlockOffset > buf.size() || request.ExtraBlockSize > buf.size() - request.ExtraBlockOffset) throw Domain::RelinkerException("Extra block exceeds the output file"); std::uint64_t headerBlockAlign = kDefaultLoadAlignment; for (const auto& ph : request.OriginalHeaders) { if (ph.Type == PT_LOAD && !_segmentFilter->ShouldSkip(ph) && ph.Alignment > headerBlockAlign && (ph.Alignment & (ph.Alignment - 1)) == 0) headerBlockAlign = ph.Alignment; } const std::uint64_t headerBlockVaddr = checkedEnd(checkedEnd(request.ExtraBlockVaddr, request.ExtraBlockSize), headerBlockAlign - 1) & ~(headerBlockAlign - 1); checkedEnd(headerBlockVaddr, headerBlockSize); if (request.DynamicSegmentOffset < request.ExtraBlockOffset) throw Domain::RelinkerException("Dynamic segment offset lies before the extra block"); if (request.DynamicSegmentOffset - request.ExtraBlockOffset > request.ExtraBlockSize || request.DynamicSegmentSize > request.ExtraBlockSize - (request.DynamicSegmentOffset - request.ExtraBlockOffset)) throw Domain::RelinkerException("Dynamic segment does not fit within the extra block"); if (request.InterpOffset < request.ExtraBlockOffset) throw Domain::RelinkerException("Interp offset lies before the extra block"); if (request.InterpOffset - request.ExtraBlockOffset > request.ExtraBlockSize || request.InterpSize > request.ExtraBlockSize - (request.InterpOffset - request.ExtraBlockOffset)) throw Domain::RelinkerException("Interp data does not fit within the extra block"); const std::uint64_t dynamicSegmentVaddr = request.ExtraBlockVaddr + (request.DynamicSegmentOffset - request.ExtraBlockOffset); const std::uint64_t interpVaddr = request.ExtraBlockVaddr + (request.InterpOffset - request.ExtraBlockOffset); std::vector layout; layout.push_back(_makePhdrHeader(request.PhOff, headerBlockVaddr + request.PhOff, static_cast(neededPh) * request.PhEntSize)); layout.push_back(_makeInterpHeader(request.InterpOffset, interpVaddr, request.InterpSize)); for (auto ph : kept) { if (ph.Type == PT_LOAD) ph.Flags = _fixLoadFlags(ph.Flags); layout.push_back(ph); } layout.push_back(_makeLoadHeader(request.ExtraBlockOffset, request.ExtraBlockVaddr, request.ExtraBlockSize)); layout.push_back(_makeHeaderBlockLoad(headerBlockVaddr, headerBlockSize, headerBlockAlign)); layout.push_back(_makeDynamicHeader(request.DynamicSegmentOffset, dynamicSegmentVaddr, request.DynamicSegmentSize)); for (std::size_t index = 0; index < layout.size(); ++index) _writeProgramHeader(buf, static_cast(request.PhOff) + index * request.PhEntSize, layout[index]); return static_cast(layout.size()); } void ProgramHeaderLayoutBuilder::_sortLoadHeaders(std::vector& headers) const { std::vector loads; for (const auto& ph : headers) { if (ph.Type == PT_LOAD) loads.push_back(ph); } std::stable_sort(loads.begin(), loads.end(), [](const Domain::ProgramHeader& left, const Domain::ProgramHeader& right) { return left.MappedAddress < right.MappedAddress; }); std::size_t next = 0; for (auto& ph : headers) { if (ph.Type == PT_LOAD) ph = loads[next++]; } } }