1/*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DynamicLinkerEnvironmentExtractor.h"
28
29#include "EnvironmentVariables.h"
30#include <mach-o/loader.h>
31#include <mach-o/swap.h>
32#include <wtf/OwnPtr.h>
33#include <wtf/PassOwnPtr.h>
34#include <wtf/text/WTFString.h>
35
36namespace WebKit {
37
38DynamicLinkerEnvironmentExtractor::DynamicLinkerEnvironmentExtractor(NSString *executablePath, cpu_type_t architecture)
39    : m_executablePath(executablePath)
40    , m_architecture(architecture)
41{
42    NSData *mainExecutableData = [NSData dataWithContentsOfFile:m_executablePath.get() options:NSDataReadingMappedIfSafe error:0];
43    if (!mainExecutableData)
44        return;
45
46    const void* mainExecutableBytes = [mainExecutableData bytes];
47    size_t length = [mainExecutableData length];
48    if (length < sizeof(uint32_t))
49        return;
50
51    uint32_t magicValue = *static_cast<const uint32_t*>(mainExecutableBytes);
52    if (magicValue == FAT_MAGIC || magicValue == FAT_CIGAM) {
53        processFatFile(mainExecutableBytes, length);
54        return;
55    }
56
57    processSingleArchitecture(mainExecutableBytes, length);
58}
59
60#define DEFINE_BYTE_SWAPPER(type) inline type byteSwapIfNeeded(const type& data, bool shouldByteSwap) \
61{ \
62    type swapped = data; \
63    if (shouldByteSwap) \
64        swap_##type(&swapped, NX_UnknownByteOrder); \
65    return swapped; \
66}
67
68DEFINE_BYTE_SWAPPER(load_command)
69DEFINE_BYTE_SWAPPER(dylinker_command)
70DEFINE_BYTE_SWAPPER(mach_header)
71DEFINE_BYTE_SWAPPER(mach_header_64)
72
73#undef DEFINE_BYTE_SWAPPER
74
75void DynamicLinkerEnvironmentExtractor::processEnvironmentVariable(const char* environmentString)
76{
77    const char* equalsLocation = strchr(environmentString, '=');
78    if (!equalsLocation)
79        return;
80
81    size_t nameLength = equalsLocation - environmentString;
82    String name(environmentString, nameLength);
83
84    // LC_DYLD_ENVIRONMENT only respects DYLD_*_PATH variables.
85    if (!name.startsWith("DYLD_") || !name.endsWith("_PATH"))
86        return;
87
88    CString value(equalsLocation + 1);
89    m_extractedVariables.append(std::make_pair(name.latin1(), value));
90}
91
92size_t DynamicLinkerEnvironmentExtractor::processLoadCommand(const void* data, size_t length, bool shouldByteSwap)
93{
94    if (length < sizeof(load_command))
95        return 0;
96
97    const load_command* rawLoadCommand = static_cast<const load_command*>(data);
98    load_command loadCommand = byteSwapIfNeeded(*rawLoadCommand, shouldByteSwap);
99
100    if (length < loadCommand.cmdsize)
101        return 0;
102
103    if (loadCommand.cmd == LC_DYLD_ENVIRONMENT) {
104        if (length < sizeof(dylinker_command))
105            return 0;
106
107        dylinker_command environmentCommand = byteSwapIfNeeded(*reinterpret_cast<const dylinker_command*>(rawLoadCommand), shouldByteSwap);
108        if (loadCommand.cmdsize < environmentCommand.name.offset)
109            return 0;
110
111        size_t environmentStringLength = loadCommand.cmdsize - environmentCommand.name.offset;
112        Vector<char, 256> environmentString;
113        environmentString.reserveCapacity(environmentStringLength + 1);
114        environmentString.append(reinterpret_cast<const char*>(rawLoadCommand) + environmentCommand.name.offset, environmentStringLength);
115        environmentString.append(0);
116
117        processEnvironmentVariable(environmentString.data());
118    }
119
120    return loadCommand.cmdsize;
121}
122
123void DynamicLinkerEnvironmentExtractor::processLoadCommands(const void* data, size_t length, int32_t numberOfCommands, bool shouldByteSwap)
124{
125    const void* dataRemaining = data;
126    size_t lengthRemaining = length;
127    for (int i = 0; i < numberOfCommands; i++) {
128        size_t commandLength = processLoadCommand(dataRemaining, lengthRemaining, shouldByteSwap);
129        if (!commandLength || lengthRemaining < commandLength)
130            return;
131
132        dataRemaining = static_cast<const char*>(dataRemaining) + commandLength;
133        lengthRemaining -= commandLength;
134    }
135}
136
137void DynamicLinkerEnvironmentExtractor::processSingleArchitecture(const void* data, size_t length)
138{
139    if (length < sizeof(mach_header))
140        return;
141
142    const mach_header* header = static_cast<const mach_header*>(data);
143    if (header->magic == MH_MAGIC || header->magic == MH_CIGAM) {
144        bool shouldByteSwap = header->magic == MH_CIGAM;
145        mach_header swappedHeader = byteSwapIfNeeded(*header, shouldByteSwap);
146        if (swappedHeader.cputype == m_architecture)
147            processLoadCommands(static_cast<const char*>(data) + sizeof(*header), length - sizeof(*header), swappedHeader.ncmds, shouldByteSwap);
148        return;
149    }
150
151    if (length < sizeof(mach_header_64))
152        return;
153
154    const mach_header_64* header64 = static_cast<const mach_header_64*>(data);
155    bool shouldByteSwap = header->magic == MH_CIGAM_64;
156    mach_header_64 swappedHeader64 = byteSwapIfNeeded(*header64, shouldByteSwap);
157    if (swappedHeader64.cputype == m_architecture)
158        processLoadCommands(static_cast<const char*>(data) + sizeof(*header64), length - sizeof(*header64), swappedHeader64.ncmds, shouldByteSwap);
159}
160
161void DynamicLinkerEnvironmentExtractor::processFatFile(const void* data, size_t length)
162{
163    if (length < sizeof(fat_header))
164        return;
165
166    const fat_header* header = static_cast<const fat_header*>(data);
167
168    size_t numberOfArchitectures = OSSwapBigToHostInt32(header->nfat_arch);
169
170    // Ensure that we have enough data remaining for numberOfArchitectures fat_arch structs.
171    if ((length - sizeof(fat_header)) / sizeof(fat_arch) < numberOfArchitectures)
172        return;
173
174    const fat_arch* archs = reinterpret_cast<const fat_arch*>(reinterpret_cast<const char*>(data) + sizeof(*header));
175    for (uint32_t i = 0; i < numberOfArchitectures; i++) {
176        uint32_t architectureOffset = OSSwapBigToHostInt32(archs[i].offset);
177        uint32_t architectureSize = OSSwapBigToHostInt32(archs[i].size);
178        if (length < architectureOffset + architectureSize)
179            return;
180
181        processSingleArchitecture(static_cast<const char*>(data) + architectureOffset, architectureSize);
182    }
183}
184
185void DynamicLinkerEnvironmentExtractor::getExtractedEnvironmentVariables(EnvironmentVariables& environmentVariables) const
186{
187    size_t extractedVariableCount = m_extractedVariables.size();
188    for (size_t i = 0; i < extractedVariableCount; ++i) {
189        const CString& name = m_extractedVariables[i].first;
190
191        // Preserve any existing environment variable by this name so that it will take
192        // precedence over what we extracted from the executable file.
193        if (environmentVariables.get(name.data()))
194            continue;
195
196        environmentVariables.set(name.data(), m_extractedVariables[i].second.data());
197    }
198}
199
200} // namespace WebKit
201