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

Last change on this file since 3396 was 3396, checked in by matszpk, 2 years ago

CLRadeonExtender: Freeing comment in "{ " to making line comments. Commenting GPUId and InputOutput?.
Replace 1 by REGTYPE_VGPR in GPUId.

File size: 18.3 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    {
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    {
120        // create region map
121        regionsMap.resize(regionsNum);
122        for (size_t i = 0; i < regionsNum; i++)
123            regionsMap[i] = std::make_pair(regions[i].regionName, i);
124        mapSort(regionsMap.begin(), regionsMap.end());
125    }
126}
127
128struct AMDGPUArchValuesEntry
129{
130    uint32_t major;
131    uint32_t minor;
132    uint32_t stepping;
133    GPUDeviceType deviceType;
134};
135
136static const AMDGPUArchValuesEntry amdGpuArchValuesTbl[] =
137{
138    { 0, 0, 0, GPUDeviceType::CAPE_VERDE },
139    { 7, 0, 0, GPUDeviceType::BONAIRE },
140    { 7, 0, 1, GPUDeviceType::HAWAII },
141    { 8, 0, 0, GPUDeviceType::ICELAND },
142    { 8, 0, 1, GPUDeviceType::CARRIZO },
143    { 8, 0, 2, GPUDeviceType::ICELAND },
144    { 8, 0, 3, GPUDeviceType::FIJI },
145    { 8, 0, 4, GPUDeviceType::FIJI },
146    { 8, 1, 0, GPUDeviceType::STONEY },
147    { 9, 0, 0, GPUDeviceType::GFX900 },
148    { 9, 0, 1, GPUDeviceType::GFX901 }
149};
150
151static const size_t amdGpuArchValuesNum = sizeof(amdGpuArchValuesTbl) /
152                sizeof(AMDGPUArchValuesEntry);
153
154
155GPUDeviceType ROCmBinary::determineGPUDeviceType(uint32_t& outArchMinor,
156                     uint32_t& outArchStepping) const
157{
158    uint32_t archMajor = 0;
159    uint32_t archMinor = 0;
160    uint32_t archStepping = 0;
161   
162    {
163        const cxbyte* noteContent = (const cxbyte*)getNotes();
164        if (noteContent==nullptr)
165            throw Exception("Missing notes in inner binary!");
166        size_t notesSize = getNotesSize();
167        // find note about AMDGPU
168        for (size_t offset = 0; offset < notesSize; )
169        {
170            const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
171            size_t namesz = ULEV(nhdr->n_namesz);
172            size_t descsz = ULEV(nhdr->n_descsz);
173            if (usumGt(offset, namesz+descsz, notesSize))
174                throw Exception("Note offset+size out of range");
175            if (ULEV(nhdr->n_type) == 0x3 && namesz==4 && descsz>=0x1a &&
176                ::strcmp((const char*)noteContent+offset+sizeof(Elf64_Nhdr), "AMD")==0)
177            {    // AMDGPU type
178                const uint32_t* content = (const uint32_t*)
179                        (noteContent+offset+sizeof(Elf64_Nhdr) + 4);
180                archMajor = ULEV(content[1]);
181                archMinor = ULEV(content[2]);
182                archStepping = ULEV(content[3]);
183            }
184            size_t align = (((namesz+descsz)&3)!=0) ? 4-((namesz+descsz)&3) : 0;
185            offset += sizeof(Elf64_Nhdr) + namesz + descsz + align;
186        }
187    }
188    // determine device type
189    GPUDeviceType deviceType = GPUDeviceType::CAPE_VERDE;
190    if (archMajor==0)
191        deviceType = GPUDeviceType::CAPE_VERDE;
192    else if (archMajor==7)
193        deviceType = GPUDeviceType::BONAIRE;
194    else if (archMajor==8)
195        deviceType = GPUDeviceType::ICELAND;
196    else if (archMajor==9)
197        deviceType = GPUDeviceType::GFX900;
198   
199    for (cxuint i = 0; i < amdGpuArchValuesNum; i++)
200        if (amdGpuArchValuesTbl[i].major==archMajor &&
201            amdGpuArchValuesTbl[i].minor==archMinor &&
202            amdGpuArchValuesTbl[i].stepping==archStepping)
203        {
204            deviceType = amdGpuArchValuesTbl[i].deviceType;
205            break;
206        }
207    outArchMinor = archMinor;
208    outArchStepping = archStepping;
209    return deviceType;
210}
211
212const ROCmRegion& ROCmBinary::getRegion(const char* name) const
213{
214    RegionMap::const_iterator it = binaryMapFind(regionsMap.begin(),
215                             regionsMap.end(), name);
216    if (it == regionsMap.end())
217        throw Exception("Can't find region name");
218    return regions[it->second];
219}
220
221bool CLRX::isROCmBinary(size_t binarySize, const cxbyte* binary)
222{
223    if (!isElfBinary(binarySize, binary))
224        return false;
225    if (binary[EI_CLASS] != ELFCLASS64)
226        return false;
227    const Elf64_Ehdr* ehdr = reinterpret_cast<const Elf64_Ehdr*>(binary);
228    if (ULEV(ehdr->e_machine) != 0xe0 || ULEV(ehdr->e_flags)!=0)
229        return false;
230    return true;
231}
232
233
234void ROCmInput::addEmptyKernel(const char* kernelName)
235{
236    symbols.push_back({ kernelName, 0, 0, ROCmRegionType::KERNEL });
237}
238/*
239 * ROCm Binary Generator
240 */
241
242ROCmBinGenerator::ROCmBinGenerator() : manageable(false), input(nullptr)
243{ }
244
245ROCmBinGenerator::ROCmBinGenerator(const ROCmInput* rocmInput)
246        : manageable(false), input(rocmInput)
247{ }
248
249ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
250        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
251        const std::vector<ROCmSymbolInput>& symbols)
252{
253    input = new ROCmInput{ deviceType, archMinor, archStepping, symbols, codeSize, code };
254}
255
256ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
257        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
258        std::vector<ROCmSymbolInput>&& symbols)
259{
260    input = new ROCmInput{ deviceType, archMinor, archStepping, std::move(symbols),
261                codeSize, code };
262}
263
264ROCmBinGenerator::~ROCmBinGenerator()
265{
266    if (manageable)
267        delete input;
268}
269
270void ROCmBinGenerator::setInput(const ROCmInput* input)
271{
272    if (manageable)
273        delete input;
274    manageable = false;
275    this->input = input;
276}
277
278static const cxbyte noteDescType1[8] =
279{ 2, 0, 0, 0, 1, 0, 0, 0 };
280
281static const cxbyte noteDescType3[27] =
282{ 4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
283  'A', 'M', 'D', 0, 'A', 'M', 'D', 'G', 'P', 'U', 0 };
284
285// section index for symbol binding
286static const uint16_t mainBuiltinSectionTable[] =
287{
288    10, // ELFSECTID_SHSTRTAB
289    11, // ELFSECTID_STRTAB
290    9, // ELFSECTID_SYMTAB
291    3, // ELFSECTID_DYNSTR
292    1, // ELFSECTID_DYNSYM
293    4, // ELFSECTID_TEXT
294    SHN_UNDEF, // ELFSECTID_RODATA
295    SHN_UNDEF, // ELFSECTID_DATA
296    SHN_UNDEF, // ELFSECTID_BSS
297    8, // ELFSECTID_COMMENT
298    2, // ROCMSECTID_HASH
299    5, // ROCMSECTID_DYNAMIC
300    6, // ROCMSECTID_NOTE
301    7 // ROCMSECTID_GPUCONFIG
302};
303
304static const AMDGPUArchValues rocmAmdGpuArchValuesTbl[] =
305{
306    { 0, 0, 0 }, // GPUDeviceType::CAPE_VERDE
307    { 0, 0, 0 }, // GPUDeviceType::PITCAIRN
308    { 0, 0, 0 }, // GPUDeviceType::TAHITI
309    { 0, 0, 0 }, // GPUDeviceType::OLAND
310    { 7, 0, 0 }, // GPUDeviceType::BONAIRE
311    { 7, 0, 0 }, // GPUDeviceType::SPECTRE
312    { 7, 0, 0 }, // GPUDeviceType::SPOOKY
313    { 7, 0, 0 }, // GPUDeviceType::KALINDI
314    { 0, 0, 0 }, // GPUDeviceType::HAINAN
315    { 7, 0, 1 }, // GPUDeviceType::HAWAII
316    { 8, 0, 0 }, // GPUDeviceType::ICELAND
317    { 8, 0, 0 }, // GPUDeviceType::TONGA
318    { 7, 0, 0 }, // GPUDeviceType::MULLINS
319    { 8, 0, 3 }, // GPUDeviceType::FIJI
320    { 8, 0, 1 }, // GPUDeviceType::CARRIZO
321    { 8, 0, 1 }, // GPUDeviceType::DUMMY
322    { 8, 0, 4 }, // GPUDeviceType::GOOSE
323    { 8, 0, 4 }, // GPUDeviceType::HORSE
324    { 8, 0, 1 }, // GPUDeviceType::STONEY
325    { 8, 0, 4 }, // GPUDeviceType::ELLESMERE
326    { 8, 0, 4 }, // GPUDeviceType::BAFFIN
327    { 8, 0, 4 }, // GPUDeviceType::GFX804
328    { 9, 0, 0 }, // GPUDeviceType::GFX900
329    { 9, 0, 1 }  // GPUDeviceType::GFX901
330};
331
332void ROCmBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
333             Array<cxbyte>* aPtr) const
334{
335    AMDGPUArchValues amdGpuArchValues = rocmAmdGpuArchValuesTbl[cxuint(input->deviceType)];
336    if (input->archMinor!=UINT32_MAX)
337        amdGpuArchValues.minor = input->archMinor;
338    if (input->archStepping!=UINT32_MAX)
339        amdGpuArchValues.stepping = input->archStepping;
340   
341    const char* comment = "CLRX ROCmBinGenerator " CLRX_VERSION;
342    uint32_t commentSize = ::strlen(comment);
343    if (input->comment!=nullptr)
344    {
345        // if comment, store comment section
346        comment = input->comment;
347        commentSize = input->commentSize;
348        if (commentSize==0)
349            commentSize = ::strlen(comment);
350    }
351   
352    ElfBinaryGen64 elfBinGen64({ 0U, 0U, 0x40, 0, ET_DYN,
353        0xe0, EV_CURRENT, UINT_MAX, 0, 0 }, true, true, true, PHREGION_FILESTART);
354    // add symbols
355    elfBinGen64.addSymbol(ElfSymbol64("_DYNAMIC", 5,
356                  ELF64_ST_INFO(STB_LOCAL, STT_NOTYPE), STV_HIDDEN, true, 0, 0));
357    for (const ROCmSymbolInput& symbol: input->symbols)
358    {
359        ElfSymbol64 elfsym;
360        switch (symbol.type)
361        {
362            case ROCmRegionType::KERNEL:
363                elfsym = ElfSymbol64(symbol.symbolName.c_str(), 4,
364                      ELF64_ST_INFO(STB_GLOBAL, STT_GNU_IFUNC), 0, true,
365                      symbol.offset, symbol.size);
366                break;
367            case ROCmRegionType::FKERNEL:
368                elfsym = ElfSymbol64(symbol.symbolName.c_str(), 4,
369                      ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), 0, true,
370                      symbol.offset, symbol.size);
371                break;
372            case ROCmRegionType::DATA:
373                elfsym = ElfSymbol64(symbol.symbolName.c_str(), 4,
374                      ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), 0, true,
375                      symbol.offset, symbol.size);
376                break;
377            default:
378                break;
379        }
380        elfBinGen64.addSymbol(elfsym);
381        elfBinGen64.addDynSymbol(elfsym);
382    }
383   
384    static const int32_t dynTags[] = {
385        DT_SYMTAB, DT_SYMENT, DT_STRTAB, DT_STRSZ, DT_HASH };
386    elfBinGen64.addDynamics(sizeof(dynTags)/sizeof(int32_t), dynTags);
387    // elf program headers
388    elfBinGen64.addProgramHeader({ PT_PHDR, PF_R, 0, 1,
389                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
390    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R, PHREGION_FILESTART, 4,
391                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 0x1000 });
392    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_X, 4, 1,
393                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
394    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_W, 5, 1,
395                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
396    elfBinGen64.addProgramHeader({ PT_DYNAMIC, PF_R|PF_W, 5, 1,
397                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 8 });
398    elfBinGen64.addProgramHeader({ PT_GNU_RELRO, PF_R, 5, 1,
399                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 1 });
400    elfBinGen64.addProgramHeader({ PT_GNU_STACK, PF_R|PF_W, PHREGION_FILESTART, 0,
401                    true, 0, 0, 0 });
402   
403    // elf notes
404    elfBinGen64.addNote({"AMD", sizeof noteDescType1, noteDescType1, 1U});
405    std::unique_ptr<cxbyte[]> noteBuf(new cxbyte[0x1b]);
406    ::memcpy(noteBuf.get(), noteDescType3, 0x1b);
407    SULEV(*(uint32_t*)(noteBuf.get()+4), amdGpuArchValues.major);
408    SULEV(*(uint32_t*)(noteBuf.get()+8), amdGpuArchValues.minor);
409    SULEV(*(uint32_t*)(noteBuf.get()+12), amdGpuArchValues.stepping);
410    elfBinGen64.addNote({"AMD", 0x1b, noteBuf.get(), 3U});
411   
412    /// region and sections
413    elfBinGen64.addRegion(ElfRegion64::programHeaderTable());
414    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
415                ".dynsym", SHT_DYNSYM, SHF_ALLOC, 0, 1, Elf64Types::nobase));
416    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 4,
417                ".hash", SHT_HASH, SHF_ALLOC, 1, 0, Elf64Types::nobase));
418    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1, ".dynstr", SHT_STRTAB,
419                SHF_ALLOC, 0, 0, Elf64Types::nobase));
420    elfBinGen64.addRegion(ElfRegion64(input->codeSize, (const cxbyte*)input->code, 
421              0x1000, ".text", SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR, 0, 0,
422              Elf64Types::nobase, 0, false, 256));
423    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 0x1000,
424                ".dynamic", SHT_DYNAMIC, SHF_ALLOC|SHF_WRITE, 3, 0,
425                Elf64Types::nobase, 0, false, 8));
426    elfBinGen64.addRegion(ElfRegion64::noteSection());
427    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1,
428                ".AMDGPU.config", SHT_PROGBITS, 0));
429    elfBinGen64.addRegion(ElfRegion64(commentSize, (const cxbyte*)comment, 1, ".comment",
430              SHT_PROGBITS, SHF_MERGE|SHF_STRINGS, 0, 0, 0, 1));
431    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
432                ".symtab", SHT_SYMTAB, 0, 0, 1));
433    elfBinGen64.addRegion(ElfRegion64::shstrtabSection());
434    elfBinGen64.addRegion(ElfRegion64::strtabSection());
435    elfBinGen64.addRegion(ElfRegion64::sectionHeaderTable());
436   
437    /* extra sections */
438    for (const BinSection& section: input->extraSections)
439        elfBinGen64.addRegion(ElfRegion64(section, mainBuiltinSectionTable,
440                         ROCMSECTID_MAX, 12));
441    /* extra symbols */
442    for (const BinSymbol& symbol: input->extraSymbols)
443        elfBinGen64.addSymbol(ElfSymbol64(symbol, mainBuiltinSectionTable,
444                         ROCMSECTID_MAX, 12));
445   
446    size_t binarySize = elfBinGen64.countSize();
447    /****
448     * prepare for write binary to output
449     ****/
450    std::unique_ptr<std::ostream> outStreamHolder;
451    std::ostream* os = nullptr;
452    if (aPtr != nullptr)
453    {
454        aPtr->resize(binarySize);
455        outStreamHolder.reset(
456                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
457        os = outStreamHolder.get();
458    }
459    else if (vPtr != nullptr)
460    {
461        vPtr->resize(binarySize);
462        outStreamHolder.reset(new VectorOStream(*vPtr));
463        os = outStreamHolder.get();
464    }
465    else // from argument
466        os = osPtr;
467   
468    const std::ios::iostate oldExceptions = os->exceptions();
469    try
470    {
471    os->exceptions(std::ios::failbit | std::ios::badbit);
472    /****
473     * write binary to output
474     ****/
475    FastOutputBuffer bos(256, *os);
476    elfBinGen64.generate(bos);
477    assert(bos.getWritten() == binarySize);
478    }
479    catch(...)
480    {
481        os->exceptions(oldExceptions);
482        throw;
483    }
484    os->exceptions(oldExceptions);
485}
486
487void ROCmBinGenerator::generate(Array<cxbyte>& array) const
488{
489    generateInternal(nullptr, nullptr, &array);
490}
491
492void ROCmBinGenerator::generate(std::ostream& os) const
493{
494    generateInternal(&os, nullptr, nullptr);
495}
496
497void ROCmBinGenerator::generate(std::vector<char>& v) const
498{
499    generateInternal(nullptr, &v, nullptr);
500}
Note: See TracBrowser for help on using the repository browser.