diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h --- a/bolt/include/bolt/Rewrite/RewriteInstance.h +++ b/bolt/include/bolt/Rewrite/RewriteInstance.h @@ -471,6 +471,10 @@ uint64_t NewTextSegmentOffset{0}; uint64_t NewTextSegmentSize{0}; + /// New writable segment info. + uint64_t NewWritableSegmentAddress{0}; + uint64_t NewWritableSegmentSize{0}; + /// Track next available address for new allocatable sections. uint64_t NextAvailableAddress{0}; diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -3916,6 +3916,13 @@ // Allocate read-only sections first, then writable sections. enum : uint8_t { ST_READONLY, ST_READWRITE }; for (uint8_t SType = ST_READONLY; SType <= ST_READWRITE; ++SType) { + const uint64_t LastNextAvailableAddress = NextAvailableAddress; + if (SType == ST_READWRITE) { + // Align R+W segment to regular page size + NextAvailableAddress = alignTo(NextAvailableAddress, BC->RegularPageSize); + NewWritableSegmentAddress = NextAvailableAddress; + } + for (BinarySection &Section : BC->allocatableSections()) { if (!Section.hasValidSectionID()) continue; @@ -3962,6 +3969,21 @@ NextAvailableAddress += Section.getOutputSize(); } } + + if (SType == ST_READONLY) { + if (PHDRTableAddress) { + // Segment size includes the size of the PHDR area. + NewTextSegmentSize = NextAvailableAddress - PHDRTableAddress; + } else { + // Existing PHDR table would be updated. + NewTextSegmentSize = NextAvailableAddress - NewTextSegmentAddress; + } + } else if (SType == ST_READWRITE) { + NewWritableSegmentSize = NextAvailableAddress - NewWritableSegmentAddress; + // Restore NextAvailableAddress if no new writable sections + if (!NewWritableSegmentSize) + NextAvailableAddress = LastNextAvailableAddress; + } } } @@ -3982,16 +4004,30 @@ // Write/re-write program headers. Phnum = Obj.getHeader().e_phnum; if (PHDRTableOffset) { - // Writing new pheader table. - Phnum += 1; // only adding one new segment - // Segment size includes the size of the PHDR area. - NewTextSegmentSize = NextAvailableAddress - PHDRTableAddress; + // Writing new pheader table and adding one new entry for R+X segment. + Phnum += 1; + if (NewWritableSegmentSize) { + // Adding one more entry for R+W segment. + Phnum += 1; + } } else { assert(!PHDRTableAddress && "unexpected address for program header table"); - // Update existing table. PHDRTableOffset = Obj.getHeader().e_phoff; - NewTextSegmentSize = NextAvailableAddress - NewTextSegmentAddress; + if (NewWritableSegmentSize) { + errs() << "Unable to add writable segment with UseGnuStack option\n"; + exit(1); + } + } + + // NOTE Currently .eh_frame_hdr appends to the last segment, recalculate + // new text segment size if writable segment won't be created + if (!NewWritableSegmentSize) { + if (PHDRTableAddress) + NewTextSegmentSize = NextAvailableAddress - PHDRTableAddress; + else + NewTextSegmentSize = NextAvailableAddress - NewTextSegmentAddress; } + OS.seek(PHDRTableOffset); bool ModdedGnuStack = false; @@ -4023,6 +4059,19 @@ return NewPhdr; }; + auto createNewWritableSectionsPhdr = [&]() { + ELF64LEPhdrTy NewPhdr; + NewPhdr.p_type = ELF::PT_LOAD; + NewPhdr.p_offset = getFileOffsetForAddress(NewWritableSegmentAddress); + NewPhdr.p_vaddr = NewWritableSegmentAddress; + NewPhdr.p_paddr = NewWritableSegmentAddress; + NewPhdr.p_filesz = NewWritableSegmentSize; + NewPhdr.p_memsz = NewWritableSegmentSize; + NewPhdr.p_align = BC->RegularPageSize; + NewPhdr.p_flags = ELF::PF_R | ELF::PF_W; + return NewPhdr; + }; + // Copy existing program headers with modifications. for (const ELF64LE::Phdr &Phdr : cantFail(Obj.program_headers())) { ELF64LE::Phdr NewPhdr = Phdr; @@ -4051,6 +4100,11 @@ ELF64LE::Phdr NewTextPhdr = createNewTextPhdr(); OS.write(reinterpret_cast(&NewTextPhdr), sizeof(NewTextPhdr)); + if (NewWritableSegmentSize) { + ELF64LEPhdrTy NewWritablePhdr = createNewWritableSectionsPhdr(); + OS.write(reinterpret_cast(&NewWritablePhdr), + sizeof(NewWritablePhdr)); + } AddedSegment = true; } OS.write(reinterpret_cast(&NewPhdr), sizeof(NewPhdr)); @@ -4060,6 +4114,11 @@ // Append the new header to the end of the table. ELF64LE::Phdr NewTextPhdr = createNewTextPhdr(); OS.write(reinterpret_cast(&NewTextPhdr), sizeof(NewTextPhdr)); + if (NewWritableSegmentSize) { + ELF64LEPhdrTy NewWritablePhdr = createNewWritableSectionsPhdr(); + OS.write(reinterpret_cast(&NewWritablePhdr), + sizeof(NewWritablePhdr)); + } } assert((!opts::UseGnuStack || ModdedGnuStack) &&