1f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// found in the LICENSE file.
4f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
5f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/*
6f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Copyright (C) 2012 The Android Open Source Project
7f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * All rights reserved.
8f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
9f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Redistribution and use in source and binary forms, with or without
10f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * modification, are permitted provided that the following conditions
11f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * are met:
12f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *  * Redistributions of source code must retain the above copyright
13f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *    notice, this list of conditions and the following disclaimer.
14f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *  * Redistributions in binary form must reproduce the above copyright
15f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *    notice, this list of conditions and the following disclaimer in
16f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *    the documentation and/or other materials provided with the
17f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *    distribution.
18f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
19f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * SUCH DAMAGE.
31f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
32f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "linker_phdr.h"
34f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include <errno.h>
36f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include <fcntl.h>
37f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include <sys/mman.h>
38f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include <unistd.h>
39f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
40f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#define PAGE_START(x) ((x) & PAGE_MASK)
41f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#define PAGE_OFFSET(x) ((x) & ~PAGE_MASK)
42f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
43f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
44f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Missing exec_elf.h definitions.
45f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#ifndef PT_GNU_RELRO
46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#define PT_GNU_RELRO 0x6474e552
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#endif
48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  TECHNICAL NOTE ON ELF LOADING.
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
52f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  An ELF file's program header table contains one or more PT_LOAD
53f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  segments, which corresponds to portions of the file that need to
54f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  be mapped into the process' address space.
55f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
56f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Each loadable segment has the following important properties:
57f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    p_offset  -> segment file offset
59f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    p_filesz  -> segment file size
60f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    p_memsz   -> segment memory size (always >= p_filesz)
61f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    p_vaddr   -> segment's virtual address
62f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    p_flags   -> segment flags (e.g. readable, writable, executable)
63f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
64f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  We will ignore the p_paddr and p_align fields of ELF::Phdr for now.
65f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
66f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz)
67f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ranges of virtual addresses. A few rules apply:
68f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
69f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  - the virtual address ranges should not overlap.
70f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
71f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  - if a segment's p_filesz is smaller than its p_memsz, the extra bytes
72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    between them should always be initialized to 0.
73f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
74f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  - ranges do not necessarily start or end at page boundaries. Two distinct
75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    segments can have their start and end on the same page. In this case, the
76f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    page inherits the mapping flags of the latter segment.
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Finally, the real load addrs of each segment is not p_vaddr. Instead the
79f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  loader decides where to load the first segment, then will load all others
80f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  relative to the first one to respect the initial range layout.
81f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
82f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  For example, consider the following list:
83f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
84f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    [ offset:0,      filesz:0x4000, memsz:0x4000, vaddr:0x30000 ],
85f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ],
86f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
87f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  This corresponds to two segments that cover these virtual address ranges:
88f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
89f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       0x30000...0x34000
90f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       0x40000...0x48000
91f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  If the loader decides to load the first segment at address 0xa0000000
93f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  then the segments' load address ranges will be:
94f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
95f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       0xa0030000...0xa0034000
96f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       0xa0040000...0xa0048000
97f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
98f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  In other words, all segments must be loaded at an address that has the same
99f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  constant offset from their p_vaddr value. This offset is computed as the
100f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  difference between the first segment's load address, and its p_vaddr value.
101f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
102f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  However, in practice, segments do _not_ start at page boundaries. Since we
103f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  can only memory-map at page boundaries, this means that the bias is
104f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  computed as:
105f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
106f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr)
107f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
108f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  (NOTE: The value must be used as a 32-bit unsigned integer, to deal with
109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          possible wrap around UINT32_MAX for possible large p_vaddr values).
110f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
111f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  And that the phdr0_load_address must start at a page boundary, with
112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  the segment's real content starting at:
113f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
114f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)       phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr)
115f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
116f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  Note that ELF requires the following condition to make the mmap()-ing work:
117f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset)
119f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
120f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  The load_bias must be added to any p_vaddr value read from the ELF file to
121f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  determine the corresponding memory address.
122f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
123f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) **/
124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0)
126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#define PFLAGS_TO_PROT(x)                 \
127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \
128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)   MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)   MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/* Returns the size of the extent of all the possibly non-contiguous
132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * loadable segments in an ELF program header table. This corresponds
133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * to the page-aligned size in bytes that needs to be reserved in the
134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * process' address space. If there are no loadable segments, 0 is
135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * returned.
136f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
137f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * If out_min_vaddr or out_max_vaddr are non-NULL, they will be
138f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * set to the minimum and maximum addresses of pages to be reserved,
139f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * or 0 if there is nothing to load.
140f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
141f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)size_t phdr_table_get_load_size(const ELF::Phdr* phdr_table,
142f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                size_t phdr_count,
143f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                ELF::Addr* out_min_vaddr,
144f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                ELF::Addr* out_max_vaddr) {
145f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ELF::Addr min_vaddr = ~static_cast<ELF::Addr>(0);
146f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ELF::Addr max_vaddr = 0x00000000U;
147f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
148f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool found_pt_load = false;
149f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (size_t i = 0; i < phdr_count; ++i) {
150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const ELF::Phdr* phdr = &phdr_table[i];
151f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
152f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (phdr->p_type != PT_LOAD) {
153f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      continue;
154f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
155f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    found_pt_load = true;
156f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
157f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (phdr->p_vaddr < min_vaddr) {
158f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      min_vaddr = phdr->p_vaddr;
159f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
160f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
161f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
162f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      max_vaddr = phdr->p_vaddr + phdr->p_memsz;
163f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
164f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
165f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!found_pt_load) {
166f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    min_vaddr = 0x00000000U;
167f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
168f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
169f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  min_vaddr = PAGE_START(min_vaddr);
170f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  max_vaddr = PAGE_END(max_vaddr);
171f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
172f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (out_min_vaddr != NULL) {
173f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *out_min_vaddr = min_vaddr;
174f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
175f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (out_max_vaddr != NULL) {
176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *out_max_vaddr = max_vaddr;
177f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return max_vaddr - min_vaddr;
179f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
180f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
181f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/* Used internally. Used to set the protection bits of all loaded segments
182f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * with optional extra flags (i.e. really PROT_WRITE). Used by
183f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * phdr_table_protect_segments and phdr_table_unprotect_segments.
184f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
185f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)static int _phdr_table_set_load_prot(const ELF::Phdr* phdr_table,
186f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                     int phdr_count,
187f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                     ELF::Addr load_bias,
188f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                     int extra_prot_flags) {
189f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const ELF::Phdr* phdr = phdr_table;
190f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const ELF::Phdr* phdr_limit = phdr + phdr_count;
191f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
192f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (; phdr < phdr_limit; phdr++) {
193f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0)
194f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      continue;
195f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
196f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    ELF::Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
197f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    ELF::Addr seg_page_end =
198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
199f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
200f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    int ret = mprotect((void*)seg_page_start,
201f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                       seg_page_end - seg_page_start,
202f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                       PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags);
203f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (ret < 0) {
204f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return -1;
205f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
206f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
207f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return 0;
208f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
209f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
210f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/* Restore the original protection modes for all loadable segments.
211f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * You should only call this after phdr_table_unprotect_segments and
212f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * applying all relocations.
213f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
214f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Input:
215f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_table  -> program header table
216f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_count  -> number of entries in tables
217f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   load_bias   -> load bias
218f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Return:
219f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   0 on error, -1 on failure (error code in errno).
220f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
221f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)int phdr_table_protect_segments(const ELF::Phdr* phdr_table,
222f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                int phdr_count,
223f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                ELF::Addr load_bias) {
224f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0);
225f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
226f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
227f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/* Change the protection of all loaded segments in memory to writable.
228f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * This is useful before performing relocations. Once completed, you
229f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * will have to call phdr_table_protect_segments to restore the original
230f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * protection flags on all segments.
231f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
232f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Note that some writable segments can also have their content turned
233f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * to read-only by calling phdr_table_protect_gnu_relro. This is no
234f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * performed here.
235f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
236f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Input:
237f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_table  -> program header table
238f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_count  -> number of entries in tables
239f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   load_bias   -> load bias
240f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Return:
241f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   0 on error, -1 on failure (error code in errno).
242f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)int phdr_table_unprotect_segments(const ELF::Phdr* phdr_table,
244f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                  int phdr_count,
245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                  ELF::Addr load_bias) {
246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return _phdr_table_set_load_prot(
247f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      phdr_table, phdr_count, load_bias, PROT_WRITE);
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/* Return the extend of the GNU RELRO segment in a program header.
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * On success, return 0 and sets |*relro_start| and |*relro_end|
252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * to the page-aligned extents of the RELRO section.
253f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * On failure, return -1.
254f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
255f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * NOTE: This assumes there is a single PT_GNU_RELRO segment in the
256f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * program header, i.e. it will return the extents of the first entry.
257f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
258f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)int phdr_table_get_relro_info(const ELF::Phdr* phdr_table,
259f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              int phdr_count,
260f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              ELF::Addr load_bias,
261f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              ELF::Addr* relro_start,
262f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              ELF::Addr* relro_size) {
263f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const ELF::Phdr* phdr;
264f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const ELF::Phdr* phdr_limit = phdr_table + phdr_count;
265f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
266f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (phdr = phdr_table; phdr < phdr_limit; ++phdr) {
267f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (phdr->p_type != PT_GNU_RELRO)
268f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      continue;
269f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
270f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    /* Tricky: what happens when the relro segment does not start
271f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     * or end at page boundaries?. We're going to be over-protective
272f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     * here and put every page touched by the segment as read-only.
273f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *
274f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     * This seems to match Ian Lance Taylor's description of the
275f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     * feature at http://www.airs.com/blog/archives/189.
276f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *
277f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     * Extract:
278f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *    Note that the current dynamic linker code will only work
279f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *    correctly if the PT_GNU_RELRO segment starts on a page
280f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *    boundary. This is because the dynamic linker rounds the
281f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *    p_vaddr field down to the previous page boundary. If
282f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *    there is anything on the page which should not be read-only,
283f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *    the program is likely to fail at runtime. So in effect the
284f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *    linker must only emit a PT_GNU_RELRO segment if it ensures
285f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     *    that it starts on a page boundary.
286f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     */
287f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *relro_start = PAGE_START(phdr->p_vaddr) + load_bias;
288f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *relro_size =
289f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias - *relro_start;
290f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return 0;
291f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
292f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
293f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return -1;
294f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
295f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
296f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/* Apply GNU relro protection if specified by the program header. This will
297f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * turn some of the pages of a writable PT_LOAD segment to read-only, as
298f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * specified by one or more PT_GNU_RELRO segments. This must be always
299f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * performed after relocations.
300f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
301f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * The areas typically covered are .got and .data.rel.ro, these are
302f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * read-only from the program's POV, but contain absolute addresses
303f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * that need to be relocated before use.
304f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
305f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Input:
306f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_table  -> program header table
307f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_count  -> number of entries in tables
308f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   load_bias   -> load bias
309f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Return:
310f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   0 on error, -1 on failure (error code in errno).
311f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
312f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)int phdr_table_protect_gnu_relro(const ELF::Phdr* phdr_table,
313f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                 int phdr_count,
314f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                 ELF::Addr load_bias) {
315f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ELF::Addr relro_start, relro_size;
316f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
317f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (phdr_table_get_relro_info(
318f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          phdr_table, phdr_count, load_bias, &relro_start, &relro_size) < 0) {
319f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return -1;
320f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
321f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
322f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return mprotect((void*)relro_start, relro_size, PROT_READ);
323f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
324f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
325f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#ifdef __arm__
326f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
327f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#ifndef PT_ARM_EXIDX
328f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
329f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#endif
330f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
331f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/* Return the address and size of the .ARM.exidx section in memory,
332f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * if present.
333f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
334f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Input:
335f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_table  -> program header table
336f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_count  -> number of entries in tables
337f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   load_bias   -> load bias
338f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Output:
339f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   arm_exidx       -> address of table in memory (NULL on failure).
340f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   arm_exidx_count -> number of items in table (0 on failure).
341f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Return:
342f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   0 on error, -1 on failure (_no_ error code in errno)
343f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
344f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)int phdr_table_get_arm_exidx(const ELF::Phdr* phdr_table,
345f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                             int phdr_count,
346f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                             ELF::Addr load_bias,
347f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                             ELF::Addr** arm_exidx,
348f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                             unsigned* arm_exidx_count) {
349f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const ELF::Phdr* phdr = phdr_table;
350f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const ELF::Phdr* phdr_limit = phdr + phdr_count;
351f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
352f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
353f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (phdr->p_type != PT_ARM_EXIDX)
354f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      continue;
355f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
356f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *arm_exidx = (ELF::Addr*)(load_bias + phdr->p_vaddr);
357f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *arm_exidx_count = (unsigned)(phdr->p_memsz / 8);
358f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return 0;
359f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
360f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  *arm_exidx = NULL;
361f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  *arm_exidx_count = 0;
362f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return -1;
363f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
364f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#endif  // __arm__
365f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
366f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/* Return the address and size of the ELF file's .dynamic section in memory,
367f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * or NULL if missing.
368f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
369f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Input:
370f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_table  -> program header table
371f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   phdr_count  -> number of entries in tables
372f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   load_bias   -> load bias
373f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Output:
374f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   dynamic       -> address of table in memory (NULL on failure).
375f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   dynamic_count -> number of items in table (0 on failure).
376f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   dynamic_flags -> protection flags for section (unset on failure)
377f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Return:
378f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *   void
379f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
380f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void phdr_table_get_dynamic_section(const ELF::Phdr* phdr_table,
381f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                    int phdr_count,
382f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                    ELF::Addr load_bias,
383f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                    const ELF::Dyn** dynamic,
384f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                    size_t* dynamic_count,
385f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                    ELF::Word* dynamic_flags) {
386f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const ELF::Phdr* phdr = phdr_table;
387f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const ELF::Phdr* phdr_limit = phdr + phdr_count;
388f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
389f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
390f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (phdr->p_type != PT_DYNAMIC) {
391f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      continue;
392f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
393f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
394f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *dynamic = reinterpret_cast<const ELF::Dyn*>(load_bias + phdr->p_vaddr);
395f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (dynamic_count) {
396f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      *dynamic_count = (unsigned)(phdr->p_memsz / sizeof(ELF::Dyn));
397f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
398f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (dynamic_flags) {
399f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      *dynamic_flags = phdr->p_flags;
400f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
401f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
402f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
403f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  *dynamic = NULL;
404f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (dynamic_count) {
405f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    *dynamic_count = 0;
406f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
407f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
408