1c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/*
2c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Copyright (C) 2012 The Android Open Source Project
3c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * All rights reserved.
4c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
5c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Redistribution and use in source and binary forms, with or without
6c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * modification, are permitted provided that the following conditions
7c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * are met:
8c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *  * Redistributions of source code must retain the above copyright
9c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *    notice, this list of conditions and the following disclaimer.
10c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *  * Redistributions in binary form must reproduce the above copyright
11c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *    notice, this list of conditions and the following disclaimer in
12c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *    the documentation and/or other materials provided with the
13c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *    distribution.
14c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
15c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * SUCH DAMAGE.
27c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
28c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
29c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#include <errno.h>
30c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#include <sys/mman.h>
31c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
32c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#include "linker_phdr.h"
33c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
34c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/**
35c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  TECHNICAL NOTE ON ELF LOADING.
36c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
37c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  An ELF file's program header table contains one or more PT_LOAD
38c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  segments, which corresponds to portions of the file that need to
39c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  be mapped into the process' address space.
40c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
41c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  Each loadable segment has the following important properties:
42c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
43c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    p_offset  -> segment file offset
44c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    p_filesz  -> segment file size
45c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    p_memsz   -> segment memory size (always >= p_filesz)
46c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    p_vaddr   -> segment's virtual address
47c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    p_flags   -> segment flags (e.g. readable, writable, executable)
48c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
49c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  We will ignore the p_paddr and p_align fields of Elf32_Phdr for now.
50c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
51c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz)
52c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  ranges of virtual addresses. A few rules apply:
53c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
54c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  - the virtual address ranges should not overlap.
55c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
56c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  - if a segment's p_filesz is smaller than its p_memsz, the extra bytes
57c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    between them should always be initialized to 0.
58c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
59c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  - ranges do not necessarily start or end at page boundaries. Two distinct
60c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    segments can have their start and end on the same page. In this case, the
61c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    page inherits the mapping flags of the latter segment.
62c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
63c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  Finally, the real load addrs of each segment is not p_vaddr. Instead the
64c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  loader decides where to load the first segment, then will load all others
65c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  relative to the first one to respect the initial range layout.
66c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
67c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  For example, consider the following list:
68c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
69c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    [ offset:0,      filesz:0x4000, memsz:0x4000, vaddr:0x30000 ],
70c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ],
71c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
72c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  This corresponds to two segments that cover these virtual address ranges:
73c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
74c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner       0x30000...0x34000
75c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner       0x40000...0x48000
76c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
77c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  If the loader decides to load the first segment at address 0xa0000000
78c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  then the segments' load address ranges will be:
79c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
80c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner       0xa0030000...0xa0034000
81c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner       0xa0040000...0xa0048000
82c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
83c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  In other words, all segments must be loaded at an address that has the same
84c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  constant offset from their p_vaddr value. This offset is computed as the
85c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  difference between the first segment's load address, and its p_vaddr value.
86c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
87c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  However, in practice, segments do _not_ start at page boundaries. Since we
88c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  can only memory-map at page boundaries, this means that the bias is
89c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  computed as:
90c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
91c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner       load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr)
92c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
93c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  (NOTE: The value must be used as a 32-bit unsigned integer, to deal with
94c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner          possible wrap around UINT32_MAX for possible large p_vaddr values).
95c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
96c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  And that the phdr0_load_address must start at a page boundary, with
97c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  the segment's real content starting at:
98c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
99c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner       phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr)
100c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
101c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  Note that ELF requires the following condition to make the mmap()-ing work:
102c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
103c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner      PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset)
104c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
105c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  The load_bias must be added to any p_vaddr value read from the ELF file to
106c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner  determine the corresponding memory address.
107c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
108c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner **/
109c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
110c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#define MAYBE_MAP_FLAG(x,from,to)    (((x) & (from)) ? (to) : 0)
111c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#define PFLAGS_TO_PROT(x)            (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \
112c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                      MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
113c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                      MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
114c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
115c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Load the program header table from an ELF file into a read-only private
116c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * anonymous mmap-ed block.
117c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
118c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
119c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   fd           -> file descriptor
120c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_offset  -> file offset of phdr table
121c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_num     -> number of entries in the table.
122c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
123c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Output:
124c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_mmap    -> address of mmap block in memory.
125c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_memsize -> size of mmap block in memory.
126c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table   -> address of first entry in memory.
127c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
128c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
129c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   -1 on error, or 0 on success.
130c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
131c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint phdr_table_load(int                fd,
132c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                    Elf32_Addr         phdr_offset,
133c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                    Elf32_Half         phdr_num,
134c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                    void**             phdr_mmap,
135c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                    Elf32_Addr*        phdr_size,
136c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                    const Elf32_Phdr** phdr_table)
137c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
138c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    Elf32_Addr  page_min, page_max, page_offset;
139c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    void*       mmap_result;
140c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
141c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    /* Just like the kernel, we only accept program header tables that
142c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner     * are smaller than 64KB. */
143c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    if (phdr_num < 1 || phdr_num > 65536/sizeof(Elf32_Phdr)) {
144c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        errno = EINVAL;
145c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        return -1;
146c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
147c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
148c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    page_min = PAGE_START(phdr_offset);
149c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    page_max = PAGE_END(phdr_offset + phdr_num*sizeof(Elf32_Phdr));
150c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    page_offset = PAGE_OFFSET(phdr_offset);
151c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
152c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    mmap_result = mmap(NULL,
153c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                       page_max - page_min,
154c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                       PROT_READ,
155c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                       MAP_PRIVATE,
156c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                       fd,
157c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                       page_min);
158c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
159c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    if (mmap_result == MAP_FAILED) {
160c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        return -1;
161c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
162c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
163c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    *phdr_mmap = mmap_result;
164c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    *phdr_size = page_max - page_min;
165c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    *phdr_table = (Elf32_Phdr*)((char*)mmap_result + page_offset);
166c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
167c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return 0;
168c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
169c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
170c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnervoid phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize)
171c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
172c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    munmap(phdr_mmap, phdr_memsize);
173c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
174c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
175c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
176c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Compute the extent of all loadable segments in an ELF program header
177c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * table. This corresponds to the page-aligned size in bytes that needs to be
178c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * reserved in the process' address space
179c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
180c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This returns 0 if there are no loadable segments.
181c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
182c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' TurnerElf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
1834688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes                                    size_t phdr_count)
184c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
185c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    Elf32_Addr min_vaddr = 0xFFFFFFFFU;
186c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    Elf32_Addr max_vaddr = 0x00000000U;
187c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
1884688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes    for (size_t i = 0; i < phdr_count; ++i) {
1894688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes        const Elf32_Phdr* phdr = &phdr_table[i];
190c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
1914688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes        if (phdr->p_type != PT_LOAD) {
192c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            continue;
1934688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes        }
194c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
1954688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes        if (phdr->p_vaddr < min_vaddr) {
196c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            min_vaddr = phdr->p_vaddr;
1974688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes        }
198c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
1994688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes        if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
200c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            max_vaddr = phdr->p_vaddr + phdr->p_memsz;
2014688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes        }
202c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
203c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
204c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    if (min_vaddr > max_vaddr) {
205c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        return 0;
206c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
207c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
208c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    min_vaddr = PAGE_START(min_vaddr);
209c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    max_vaddr = PAGE_END(max_vaddr);
210c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
211c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return max_vaddr - min_vaddr;
212c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
213c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
214c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Reserve a virtual address range big enough to hold all loadable
215c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * segments of a program header table. This is done by creating a
216c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * private anonymous mmap() with PROT_NONE.
217c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
218c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
219c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table    -> program header table
220c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_count    -> number of entries in the tables
221c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Output:
222c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_start    -> first page of reserved address space range
223c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_size     -> size in bytes of reserved address space range
224c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_bias     -> load bias, as described in technical note above.
225c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
226c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
227c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   0 on success, -1 otherwise. Error code in errno.
228c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
229c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint
230c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
2314688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes                          size_t phdr_count,
2324688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes                          void** load_start,
2334688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes                          Elf32_Addr* load_size,
2344688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes                          Elf32_Addr* load_bias)
235c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
236c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count);
237c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    if (size == 0) {
238c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        errno = EINVAL;
239c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        return -1;
240c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
241c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
2424688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes    int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
2434688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes    void* start = mmap(NULL, size, PROT_NONE, mmap_flags, -1, 0);
244c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    if (start == MAP_FAILED) {
245c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        return -1;
246c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
247c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
248c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    *load_start = start;
249c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    *load_size  = size;
250c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    *load_bias  = 0;
251c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
2524688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes    for (size_t i = 0; i < phdr_count; ++i) {
2534688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughes        const Elf32_Phdr* phdr = &phdr_table[i];
254c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (phdr->p_type == PT_LOAD) {
255c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            *load_bias = (Elf32_Addr)start - PAGE_START(phdr->p_vaddr);
256c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            break;
257c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
258c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
259c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return 0;
260c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
261c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
262c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Map all loadable segments in process' address space.
263c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This assumes you already called phdr_table_reserve_memory to
264c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * reserve the address space range for the library.
265c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
266c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
267c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table    -> program header table
268c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_count    -> number of entries in the table
269c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_bias     -> load offset.
270c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   fd            -> input file descriptor.
271c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
272c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
273c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   0 on success, -1 otherwise. Error code in errno.
274c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
275c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint
276c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_load_segments(const Elf32_Phdr* phdr_table,
277c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                         int               phdr_count,
278c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                         Elf32_Addr        load_bias,
279c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                         int               fd)
280c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
281c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    int nn;
282c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
283c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    for (nn = 0; nn < phdr_count; nn++) {
284c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        const Elf32_Phdr* phdr = &phdr_table[nn];
285c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        void* seg_addr;
286c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
287c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (phdr->p_type != PT_LOAD)
288c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            continue;
289c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
290c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        /* Segment addresses in memory */
291c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_start = phdr->p_vaddr + load_bias;
292c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_end   = seg_start + phdr->p_memsz;
293c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
294c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_page_start = PAGE_START(seg_start);
295c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_page_end   = PAGE_END(seg_end);
296c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
297c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_file_end   = seg_start + phdr->p_filesz;
298c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
299c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        /* File offsets */
300c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr file_start = phdr->p_offset;
301c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr file_end   = file_start + phdr->p_filesz;
302c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
303c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr file_page_start = PAGE_START(file_start);
304c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr file_page_end   = PAGE_END(file_end);
305c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
306c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        seg_addr = mmap((void*)seg_page_start,
307c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                        file_end - file_page_start,
308c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                        PFLAGS_TO_PROT(phdr->p_flags),
309c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                        MAP_FIXED|MAP_PRIVATE,
310c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                        fd,
311c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                        file_page_start);
312c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
313c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (seg_addr == MAP_FAILED) {
314c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            return -1;
315c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
316c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
317c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        /* if the segment is writable, and does not end on a page boundary,
318c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * zero-fill it until the page limit. */
319c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) {
320c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            memset((void*)seg_file_end, 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end));
321c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
322c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
323c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        seg_file_end = PAGE_END(seg_file_end);
324c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
325c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        /* seg_file_end is now the first page address after the file
326c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * content. If seg_end is larger, we need to zero anything
327c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * between them. This is done by using a private anonymous
328c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * map for all extra pages.
329c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         */
330c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (seg_page_end > seg_file_end) {
331c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            void* zeromap = mmap((void*)seg_file_end,
332c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                    seg_page_end - seg_file_end,
333c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                    PFLAGS_TO_PROT(phdr->p_flags),
334c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                    MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE,
335c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                    -1,
336c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                    0);
337c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            if (zeromap == MAP_FAILED) {
338c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                return -1;
339c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            }
340c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
341c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
342c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return 0;
343c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
344c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
345105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes/* Used internally. Used to set the protection bits of all loaded segments
346c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * with optional extra flags (i.e. really PROT_WRITE). Used by
347c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table_protect_segments and phdr_table_unprotect_segments.
348c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
349c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerstatic int
350c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner_phdr_table_set_load_prot(const Elf32_Phdr* phdr_table,
351c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                          int               phdr_count,
352c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                          Elf32_Addr        load_bias,
353c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                          int               extra_prot_flags)
354c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
355c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr = phdr_table;
356c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr_limit = phdr + phdr_count;
357c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
358c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    for (; phdr < phdr_limit; phdr++) {
359c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0)
360c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            continue;
361c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
362c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
363c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_page_end   = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
364c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
365c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        int ret = mprotect((void*)seg_page_start,
366c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                           seg_page_end - seg_page_start,
367c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                           PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags);
368c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (ret < 0) {
369c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            return -1;
370c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
371c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
372c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return 0;
373c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
374c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
375c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Restore the original protection modes for all loadable segments.
376c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * You should only call this after phdr_table_unprotect_segments and
377c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * applying all relocations.
378c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
379c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
380c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table  -> program header table
381105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes *   phdr_count  -> number of entries in tables
382c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_bias   -> load bias
383c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
384c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   0 on error, -1 on failure (error code in errno).
385c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
386c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint
387c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_protect_segments(const Elf32_Phdr* phdr_table,
388c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                            int               phdr_count,
389c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                            Elf32_Addr        load_bias)
390c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
391c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return _phdr_table_set_load_prot(phdr_table, phdr_count,
392c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                      load_bias, 0);
393c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
394c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
395c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Change the protection of all loaded segments in memory to writable.
396c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * This is useful before performing relocations. Once completed, you
397c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * will have to call phdr_table_protect_segments to restore the original
398c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * protection flags on all segments.
399c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
400c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Note that some writable segments can also have their content turned
401c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * to read-only by calling phdr_table_protect_gnu_relro. This is no
402c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * performed here.
403c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
404c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
405c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table  -> program header table
406105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes *   phdr_count  -> number of entries in tables
407c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_bias   -> load bias
408c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
409c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   0 on error, -1 on failure (error code in errno).
410c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
411c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint
412c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_unprotect_segments(const Elf32_Phdr* phdr_table,
413c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                              int               phdr_count,
414c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                              Elf32_Addr        load_bias)
415c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
416c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return _phdr_table_set_load_prot(phdr_table, phdr_count,
417c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                      load_bias, PROT_WRITE);
418c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
419c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
420c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Used internally by phdr_table_protect_gnu_relro and
421c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * phdr_table_unprotect_gnu_relro.
422c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
423c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerstatic int
424c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner_phdr_table_set_gnu_relro_prot(const Elf32_Phdr* phdr_table,
425c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                               int               phdr_count,
426c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                               Elf32_Addr        load_bias,
427c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                               int               prot_flags)
428c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
429c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr = phdr_table;
430c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr_limit = phdr + phdr_count;
431c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
432c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
433c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (phdr->p_type != PT_GNU_RELRO)
434c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            continue;
435c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
436c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        /* Tricky: what happens when the relro segment does not start
437c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * or end at page boundaries?. We're going to be over-protective
438c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * here and put every page touched by the segment as read-only.
439c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *
440c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * This seems to match Ian Lance Taylor's description of the
441c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * feature at http://www.airs.com/blog/archives/189.
442c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *
443c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         * Extract:
444c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *    Note that the current dynamic linker code will only work
445c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *    correctly if the PT_GNU_RELRO segment starts on a page
446c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *    boundary. This is because the dynamic linker rounds the
447c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *    p_vaddr field down to the previous page boundary. If
448c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *    there is anything on the page which should not be read-only,
449c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *    the program is likely to fail at runtime. So in effect the
450c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *    linker must only emit a PT_GNU_RELRO segment if it ensures
451c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         *    that it starts on a page boundary.
452c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner         */
453c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
454c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_page_end   = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
455c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
456c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        int ret = mprotect((void*)seg_page_start,
457c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                           seg_page_end - seg_page_start,
458c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                           prot_flags);
459c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (ret < 0) {
460c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            return -1;
461c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
462c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
463c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return 0;
464c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
465c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
466c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Apply GNU relro protection if specified by the program header. This will
467c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * turn some of the pages of a writable PT_LOAD segment to read-only, as
468c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * specified by one or more PT_GNU_RELRO segments. This must be always
469c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * performed after relocations.
470c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
47112c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * The areas typically covered are .got and .data.rel.ro, these are
47212c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * read-only from the program's POV, but contain absolute addresses
47312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * that need to be relocated before use.
474c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
475c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
476c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table  -> program header table
477105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes *   phdr_count  -> number of entries in tables
478c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_bias   -> load bias
479c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
480c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   0 on error, -1 on failure (error code in errno).
481c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
482c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint
483c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_protect_gnu_relro(const Elf32_Phdr* phdr_table,
484c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                             int               phdr_count,
485c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                             Elf32_Addr        load_bias)
486c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
487c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return _phdr_table_set_gnu_relro_prot(phdr_table,
488c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                          phdr_count,
489c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                          load_bias,
490c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                                          PROT_READ);
491c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
492c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
493c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#ifdef ANDROID_ARM_LINKER
494c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
495c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#  ifndef PT_ARM_EXIDX
496c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#    define PT_ARM_EXIDX    0x70000001      /* .ARM.exidx segment */
497c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#  endif
498c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
499c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Return the address and size of the .ARM.exidx section in memory,
500c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * if present.
501c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
502c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
503c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table  -> program header table
504105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes *   phdr_count  -> number of entries in tables
505c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_bias   -> load bias
506c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Output:
507c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   arm_exidx       -> address of table in memory (NULL on failure).
508c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   arm_exidx_count -> number of items in table (0 on failure).
509c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
510c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   0 on error, -1 on failure (_no_ error code in errno)
511c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
512c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerint
513c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_get_arm_exidx(const Elf32_Phdr* phdr_table,
514c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                         int               phdr_count,
515c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                         Elf32_Addr        load_bias,
516c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                         Elf32_Addr**      arm_exidx,
517c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                         unsigned*         arm_exidx_count)
518c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
519c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr = phdr_table;
520c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr_limit = phdr + phdr_count;
521c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
522c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
523c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (phdr->p_type != PT_ARM_EXIDX)
524c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            continue;
525c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
526c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        *arm_exidx = (Elf32_Addr*)(load_bias + phdr->p_vaddr);
527c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        *arm_exidx_count = (unsigned)(phdr->p_memsz / 8);
528c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        return 0;
529c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
530c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    *arm_exidx = NULL;
531c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    *arm_exidx_count = 0;
532c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return -1;
533c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
534c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner#endif /* ANDROID_ARM_LINKER */
535c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
53612c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel/* Return the address and size of the ELF file's .dynamic section in memory,
537c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * or NULL if missing.
538c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
539c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
540c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table  -> program header table
541105bc26fa6e5f6a946a2ff144ae316e69c6ce08eElliott Hughes *   phdr_count  -> number of entries in tables
542c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_bias   -> load bias
54312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel * Output:
54412c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel *   dynamic       -> address of table in memory (NULL on failure).
54512c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel *   dynamic_count -> number of items in table (0 on failure).
546c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
54712c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel *   void
548c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
54912c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvelvoid
550c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table,
551c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                               int               phdr_count,
55212c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel                               Elf32_Addr        load_bias,
55312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel                               Elf32_Addr**      dynamic,
55412c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel                               size_t*           dynamic_count)
555c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
556c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr = phdr_table;
557c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr_limit = phdr + phdr_count;
558c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
559c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
56012c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel        if (phdr->p_type != PT_DYNAMIC) {
56112c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel            continue;
562c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
56312c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel
56412c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel        *dynamic = (Elf32_Addr*)(load_bias + phdr->p_vaddr);
56512c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel        if (dynamic_count) {
56612c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel            *dynamic_count = (unsigned)(phdr->p_memsz / 8);
56712c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel        }
56812c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel        return;
56912c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel    }
57012c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel    *dynamic = NULL;
57112c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel    if (dynamic_count) {
57212c78bbded8ec03f821dfa09174464c04836e4eaArd Biesheuvel        *dynamic_count = 0;
573c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
574c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
575c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
576c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner/* Return the address of the program header table as it appears in the loaded
577c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * segments in memory. This is in contrast with the input 'phdr_table' which
578c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * is temporary and will be released before the library is relocated.
579c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *
580c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Input:
581c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_table  -> program header table
582c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   phdr_count  -> number of entries in tables
583c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   load_bias   -> load bias
584c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner * Return:
585c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   Address of loaded program header table on success (it has
586c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner *   'phdr_count' entries), or NULL on failure (no error code).
587c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner */
588c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerconst Elf32_Phdr*
589c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turnerphdr_table_get_loaded_phdr(const Elf32_Phdr*   phdr_table,
590c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                           int                 phdr_count,
591c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                           Elf32_Addr          load_bias)
592c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner{
593c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr = phdr_table;
594c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    const Elf32_Phdr* phdr_limit = phdr + phdr_count;
595c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    Elf32_Addr  loaded = 0;
596c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    Elf32_Addr  loaded_end;
597c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
598c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    /* If there is a PT_PHDR, use it directly */
599c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
600c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (phdr->p_type == PT_PHDR) {
601c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            loaded = load_bias + phdr->p_vaddr;
602c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            goto CHECK;
603c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
604c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
605c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
606c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    /* Otherwise, check the first loadable segment. If its file offset
607c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner     * is 0, it starts with the ELF header, and we can trivially find the
608c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner     * loaded program header from it. */
609c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
610c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (phdr->p_type == PT_LOAD) {
611c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            if (phdr->p_offset == 0) {
612c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                Elf32_Addr  elf_addr = load_bias + phdr->p_vaddr;
613c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                const Elf32_Ehdr* ehdr = (const Elf32_Ehdr*)(void*)elf_addr;
614c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                Elf32_Addr  offset = ehdr->e_phoff;
615c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                loaded = (Elf32_Addr)ehdr + offset;
616c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner                goto CHECK;
617c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            }
618c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            break;
619c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
620c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
621c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
622c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    /* We didn't find it, let the client know. He may be able to
623c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner     * keep a copy of the input phdr_table instead. */
624c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return NULL;
625c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
626c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' TurnerCHECK:
627c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    /* Ensure that our program header is actually within a loadable
628c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner     * segment. This should help catch badly-formed ELF files that
629c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner     * would cause the linker to crash later when trying to access it.
630c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner     */
631c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    loaded_end = loaded + phdr_count*sizeof(Elf32_Phdr);
632c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
633c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
634c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (phdr->p_type != PT_LOAD)
635c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            continue;
636c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_start = phdr->p_vaddr + load_bias;
637c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        Elf32_Addr seg_end   = phdr->p_filesz + seg_start;
638c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner
639c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        if (seg_start <= loaded && loaded_end <= seg_end) {
640c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner            return (const Elf32_Phdr*)loaded;
641c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner        }
642c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    }
643c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner    return NULL;
644c1bd559d5b0fdcc25db2b6ae2705914103b24699David 'Digit' Turner}
645