Skip to content

Commit 667e1fa

Browse files
authored
[profile] Use base+vaddr for __llvm_write_binary_ids note pointers (#114907)
This function is always examining its own ELF headers in memory, but it was trying to use conditions between examining files or memory, and it wasn't accounting for LOAD offsets at runtime. This is especially bad if a loaded segment has additional padding that's not in the file offsets. Now we do a first scan of the program headers to figure out the runtime base address based on `PT_PHDR` and/or `PT_DYNAMIC` (else assume zero), similar to libc's `do_start`. Then each `PT_NOTE` pointer is simply the base plus the segments's `pt_vaddr`, which includes LOAD offsets. Fixes #114605
1 parent 391bf06 commit 667e1fa

File tree

2 files changed

+49
-24
lines changed

2 files changed

+49
-24
lines changed

compiler-rt/lib/profile/InstrProfilingPlatformLinux.c

+16-24
Original file line numberDiff line numberDiff line change
@@ -194,41 +194,33 @@ static int WriteBinaryIds(ProfDataWriter *Writer, const ElfW(Nhdr) * Note,
194194
*/
195195
COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
196196
extern const ElfW(Ehdr) __ehdr_start __attribute__((visibility("hidden")));
197+
extern ElfW(Dyn) _DYNAMIC[] __attribute__((weak, visibility("hidden")));
198+
197199
const ElfW(Ehdr) *ElfHeader = &__ehdr_start;
198200
const ElfW(Phdr) *ProgramHeader =
199201
(const ElfW(Phdr) *)((uintptr_t)ElfHeader + ElfHeader->e_phoff);
200202

203+
/* Compute the added base address in case of position-independent code. */
204+
uintptr_t Base = 0;
205+
for (uint32_t I = 0; I < ElfHeader->e_phnum; I++) {
206+
if (ProgramHeader[I].p_type == PT_PHDR)
207+
Base = (uintptr_t)ProgramHeader - ProgramHeader[I].p_vaddr;
208+
if (ProgramHeader[I].p_type == PT_DYNAMIC && _DYNAMIC)
209+
Base = (uintptr_t)_DYNAMIC - ProgramHeader[I].p_vaddr;
210+
}
211+
201212
int TotalBinaryIdsSize = 0;
202-
uint32_t I;
203213
/* Iterate through entries in the program header. */
204-
for (I = 0; I < ElfHeader->e_phnum; I++) {
214+
for (uint32_t I = 0; I < ElfHeader->e_phnum; I++) {
205215
/* Look for the notes segment in program header entries. */
206216
if (ProgramHeader[I].p_type != PT_NOTE)
207217
continue;
208218

209219
/* There can be multiple notes segment, and examine each of them. */
210-
const ElfW(Nhdr) * Note;
211-
const ElfW(Nhdr) * NotesEnd;
212-
/*
213-
* When examining notes in file, use p_offset, which is the offset within
214-
* the elf file, to find the start of notes.
215-
*/
216-
if (ProgramHeader[I].p_memsz == 0 ||
217-
ProgramHeader[I].p_memsz == ProgramHeader[I].p_filesz) {
218-
Note = (const ElfW(Nhdr) *)((uintptr_t)ElfHeader +
219-
ProgramHeader[I].p_offset);
220-
NotesEnd = (const ElfW(Nhdr) *)((const char *)(Note) +
221-
ProgramHeader[I].p_filesz);
222-
} else {
223-
/*
224-
* When examining notes in memory, use p_vaddr, which is the address of
225-
* section after loaded to memory, to find the start of notes.
226-
*/
227-
Note =
228-
(const ElfW(Nhdr) *)((uintptr_t)ElfHeader + ProgramHeader[I].p_vaddr);
229-
NotesEnd =
230-
(const ElfW(Nhdr) *)((const char *)(Note) + ProgramHeader[I].p_memsz);
231-
}
220+
const ElfW(Nhdr) *Note =
221+
(const ElfW(Nhdr) *)(Base + ProgramHeader[I].p_vaddr);
222+
const ElfW(Nhdr) *NotesEnd =
223+
(const ElfW(Nhdr) *)((const char *)(Note) + ProgramHeader[I].p_memsz);
232224

233225
int BinaryIdsSize = WriteBinaryIds(Writer, Note, NotesEnd);
234226
if (TotalBinaryIdsSize == -1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// REQUIRES: linux
2+
//
3+
// Make sure the build-id can be found in both EXEC and DYN (PIE) files,
4+
// even when the note's section-start is forced to a weird address.
5+
// (The DYN case would also apply to libraries, not explicitly tested here.)
6+
7+
// DEFINE: %{cflags} =
8+
// DEFINE: %{check} = ( \
9+
// DEFINE: %clang_profgen -Wl,--build-id -o %t %s %{cflags} && \
10+
// DEFINE: env LLVM_PROFILE_FILE=%t.profraw %run %t && \
11+
// DEFINE: llvm-readelf --notes %t && \
12+
// DEFINE: llvm-profdata show --binary-ids %t.profraw \
13+
// DEFINE: ) | FileCheck %s
14+
15+
// REDEFINE: %{cflags} = -no-pie
16+
// RUN: %{check}
17+
18+
// REDEFINE: %{cflags} = -pie -fPIE
19+
// RUN: %{check}
20+
21+
// REDEFINE: %{cflags} = -no-pie -Wl,--section-start=.note.gnu.build-id=0x1000000
22+
// RUN: %{check}
23+
24+
// REDEFINE: %{cflags} = -pie -fPIE -Wl,--section-start=.note.gnu.build-id=0x1000000
25+
// RUN: %{check}
26+
27+
// CHECK-LABEL{LITERAL}: .note.gnu.build-id
28+
// CHECK: Build ID: [[ID:[0-9a-f]+]]
29+
30+
// CHECK-LABEL{LITERAL}: Binary IDs:
31+
// CHECK-NEXT: [[ID]]
32+
33+
int main() { return 0; }

0 commit comments

Comments
 (0)