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

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

CLRadeonExtender: Filled up a missing device code types for Amd OpenCL 2.0 binary format and ROCm binary format.

File size: 15.2 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2017 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 (ULEV(sym.st_shndx)==textIndex &&
62            (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
63                (bind==STB_GLOBAL && symType==STT_OBJECT)))
64            regionsNum++;
65    }
66    if (code==nullptr && regionsNum!=0)
67        throw Exception("No code if regions number is not zero");
68    regions.reset(new ROCmRegion[regionsNum]);
69    size_t j = 0;
70    typedef std::pair<uint64_t, size_t> RegionOffsetEntry;
71    std::unique_ptr<RegionOffsetEntry[]> symOffsets(new RegionOffsetEntry[regionsNum]);
72   
73    for (size_t i = 0; i < symbolsNum; i++)
74    {
75        const Elf64_Sym& sym = getSymbol(i);
76        if (ULEV(sym.st_shndx)!=textIndex)
77            continue;   // if not in '.text' section
78        const size_t value = ULEV(sym.st_value);
79        if (value < codeOffset)
80            throw Exception("Region offset is too small!");
81        const size_t size = ULEV(sym.st_size);
82       
83        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
84        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
85        if (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
86                (bind==STB_GLOBAL && symType==STT_OBJECT))
87        {
88            ROCmRegionType type = ROCmRegionType::DATA;
89            if (symType==STT_GNU_IFUNC) 
90                type = ROCmRegionType::KERNEL;
91            else if (symType==STT_FUNC)
92                type = ROCmRegionType::FKERNEL;
93            symOffsets[j] = std::make_pair(value, j);
94            if (type!=ROCmRegionType::DATA && value+0x100 > codeOffset+codeSize)
95                throw Exception("Kernel or code offset is too big!");
96            regions[j++] = { getSymbolName(i), size, value, type };
97        }
98    }
99    std::sort(symOffsets.get(), symOffsets.get()+regionsNum,
100            [](const RegionOffsetEntry& a, const RegionOffsetEntry& b)
101            { return a.first < b.first; });
102    // checking distance between regions
103    for (size_t i = 1; i <= regionsNum; i++)
104    {
105        size_t end = (i<regionsNum) ? symOffsets[i].first : codeOffset+codeSize;
106        ROCmRegion& region = regions[symOffsets[i-1].second];
107        if (region.type==ROCmRegionType::KERNEL && symOffsets[i-1].first+0x100 > end)
108            throw Exception("Kernel size is too small!");
109       
110        const size_t regSize = end - symOffsets[i-1].first;
111        if (region.size==0)
112            region.size = regSize;
113        else
114            region.size = std::min(regSize, region.size);
115    }
116   
117    if (hasRegionMap())
118    {   // create region map
119        regionsMap.resize(regionsNum);
120        for (size_t i = 0; i < regionsNum; i++)
121            regionsMap[i] = std::make_pair(regions[i].regionName, i);
122        mapSort(regionsMap.begin(), regionsMap.end());
123    }
124}
125
126const ROCmRegion& ROCmBinary::getRegion(const char* name) const
127{
128    RegionMap::const_iterator it = binaryMapFind(regionsMap.begin(),
129                             regionsMap.end(), name);
130    if (it == regionsMap.end())
131        throw Exception("Can't find region name");
132    return regions[it->second];
133}
134
135bool CLRX::isROCmBinary(size_t binarySize, const cxbyte* binary)
136{
137    if (!isElfBinary(binarySize, binary))
138        return false;
139    if (binary[EI_CLASS] != ELFCLASS64)
140        return false;
141    const Elf64_Ehdr* ehdr = reinterpret_cast<const Elf64_Ehdr*>(binary);
142    if (ULEV(ehdr->e_machine) != 0xe0 || ULEV(ehdr->e_flags)!=0)
143        return false;
144    return true;
145}
146
147
148void ROCmInput::addEmptyKernel(const char* kernelName)
149{
150    symbols.push_back({ kernelName, 0, 0, ROCmRegionType::KERNEL });
151}
152/*
153 * ROCm Binary Generator
154 */
155
156ROCmBinGenerator::ROCmBinGenerator() : manageable(false), input(nullptr)
157{ }
158
159ROCmBinGenerator::ROCmBinGenerator(const ROCmInput* rocmInput)
160        : manageable(false), input(rocmInput)
161{ }
162
163ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
164        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
165        const std::vector<ROCmSymbolInput>& symbols)
166{
167    input = new ROCmInput{ deviceType, archMinor, archStepping, symbols, codeSize, code };
168}
169
170ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
171        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
172        std::vector<ROCmSymbolInput>&& symbols)
173{
174    input = new ROCmInput{ deviceType, archMinor, archStepping, std::move(symbols),
175                codeSize, code };
176}
177
178ROCmBinGenerator::~ROCmBinGenerator()
179{
180    if (manageable)
181        delete input;
182}
183
184void ROCmBinGenerator::setInput(const ROCmInput* input)
185{
186    if (manageable)
187        delete input;
188    manageable = false;
189    this->input = input;
190}
191
192static const cxbyte noteDescType1[8] =
193{ 2, 0, 0, 0, 1, 0, 0, 0 };
194
195static const cxbyte noteDescType3[27] =
196{ 4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
197  'A', 'M', 'D', 0, 'A', 'M', 'D', 'G', 'P', 'U', 0 };
198
199// section index for symbol binding
200static const uint16_t mainBuiltinSectionTable[] =
201{
202    10, // ELFSECTID_SHSTRTAB
203    11, // ELFSECTID_STRTAB
204    9, // ELFSECTID_SYMTAB
205    3, // ELFSECTID_DYNSTR
206    1, // ELFSECTID_DYNSYM
207    4, // ELFSECTID_TEXT
208    SHN_UNDEF, // ELFSECTID_RODATA
209    SHN_UNDEF, // ELFSECTID_DATA
210    SHN_UNDEF, // ELFSECTID_BSS
211    8, // ELFSECTID_COMMENT
212    2, // ROCMSECTID_HASH
213    5, // ROCMSECTID_DYNAMIC
214    6, // ROCMSECTID_NOTE
215    7 // ROCMSECTID_GPUCONFIG
216};
217
218static const AMDGPUArchValues rocmAmdGpuArchValuesTbl[] =
219{
220    { 0, 0, 0 }, // GPUDeviceType::CAPE_VERDE
221    { 0, 0, 0 }, // GPUDeviceType::PITCAIRN
222    { 0, 0, 0 }, // GPUDeviceType::TAHITI
223    { 0, 0, 0 }, // GPUDeviceType::OLAND
224    { 7, 0, 0 }, // GPUDeviceType::BONAIRE
225    { 7, 0, 0 }, // GPUDeviceType::SPECTRE
226    { 7, 0, 0 }, // GPUDeviceType::SPOOKY
227    { 7, 0, 0 }, // GPUDeviceType::KALINDI
228    { 0, 0, 0 }, // GPUDeviceType::HAINAN
229    { 7, 0, 1 }, // GPUDeviceType::HAWAII
230    { 8, 0, 0 }, // GPUDeviceType::ICELAND
231    { 8, 0, 0 }, // GPUDeviceType::TONGA
232    { 7, 0, 0 }, // GPUDeviceType::MULLINS
233    { 8, 0, 3 }, // GPUDeviceType::FIJI
234    { 8, 0, 1 }, // GPUDeviceType::CARRIZO
235    { 8, 0, 1 }, // GPUDeviceType::DUMMY
236    { 8, 0, 4 }, // GPUDeviceType::GOOSE
237    { 8, 0, 4 }, // GPUDeviceType::HORSE
238    { 8, 0, 1 }, // GPUDeviceType::STONEY
239    { 8, 0, 4 }, // GPUDeviceType::ELLESMERE
240    { 8, 0, 4 } // GPUDeviceType::BAFFIN
241};
242
243void ROCmBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
244             Array<cxbyte>* aPtr) const
245{
246    AMDGPUArchValues amdGpuArchValues = rocmAmdGpuArchValuesTbl[cxuint(input->deviceType)];
247    if (input->archMinor!=UINT32_MAX)
248        amdGpuArchValues.minor = input->archMinor;
249    if (input->archStepping!=UINT32_MAX)
250        amdGpuArchValues.stepping = input->archStepping;
251   
252    const char* comment = "CLRX ROCmBinGenerator " CLRX_VERSION;
253    uint32_t commentSize = ::strlen(comment);
254    if (input->comment!=nullptr)
255    {   // if comment, store comment section
256        comment = input->comment;
257        commentSize = input->commentSize;
258        if (commentSize==0)
259            commentSize = ::strlen(comment);
260    }
261   
262    ElfBinaryGen64 elfBinGen64({ 0U, 0U, 0x40, 0, ET_DYN,
263        0xe0, EV_CURRENT, UINT_MAX, 0, 0 }, true, true, true, PHREGION_FILESTART);
264    // add symbols
265    elfBinGen64.addSymbol(ElfSymbol64("_DYNAMIC", 5,
266                  ELF64_ST_INFO(STB_LOCAL, STT_NOTYPE), STV_HIDDEN, true, 0, 0));
267    for (const ROCmSymbolInput& symbol: input->symbols)
268    {
269        ElfSymbol64 elfsym;
270        switch (symbol.type)
271        {
272            case ROCmRegionType::KERNEL:
273                elfsym = ElfSymbol64(symbol.symbolName.c_str(), 4,
274                      ELF64_ST_INFO(STB_GLOBAL, STT_GNU_IFUNC), 0, true,
275                      symbol.offset, symbol.size);
276                break;
277            case ROCmRegionType::FKERNEL:
278                elfsym = ElfSymbol64(symbol.symbolName.c_str(), 4,
279                      ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), 0, true,
280                      symbol.offset, symbol.size);
281                break;
282            case ROCmRegionType::DATA:
283                elfsym = ElfSymbol64(symbol.symbolName.c_str(), 4,
284                      ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), 0, true,
285                      symbol.offset, symbol.size);
286                break;
287            default:
288                break;
289        }
290        elfBinGen64.addSymbol(elfsym);
291        elfBinGen64.addDynSymbol(elfsym);
292    }
293   
294    static const int32_t dynTags[] = {
295        DT_SYMTAB, DT_SYMENT, DT_STRTAB, DT_STRSZ, DT_HASH };
296    elfBinGen64.addDynamics(sizeof(dynTags)/sizeof(int32_t), dynTags);
297    // elf program headers
298    elfBinGen64.addProgramHeader({ PT_PHDR, PF_R, 0, 1,
299                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
300    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R, PHREGION_FILESTART, 4,
301                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 0x1000 });
302    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_X, 4, 1,
303                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
304    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_W, 5, 1,
305                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
306    elfBinGen64.addProgramHeader({ PT_DYNAMIC, PF_R|PF_W, 5, 1,
307                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 8 });
308    elfBinGen64.addProgramHeader({ PT_GNU_RELRO, PF_R, 5, 1,
309                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 1 });
310    elfBinGen64.addProgramHeader({ PT_GNU_STACK, PF_R|PF_W, PHREGION_FILESTART, 0,
311                    true, 0, 0, 0 });
312   
313    // elf notes
314    elfBinGen64.addNote({"AMD", sizeof noteDescType1, noteDescType1, 1U});
315    std::unique_ptr<cxbyte[]> noteBuf(new cxbyte[0x1b]);
316    ::memcpy(noteBuf.get(), noteDescType3, 0x1b);
317    SULEV(*(uint32_t*)(noteBuf.get()+4), amdGpuArchValues.major);
318    SULEV(*(uint32_t*)(noteBuf.get()+8), amdGpuArchValues.minor);
319    SULEV(*(uint32_t*)(noteBuf.get()+12), amdGpuArchValues.stepping);
320    elfBinGen64.addNote({"AMD", 0x1b, noteBuf.get(), 3U});
321   
322    /// region and sections
323    elfBinGen64.addRegion(ElfRegion64::programHeaderTable());
324    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
325                ".dynsym", SHT_DYNSYM, SHF_ALLOC, 0, 1, Elf64Types::nobase));
326    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 4,
327                ".hash", SHT_HASH, SHF_ALLOC, 1, 0, Elf64Types::nobase));
328    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1, ".dynstr", SHT_STRTAB,
329                SHF_ALLOC, 0, 0, Elf64Types::nobase));
330    elfBinGen64.addRegion(ElfRegion64(input->codeSize, (const cxbyte*)input->code, 
331              0x1000, ".text", SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR, 0, 0,
332              Elf64Types::nobase, 0, false, 256));
333    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 0x1000,
334                ".dynamic", SHT_DYNAMIC, SHF_ALLOC|SHF_WRITE, 3, 0,
335                Elf64Types::nobase, 0, false, 8));
336    elfBinGen64.addRegion(ElfRegion64::noteSection());
337    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1,
338                ".AMDGPU.config", SHT_PROGBITS, 0));
339    elfBinGen64.addRegion(ElfRegion64(commentSize, (const cxbyte*)comment, 1, ".comment",
340              SHT_PROGBITS, SHF_MERGE|SHF_STRINGS, 0, 0, 0, 1));
341    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
342                ".symtab", SHT_SYMTAB, 0, 0, 1));
343    elfBinGen64.addRegion(ElfRegion64::shstrtabSection());
344    elfBinGen64.addRegion(ElfRegion64::strtabSection());
345    elfBinGen64.addRegion(ElfRegion64::sectionHeaderTable());
346   
347    /* extra sections */
348    for (const BinSection& section: input->extraSections)
349        elfBinGen64.addRegion(ElfRegion64(section, mainBuiltinSectionTable,
350                         ROCMSECTID_MAX, 12));
351    /* extra symbols */
352    for (const BinSymbol& symbol: input->extraSymbols)
353        elfBinGen64.addSymbol(ElfSymbol64(symbol, mainBuiltinSectionTable,
354                         ROCMSECTID_MAX, 12));
355   
356    size_t binarySize = elfBinGen64.countSize();
357    /****
358     * prepare for write binary to output
359     ****/
360    std::unique_ptr<std::ostream> outStreamHolder;
361    std::ostream* os = nullptr;
362    if (aPtr != nullptr)
363    {
364        aPtr->resize(binarySize);
365        outStreamHolder.reset(
366                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
367        os = outStreamHolder.get();
368    }
369    else if (vPtr != nullptr)
370    {
371        vPtr->resize(binarySize);
372        outStreamHolder.reset(new VectorOStream(*vPtr));
373        os = outStreamHolder.get();
374    }
375    else // from argument
376        os = osPtr;
377   
378    const std::ios::iostate oldExceptions = os->exceptions();
379    try
380    {
381    os->exceptions(std::ios::failbit | std::ios::badbit);
382    /****
383     * write binary to output
384     ****/
385    FastOutputBuffer bos(256, *os);
386    elfBinGen64.generate(bos);
387    assert(bos.getWritten() == binarySize);
388    }
389    catch(...)
390    {
391        os->exceptions(oldExceptions);
392        throw;
393    }
394    os->exceptions(oldExceptions);
395}
396
397void ROCmBinGenerator::generate(Array<cxbyte>& array) const
398{
399    generateInternal(nullptr, nullptr, &array);
400}
401
402void ROCmBinGenerator::generate(std::ostream& os) const
403{
404    generateInternal(&os, nullptr, nullptr);
405}
406
407void ROCmBinGenerator::generate(std::vector<char>& v) const
408{
409    generateInternal(nullptr, &v, nullptr);
410}
Note: See TracBrowser for help on using the repository browser.