source: CLRX/CLRadeonExtender/trunk/amdbin/ROCmBinaries.cpp @ 2568

Last change on this file since 2568 was 2568, checked in by matszpk, 3 years ago

CLRadeonExtender: ROCmBinGen: Set physAddr and virtAddr to program headers.

File size: 13.6 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2016 Mateusz Szpakowski
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2.1 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <CLRX/Config.h>
21#include <cassert>
22#include <cstdint>
23#include <algorithm>
24#include <utility>
25#include <CLRX/amdbin/ElfBinaries.h>
26#include <CLRX/utils/Utilities.h>
27#include <CLRX/utils/MemAccess.h>
28#include <CLRX/utils/InputOutput.h>
29#include <CLRX/utils/Containers.h>
30#include <CLRX/amdbin/ROCmBinaries.h>
31
32using namespace CLRX;
33
34/* TODO: add support for various kernel code offset (now only 256 is supported) */
35
36ROCmBinary::ROCmBinary(size_t binaryCodeSize, cxbyte* binaryCode, Flags creationFlags)
37        : ElfBinary64(binaryCodeSize, binaryCode, creationFlags),
38          regionsNum(0), codeSize(0), code(nullptr)
39{
40    cxuint textIndex = SHN_UNDEF;
41    try
42    { textIndex = getSectionIndex(".text"); }
43    catch(const Exception& ex)
44    { } // ignore failed
45    uint64_t codeOffset = 0;
46    if (textIndex!=SHN_UNDEF)
47    {
48        code = getSectionContent(textIndex);
49        const Elf64_Shdr& textShdr = getSectionHeader(textIndex);
50        codeSize = ULEV(textShdr.sh_size);
51        codeOffset = ULEV(textShdr.sh_offset);
52    }
53   
54    regionsNum = 0;
55    const size_t symbolsNum = getSymbolsNum();
56    for (size_t i = 0; i < symbolsNum; i++)
57    {   // count regions number
58        const Elf64_Sym& sym = getSymbol(i);
59        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
60        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
61        if (sym.st_shndx==textIndex &&
62            (symType==STT_GNU_IFUNC || (bind==STB_GLOBAL && symType==STT_OBJECT)))
63            regionsNum++;
64    }
65    if (code==nullptr && regionsNum!=0)
66        throw Exception("No code if regions number is not zero");
67    regions.reset(new ROCmRegion[regionsNum]);
68    size_t j = 0;
69    typedef std::pair<uint64_t, size_t> RegionOffsetEntry;
70    std::unique_ptr<RegionOffsetEntry[]> symOffsets(new RegionOffsetEntry[regionsNum]);
71   
72    for (size_t i = 0; i < symbolsNum; i++)
73    {
74        const Elf64_Sym& sym = getSymbol(i);
75        if (sym.st_shndx!=textIndex)
76            continue;   // if not in '.text' section
77        const size_t value = ULEV(sym.st_value);
78        if (value < codeOffset)
79            throw Exception("Region offset is too small!");
80        const size_t size = ULEV(sym.st_size);
81       
82        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
83        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
84        if (symType==STT_GNU_IFUNC || (bind==STB_GLOBAL && symType==STT_OBJECT))
85        {
86            const bool isKernel = (symType==STT_GNU_IFUNC);
87            symOffsets[j] = std::make_pair(value, j);
88            if (isKernel && value+0x100 > codeOffset+codeSize)
89                throw Exception("Kernel offset is too big!");
90            regions[j++] = { getSymbolName(i), size, value, isKernel };
91        }
92    }
93    std::sort(symOffsets.get(), symOffsets.get()+regionsNum,
94            [](const RegionOffsetEntry& a, const RegionOffsetEntry& b)
95            { return a.first < b.first; });
96    // checking distance between regions
97    for (size_t i = 1; i <= regionsNum; i++)
98    {
99        size_t end = (i<regionsNum) ? symOffsets[i].first : codeOffset+codeSize;
100        ROCmRegion& region = regions[symOffsets[i-1].second];
101        if (region.isKernel && symOffsets[i-1].first+0x100 > end)
102            throw Exception("Kernel size is too small!");
103       
104        const size_t regSize = end - symOffsets[i-1].first;
105        if (region.size==0)
106            region.size = regSize;
107        else
108            region.size = std::min(regSize, region.size);
109    }
110   
111    if (hasRegionMap())
112    {   // create region map
113        regionsMap.resize(regionsNum);
114        for (size_t i = 0; i < regionsNum; i++)
115            regionsMap[i] = std::make_pair(regions[i].regionName, i);
116        mapSort(regionsMap.begin(), regionsMap.end());
117    }
118}
119
120const ROCmRegion& ROCmBinary::getRegion(const char* name) const
121{
122    RegionMap::const_iterator it = binaryMapFind(regionsMap.begin(),
123                             regionsMap.end(), name);
124    if (it == regionsMap.end())
125        throw Exception("Can't find region name");
126    return regions[it->second];
127}
128
129bool CLRX::isROCmBinary(size_t binarySize, const cxbyte* binary)
130{
131    if (!isElfBinary(binarySize, binary))
132        return false;
133    if (binary[EI_CLASS] != ELFCLASS64)
134        return false;
135    const Elf64_Ehdr* ehdr = reinterpret_cast<const Elf64_Ehdr*>(binary);
136    if (ULEV(ehdr->e_machine) != 0xe0 || ULEV(ehdr->e_flags)!=0)
137        return false;
138    return true;
139}
140
141/*
142 * ROCm Binary Generator
143 */
144
145ROCmBinGenerator::ROCmBinGenerator() : manageable(false), input(nullptr)
146{ }
147
148ROCmBinGenerator::ROCmBinGenerator(const ROCmInput* rocmInput)
149        : manageable(false), input(rocmInput)
150{ }
151
152ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
153        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
154        const std::vector<ROCmSymbolInput>& symbols)
155{
156    input = new ROCmInput{ deviceType, archMinor, archStepping, symbols, codeSize, code };
157}
158
159ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
160        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
161        std::vector<ROCmSymbolInput>&& symbols)
162{
163    input = new ROCmInput{ deviceType, archMinor, archStepping, std::move(symbols),
164                codeSize, code };
165}
166
167ROCmBinGenerator::~ROCmBinGenerator()
168{
169    if (manageable)
170        delete input;
171}
172
173void ROCmBinGenerator::setInput(const ROCmInput* input)
174{
175    if (manageable)
176        delete input;
177    manageable = false;
178    this->input = input;
179}
180
181static const cxbyte noteDescType1[8] =
182{ 2, 0, 0, 0, 1, 0, 0, 0 };
183
184static const cxbyte noteDescType3[27] =
185{ 4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186  'A', 'M', 'D', 0, 'A', 'M', 'D', 'G', 'P', 'U', 0 };
187
188struct AMDGPUArchValues
189{
190    uint32_t major;
191    uint32_t minor;
192    uint32_t stepping;
193};
194
195static const AMDGPUArchValues amdGpuArchValuesTbl[] =
196{
197    { 0, 0, 0 }, // GPUDeviceType::CAPE_VERDE
198    { 0, 0, 0 }, // GPUDeviceType::PITCAIRN
199    { 0, 0, 0 }, // GPUDeviceType::TAHITI
200    { 0, 0, 0 }, // GPUDeviceType::OLAND
201    { 7, 0, 0 }, // GPUDeviceType::BONAIRE
202    { 7, 0, 0 }, // GPUDeviceType::SPECTRE
203    { 7, 0, 0 }, // GPUDeviceType::SPOOKY
204    { 7, 0, 0 }, // GPUDeviceType::KALINDI
205    { 0, 0, 0 }, // GPUDeviceType::HAINAN
206    { 7, 0, 1 }, // GPUDeviceType::HAWAII
207    { 8, 0, 0 }, // GPUDeviceType::ICELAND
208    { 8, 0, 0 }, // GPUDeviceType::TONGA
209    { 7, 0, 0 }, // GPUDeviceType::MULLINS
210    { 8, 0, 3 }, // GPUDeviceType::FIJI
211    { 8, 0, 1 }, // GPUDeviceType::CARRIZO
212    { 0, 0, 0 }, // GPUDeviceType::DUMMY
213    { 0, 0, 0 }, // GPUDeviceType::GOOSE
214    { 0, 0, 0 }, // GPUDeviceType::HORSE
215    { 8, 0, 1 }, // GPUDeviceType::STONEY
216    { 8, 0, 4 }, // GPUDeviceType::ELLESMERE
217    { 8, 0, 4 } // GPUDeviceType::BAFFIN
218};
219
220
221void ROCmBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
222             Array<cxbyte>* aPtr) const
223{
224    const GPUArchitecture arch = getGPUArchitectureFromDeviceType(input->deviceType);
225    if (arch == GPUArchitecture::GCN1_0)
226        throw Exception("OpenCL 2.0 supported only for GCN1.1 or later");
227   
228    AMDGPUArchValues amdGpuArchValues = amdGpuArchValuesTbl[cxuint(input->deviceType)];
229    if (input->archMinor!=UINT32_MAX)
230        amdGpuArchValues.minor = input->archMinor;
231    if (input->archStepping!=UINT32_MAX)
232        amdGpuArchValues.stepping = input->archStepping;
233   
234    const char* comment = "CLRX ROCmBinGenerator " CLRX_VERSION;
235    uint32_t commentSize = ::strlen(comment);
236    if (input->comment!=nullptr)
237    {   // if comment, store comment section
238        comment = input->comment;
239        commentSize = input->commentSize;
240        if (commentSize==0)
241            commentSize = ::strlen(comment);
242    }
243   
244    ElfBinaryGen64 elfBinGen64({ 0U, 0U, 0x40, 0, ET_DYN,
245        0xe0, EV_CURRENT, UINT_MAX, 0, 0 }, true, true, true, PHREGION_FILESTART);
246    // add symbols
247    elfBinGen64.addSymbol(ElfSymbol64("_DYNAMIC", 5,
248                  ELF64_ST_INFO(STB_LOCAL, STT_NOTYPE), STV_HIDDEN, true, 0, 0));
249    for (const ROCmSymbolInput& symbol: input->symbols)
250    {
251        ElfSymbol64 elfsym;
252        if (symbol.isKernel)
253            elfsym = ElfSymbol64(symbol.symbolName.c_str(), 4,
254                  ELF64_ST_INFO(STB_GLOBAL, STT_GNU_IFUNC), 0, true,
255                  symbol.offset, symbol.size);
256        else
257            elfsym = ElfSymbol64(symbol.symbolName.c_str(), 4,
258                  ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), 0, true,
259                  symbol.offset, symbol.size);
260        elfBinGen64.addSymbol(elfsym);
261        elfBinGen64.addDynSymbol(elfsym);
262    }
263   
264    static const int32_t dynTags[] = {
265        DT_SYMTAB, DT_SYMENT, DT_STRTAB, DT_STRSZ, DT_HASH };
266    elfBinGen64.addDynamics(sizeof(dynTags)/sizeof(int32_t), dynTags);
267    // elf program headers
268    elfBinGen64.addProgramHeader({ PT_PHDR, PF_R, 0, 1,
269                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
270    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R, PHREGION_FILESTART, 4,
271                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 0x1000 });
272    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_X, 4, 1,
273                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
274    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_W, 5, 1,
275                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
276    elfBinGen64.addProgramHeader({ PT_DYNAMIC, PF_R|PF_W, 5, 1,
277                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 8 });
278    elfBinGen64.addProgramHeader({ PT_GNU_RELRO, PF_R, 5, 1,
279                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 1 });
280    elfBinGen64.addProgramHeader({ PT_GNU_STACK, PF_R|PF_W, PHREGION_FILESTART, 0,
281                    true, 0, 0, 0 });
282   
283    // elf notes
284    elfBinGen64.addNote({"AMD", sizeof noteDescType1, noteDescType1, 1U});
285    std::unique_ptr<cxbyte[]> noteBuf(new cxbyte[0x1b]);
286    ::memcpy(noteBuf.get(), noteDescType3, 0x1b);
287    SULEV(*(uint32_t*)(noteBuf.get()+4), amdGpuArchValues.major);
288    SULEV(*(uint32_t*)(noteBuf.get()+8), amdGpuArchValues.minor);
289    SULEV(*(uint32_t*)(noteBuf.get()+12), amdGpuArchValues.stepping);
290    elfBinGen64.addNote({"AMD", 0x1b, noteBuf.get(), 3U});
291   
292    /// region and sections
293    elfBinGen64.addRegion(ElfRegion64::programHeaderTable());
294    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
295                ".dynsym", SHT_DYNSYM, SHF_ALLOC, 0, 1, Elf64Types::nobase));
296    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 4,
297                ".hash", SHT_HASH, SHF_ALLOC, 1, 0, Elf64Types::nobase));
298    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1, ".dynstr", SHT_STRTAB,
299                SHF_ALLOC, 0, 0, Elf64Types::nobase));
300    elfBinGen64.addRegion(ElfRegion64(input->codeSize, (const cxbyte*)input->code, 
301              0x1000, ".text", SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR, 0, 0,
302              Elf64Types::nobase, 0, false, 256));
303    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 0x1000,
304                ".dynamic", SHT_DYNAMIC, SHF_ALLOC|SHF_WRITE, 3, 0,
305                Elf64Types::nobase, 0, false, 8));
306    elfBinGen64.addRegion(ElfRegion64::noteSection());
307    elfBinGen64.addRegion(ElfRegion64(commentSize, (const cxbyte*)comment, 1, ".comment",
308              SHT_PROGBITS, SHF_MERGE|SHF_STRINGS, 0, 0, 0, 1));
309    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
310                ".symtab", SHT_SYMTAB, 0, 0, 1));
311    elfBinGen64.addRegion(ElfRegion64::shstrtabSection());
312    elfBinGen64.addRegion(ElfRegion64::strtabSection());
313    elfBinGen64.addRegion(ElfRegion64::sectionHeaderTable());
314   
315    size_t binarySize = elfBinGen64.countSize();
316    /****
317     * prepare for write binary to output
318     ****/
319    std::unique_ptr<std::ostream> outStreamHolder;
320    std::ostream* os = nullptr;
321    if (aPtr != nullptr)
322    {
323        aPtr->resize(binarySize);
324        outStreamHolder.reset(
325                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
326        os = outStreamHolder.get();
327    }
328    else if (vPtr != nullptr)
329    {
330        vPtr->resize(binarySize);
331        outStreamHolder.reset(new VectorOStream(*vPtr));
332        os = outStreamHolder.get();
333    }
334    else // from argument
335        os = osPtr;
336   
337    const std::ios::iostate oldExceptions = os->exceptions();
338    try
339    {
340    os->exceptions(std::ios::failbit | std::ios::badbit);
341    /****
342     * write binary to output
343     ****/
344    FastOutputBuffer bos(256, *os);
345    elfBinGen64.generate(bos);
346    assert(bos.getWritten() == binarySize);
347    }
348    catch(...)
349    {
350        os->exceptions(oldExceptions);
351        throw;
352    }
353    os->exceptions(oldExceptions);
354}
355
356void ROCmBinGenerator::generate(Array<cxbyte>& array) const
357{
358    generateInternal(nullptr, nullptr, &array);
359}
360
361void ROCmBinGenerator::generate(std::ostream& os) const
362{
363    generateInternal(&os, nullptr, nullptr);
364}
365
366void ROCmBinGenerator::generate(std::vector<char>& v) const
367{
368    generateInternal(nullptr, &v, nullptr);
369}
Note: See TracBrowser for help on using the repository browser.