bpf: Move stack_map_get_build_id into lib
[linux-2.6-block.git] / lib / buildid.c
CommitLineData
bd7525da
JO
1// SPDX-License-Identifier: GPL-2.0
2
3#include <linux/buildid.h>
4#include <linux/elf.h>
5#include <linux/pagemap.h>
6
7#define BUILD_ID 3
8/*
9 * Parse build id from the note segment. This logic can be shared between
10 * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
11 * identical.
12 */
13static inline int parse_build_id(void *page_addr,
14 unsigned char *build_id,
15 void *note_start,
16 Elf32_Word note_size)
17{
18 Elf32_Word note_offs = 0, new_offs;
19
20 /* check for overflow */
21 if (note_start < page_addr || note_start + note_size < note_start)
22 return -EINVAL;
23
24 /* only supports note that fits in the first page */
25 if (note_start + note_size > page_addr + PAGE_SIZE)
26 return -EINVAL;
27
28 while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
29 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
30
31 if (nhdr->n_type == BUILD_ID &&
32 nhdr->n_namesz == sizeof("GNU") &&
33 nhdr->n_descsz > 0 &&
34 nhdr->n_descsz <= BUILD_ID_SIZE_MAX) {
35 memcpy(build_id,
36 note_start + note_offs +
37 ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr),
38 nhdr->n_descsz);
39 memset(build_id + nhdr->n_descsz, 0,
40 BUILD_ID_SIZE_MAX - nhdr->n_descsz);
41 return 0;
42 }
43 new_offs = note_offs + sizeof(Elf32_Nhdr) +
44 ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
45 if (new_offs <= note_offs) /* overflow */
46 break;
47 note_offs = new_offs;
48 }
49 return -EINVAL;
50}
51
52/* Parse build ID from 32-bit ELF */
53static int get_build_id_32(void *page_addr, unsigned char *build_id)
54{
55 Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;
56 Elf32_Phdr *phdr;
57 int i;
58
59 /* only supports phdr that fits in one page */
60 if (ehdr->e_phnum >
61 (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
62 return -EINVAL;
63
64 phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
65
66 for (i = 0; i < ehdr->e_phnum; ++i) {
67 if (phdr[i].p_type == PT_NOTE &&
68 !parse_build_id(page_addr, build_id,
69 page_addr + phdr[i].p_offset,
70 phdr[i].p_filesz))
71 return 0;
72 }
73 return -EINVAL;
74}
75
76/* Parse build ID from 64-bit ELF */
77static int get_build_id_64(void *page_addr, unsigned char *build_id)
78{
79 Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;
80 Elf64_Phdr *phdr;
81 int i;
82
83 /* only supports phdr that fits in one page */
84 if (ehdr->e_phnum >
85 (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
86 return -EINVAL;
87
88 phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
89
90 for (i = 0; i < ehdr->e_phnum; ++i) {
91 if (phdr[i].p_type == PT_NOTE &&
92 !parse_build_id(page_addr, build_id,
93 page_addr + phdr[i].p_offset,
94 phdr[i].p_filesz))
95 return 0;
96 }
97 return -EINVAL;
98}
99
100/* Parse build ID of ELF file mapped to vma */
101int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id)
102{
103 Elf32_Ehdr *ehdr;
104 struct page *page;
105 void *page_addr;
106 int ret;
107
108 /* only works for page backed storage */
109 if (!vma->vm_file)
110 return -EINVAL;
111
112 page = find_get_page(vma->vm_file->f_mapping, 0);
113 if (!page)
114 return -EFAULT; /* page not mapped */
115
116 ret = -EINVAL;
117 page_addr = kmap_atomic(page);
118 ehdr = (Elf32_Ehdr *)page_addr;
119
120 /* compare magic x7f "ELF" */
121 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
122 goto out;
123
124 /* only support executable file and shared object file */
125 if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
126 goto out;
127
128 if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
129 ret = get_build_id_32(page_addr, build_id);
130 else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
131 ret = get_build_id_64(page_addr, build_id);
132out:
133 kunmap_atomic(page_addr);
134 put_page(page);
135 return ret;
136}