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

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

CLRadeonExtender: ROCm: Change ROCmRegionType::CODE to FKERNEL.

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