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/text/WTFString.h>
34
35namespace WebKit {
36
37DynamicLinkerEnvironmentExtractor::DynamicLinkerEnvironmentExtractor(NSString *executablePath, cpu_type_t architecture)
38    : m_executablePath(executablePath)
39    , m_architecture(architecture)
40{
41    NSData *mainExecutableData = [NSData dataWithContentsOfFile:m_executablePath.get() options:NSDataReadingMappedIfSafe error:0];
42    if (!mainExecutableData)
43        return;
44
45    const void* mainExecutableBytes = [mainExecutableData bytes];
46    size_t length = [mainExecutableData length];
47    if (length < sizeof(uint32_t))
48        return;
49
50    uint32_t magicValue = *static_cast<const uint32_t*>(mainExecutableBytes);
51    if (magicValue == FAT_MAGIC || magicValue == FAT_CIGAM) {
52        processFatFile(mainExecutableBytes, length);
53        return;
54    }
55
56    processSingleArchitecture(mainExecutableBytes, length);
57}
58
59#define DEFINE_BYTE_SWAPPER(type) inline type byteSwapIfNeeded(const type& data, bool shouldByteSwap) \
60{ \
61    type swapped = data; \
62    if (shouldByteSwap) \
63        swap_##type(&swapped, NX_UnknownByteOrder); \
64    return swapped; \
65}
66
67DEFINE_BYTE_SWAPPER(load_command)
68DEFINE_BYTE_SWAPPER(dylinker_command)
69DEFINE_BYTE_SWAPPER(mach_header)
70DEFINE_BYTE_SWAPPER(mach_header_64)
71
72#undef DEFINE_BYTE_SWAPPER
73
74void DynamicLinkerEnvironmentExtractor::processEnvironmentVariable(const char* environmentString)
75{
76    const char* equalsLocation = strchr(environmentString, '=');
77    if (!equalsLocation)
78        return;
79
80    size_t nameLength = equalsLocation - environmentString;
81    String name(environmentString, nameLength);
82
83    // LC_DYLD_ENVIRONMENT only respects DYLD_*_PATH variables.
84    if (!name.startsWith("DYLD_") || !name.endsWith("_PATH"))
85        return;
86
87    CString value(equalsLocation + 1);
88    m_extractedVariables.append(std::make_pair(name.latin1(), value));
89}
90
91size_t DynamicLinkerEnvironmentExtractor::processLoadCommand(const void* data, size_t length, bool shouldByteSwap)
92{
93    if (length < sizeof(load_command))
94        return 0;
95
96    const load_command* rawLoadCommand = static_cast<const load_command*>(data);
97    load_command loadCommand = byteSwapIfNeeded(*rawLoadCommand, shouldByteSwap);
98
99    if (length < loadCommand.cmdsize)
100        return 0;
101
102    if (loadCommand.cmd == LC_DYLD_ENVIRONMENT) {
103        if (length < sizeof(dylinker_command))
104            return 0;
105
106        dylinker_command environmentCommand = byteSwapIfNeeded(*reinterpret_cast<const dylinker_command*>(rawLoadCommand), shouldByteSwap);
107        if (loadCommand.cmdsize < environmentCommand.name.offset)
108            return 0;
109
110        size_t environmentStringLength = loadCommand.cmdsize - environmentCommand.name.offset;
111        Vector<char, 256> environmentString;
112        environmentString.reserveCapacity(environmentStringLength + 1);
113        environmentString.append(reinterpret_cast<const char*>(rawLoadCommand) + environmentCommand.name.offset, environmentStringLength);
114        environmentString.append(0);
115
116        processEnvironmentVariable(environmentString.data());
117    }
118
119    return loadCommand.cmdsize;
120}
121
122void DynamicLinkerEnvironmentExtractor::processLoadCommands(const void* data, size_t length, int32_t numberOfCommands, bool shouldByteSwap)
123{
124    const void* dataRemaining = data;
125    size_t lengthRemaining = length;
126    for (int i = 0; i < numberOfCommands; i++) {
127        size_t commandLength = processLoadCommand(dataRemaining, lengthRemaining, shouldByteSwap);
128        if (!commandLength || lengthRemaining < commandLength)
129            return;
130
131        dataRemaining = static_cast<const char*>(dataRemaining) + commandLength;
132        lengthRemaining -= commandLength;
133    }
134}
135
136void DynamicLinkerEnvironmentExtractor::processSingleArchitecture(const void* data, size_t length)
137{
138    if (length < sizeof(mach_header))
139        return;
140
141    const mach_header* header = static_cast<const mach_header*>(data);
142    if (header->magic == MH_MAGIC || header->magic == MH_CIGAM) {
143        bool shouldByteSwap = header->magic == MH_CIGAM;
144        mach_header swappedHeader = byteSwapIfNeeded(*header, shouldByteSwap);
145        if (swappedHeader.cputype == m_architecture)
146            processLoadCommands(static_cast<const char*>(data) + sizeof(*header), length - sizeof(*header), swappedHeader.ncmds, shouldByteSwap);
147        return;
148    }
149
150    if (length < sizeof(mach_header_64))
151        return;
152
153    const mach_header_64* header64 = static_cast<const mach_header_64*>(data);
154    bool shouldByteSwap = header->magic == MH_CIGAM_64;
155    mach_header_64 swappedHeader64 = byteSwapIfNeeded(*header64, shouldByteSwap);
156    if (swappedHeader64.cputype == m_architecture)
157        processLoadCommands(static_cast<const char*>(data) + sizeof(*header64), length - sizeof(*header64), swappedHeader64.ncmds, shouldByteSwap);
158}
159
160void DynamicLinkerEnvironmentExtractor::processFatFile(const void* data, size_t length)
161{
162    if (length < sizeof(fat_header))
163        return;
164
165    const fat_header* header = static_cast<const fat_header*>(data);
166
167    size_t numberOfArchitectures = OSSwapBigToHostInt32(header->nfat_arch);
168
169    // Ensure that we have enough data remaining for numberOfArchitectures fat_arch structs.
170    if ((length - sizeof(fat_header)) / sizeof(fat_arch) < numberOfArchitectures)
171        return;
172
173    const fat_arch* archs = reinterpret_cast<const fat_arch*>(reinterpret_cast<const char*>(data) + sizeof(*header));
174    for (uint32_t i = 0; i < numberOfArchitectures; i++) {
175        uint32_t architectureOffset = OSSwapBigToHostInt32(archs[i].offset);
176        uint32_t architectureSize = OSSwapBigToHostInt32(archs[i].size);
177        if (length < architectureOffset + architectureSize)
178            return;
179
180        processSingleArchitecture(static_cast<const char*>(data) + architectureOffset, architectureSize);
181    }
182}
183
184void DynamicLinkerEnvironmentExtractor::getExtractedEnvironmentVariables(EnvironmentVariables& environmentVariables) const
185{
186    size_t extractedVariableCount = m_extractedVariables.size();
187    for (size_t i = 0; i < extractedVariableCount; ++i) {
188        const CString& name = m_extractedVariables[i].first;
189
190        // Preserve any existing environment variable by this name so that it will take
191        // precedence over what we extracted from the executable file.
192        if (environmentVariables.get(name.data()))
193            continue;
194
195        environmentVariables.set(name.data(), m_extractedVariables[i].second.data());
196    }
197}
198
199} // namespace WebKit
200