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

Last change on this file was 4986, checked in by matszpk, 7 weeks ago

CLRadeonExtender: ROCm: Fixed alignment for rodata section (LLVM10 bin format).

File size: 38.5 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2018 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 <cstdio>
23#include <cstring>
24#include <cstdint>
25#include <string>
26#include <vector>
27#include <algorithm>
28#include <utility>
29#include <memory>
30#include <unordered_set>
31#include <CLRX/amdbin/ElfBinaries.h>
32#include <CLRX/utils/Utilities.h>
33#include <CLRX/utils/MemAccess.h>
34#include <CLRX/utils/InputOutput.h>
35#include <CLRX/utils/Containers.h>
36#include <CLRX/amdbin/ROCmBinaries.h>
37
38using namespace CLRX;
39
40
41/*
42 * ROCm binary reader and generator
43 */
44
45/* TODO: add support for various kernel code offset (now only 256 is supported) */
46
47ROCmBinary::ROCmBinary(size_t binaryCodeSize, cxbyte* binaryCode, Flags creationFlags)
48        : ElfBinary64(binaryCodeSize, binaryCode, creationFlags),
49          regionsNum(0), codeSize(0), code(nullptr),
50          globalDataSize(0), globalData(nullptr), metadataSize(0), metadata(nullptr),
51          newBinFormat(false), llvm10BinFormat(false), metadataV3Format(false)
52{
53    cxuint textIndex = SHN_UNDEF;
54    try
55    { textIndex = getSectionIndex(".text"); }
56    catch(const Exception& ex)
57    { } // ignore failed
58    uint64_t codeOffset = 0;
59    // find '.text' section
60    if (textIndex!=SHN_UNDEF)
61    {
62        code = getSectionContent(textIndex);
63        const Elf64_Shdr& textShdr = getSectionHeader(textIndex);
64        codeSize = ULEV(textShdr.sh_size);
65        codeOffset = ULEV(textShdr.sh_offset);
66    }
67   
68    if (getHeader().e_ident[EI_ABIVERSION] == 1)
69        llvm10BinFormat = true; // likely llvm10 bin format
70   
71    cxuint rodataIndex = SHN_UNDEF;
72    const cxbyte* rodataContent = nullptr;
73    try
74    { rodataIndex = getSectionIndex(".rodata"); }
75    catch(const Exception& ex)
76    { } // ignore failed
77    // find '.text' section
78    if (rodataIndex!=SHN_UNDEF)
79    {
80        rodataContent = globalData = getSectionContent(rodataIndex);
81        const Elf64_Shdr& rodataShdr = getSectionHeader(rodataIndex);
82        globalDataSize = ULEV(rodataShdr.sh_size);
83    }
84   
85    cxuint gpuConfigIndex = SHN_UNDEF;
86    try
87    { gpuConfigIndex = getSectionIndex(".AMDGPU.config"); }
88    catch(const Exception& ex)
89    { } // ignore failed
90    newBinFormat = (gpuConfigIndex == SHN_UNDEF);
91   
92    cxuint relaDynIndex = SHN_UNDEF;
93    try
94    { relaDynIndex = getSectionIndex(".rela.dyn"); }
95    catch(const Exception& ex)
96    { } // ignore failed
97   
98    cxuint gotIndex = SHN_UNDEF;
99    try
100    { gotIndex = getSectionIndex(".got"); }
101    catch(const Exception& ex)
102    { } // ignore failed
103   
104    // counts regions (symbol or kernel)
105    std::vector<std::pair<CString, size_t> > tmpKernelDescs;
106    regionsNum = 0;
107    const size_t symbolsNum = getSymbolsNum();
108    for (size_t i = 0; i < symbolsNum; i++)
109    {
110        // count regions number
111        const Elf64_Sym& sym = getSymbol(i);
112        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
113        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
114        Elf64_Half shndx = ULEV(sym.st_shndx);
115        if (shndx==textIndex &&
116            (symType==STT_GNU_IFUNC ||
117                (symType==STT_FUNC && (!newBinFormat || llvm10BinFormat)) ||
118                (bind==STB_GLOBAL && symType==STT_OBJECT)))
119            regionsNum++;
120        if (llvm10BinFormat && shndx==rodataIndex && symType==STT_OBJECT)
121        {
122            const char* symName = getSymbolName(i);
123            size_t symNameLen = ::strlen(symName);
124            // if symname have '.kd' at end
125            if (symNameLen > 3 && symName[symNameLen-3]=='.' &&
126                symName[symNameLen-2]=='k' && symName[symNameLen-1]=='d')
127                tmpKernelDescs.push_back({ CString(symName, symName+symNameLen-3),
128                            ULEV(sym.st_value) });
129        }
130    }
131    if (llvm10BinFormat)
132    {
133        if (rodataContent==nullptr)
134            throw BinException("No rodata section in ROCm LLVM10Bin format");
135        mapSort(tmpKernelDescs.begin(), tmpKernelDescs.end());
136        kernelDescs.resize(regionsNum);
137        std::fill(kernelDescs.begin(), kernelDescs.end(), nullptr);
138    }
139   
140    if (code==nullptr && regionsNum!=0)
141        throw BinException("No code if regions number is not zero");
142    regions.reset(new ROCmRegion[regionsNum]);
143    size_t j = 0;
144    typedef std::pair<uint64_t, size_t> RegionOffsetEntry;
145    std::unique_ptr<RegionOffsetEntry[]> symOffsets(new RegionOffsetEntry[regionsNum]);
146   
147    // get regions info
148    for (size_t i = 0; i < symbolsNum; i++)
149    {
150        const Elf64_Sym& sym = getSymbol(i);
151        if (ULEV(sym.st_shndx)!=textIndex)
152            continue;   // if not in '.text' section
153        const size_t value = ULEV(sym.st_value);
154        if (value < codeOffset)
155            throw BinException("Region offset is too small!");
156        const size_t size = ULEV(sym.st_size);
157       
158        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
159        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
160        if (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
161                (bind==STB_GLOBAL && symType==STT_OBJECT))
162        {
163            ROCmRegionType type = ROCmRegionType::DATA;
164            // if kernel
165            if (symType==STT_GNU_IFUNC) 
166                type = ROCmRegionType::KERNEL;
167            // if function kernel
168            else if (symType==STT_FUNC)
169            {
170                if (newBinFormat && !llvm10BinFormat)
171                    continue;
172                type = llvm10BinFormat ? ROCmRegionType::KERNEL : ROCmRegionType::FKERNEL;
173            }
174            symOffsets[j] = std::make_pair(value, j);
175            if (type!=ROCmRegionType::DATA && value+0x100 > codeOffset+codeSize)
176                throw BinException("Kernel or code offset is too big!");
177            const char* symName = getSymbolName(i);
178            regions[j] = { symName, size, value, type };
179            if (llvm10BinFormat)
180            {
181                auto it = binaryMapFind(tmpKernelDescs.begin(), tmpKernelDescs.end(),
182                                        CString(symName));
183                if (it != tmpKernelDescs.end())
184                    kernelDescs[j] = reinterpret_cast<const ROCmKernelDescriptor*>(
185                                binaryCode + it->second);
186                else
187                    kernelDescs[j] = nullptr;
188            }
189            j++;
190        }
191    }
192    // sort regions by offset
193    std::sort(symOffsets.get(), symOffsets.get()+regionsNum,
194            [](const RegionOffsetEntry& a, const RegionOffsetEntry& b)
195            { return a.first < b.first; });
196    // checking distance between regions
197    for (size_t i = 1; i <= regionsNum; i++)
198    {
199        size_t end = (i<regionsNum) ? symOffsets[i].first : codeOffset+codeSize;
200        ROCmRegion& region = regions[symOffsets[i-1].second];
201        if (!llvm10BinFormat)
202            if (region.type==ROCmRegionType::KERNEL && symOffsets[i-1].first+0x100 > end)
203                throw BinException("Kernel size is too small!");
204       
205        const size_t regSize = end - symOffsets[i-1].first;
206        if (region.size==0)
207            region.size = regSize;
208        else
209            region.size = std::min(regSize, region.size);
210    }
211   
212    // load got symbols
213    if (relaDynIndex != SHN_UNDEF && gotIndex != SHN_UNDEF)
214    {
215        const Elf64_Shdr& relaShdr = getSectionHeader(relaDynIndex);
216        const Elf64_Shdr& gotShdr = getSectionHeader(gotIndex);
217       
218        size_t relaEntrySize = ULEV(relaShdr.sh_entsize);
219        if (relaEntrySize==0)
220            relaEntrySize = sizeof(Elf64_Rela);
221        const size_t relaEntriesNum = ULEV(relaShdr.sh_size)/relaEntrySize;
222        const size_t gotEntriesNum = ULEV(gotShdr.sh_size) >> 3;
223        if (gotEntriesNum != relaEntriesNum)
224            throw BinException("RelaDyn entries number and GOT entries "
225                        "number doesn't match!");
226       
227        // initialize GOT symbols table
228        gotSymbols.resize(gotEntriesNum);
229        const cxbyte* relaDyn = getSectionContent(relaDynIndex);
230        for (size_t i = 0; i < relaEntriesNum; i++)
231        {
232            const Elf64_Rela& rela = *reinterpret_cast<const Elf64_Rela*>(
233                            relaDyn + relaEntrySize*i);
234            // check rela entry fields
235            if (ULEV(rela.r_offset) != ULEV(gotShdr.sh_offset) + i*8)
236                throw BinException("Wrong dyn relocation offset");
237            if (ULEV(rela.r_addend) != 0ULL)
238                throw BinException("Wrong dyn relocation addend");
239            size_t symIndex = ELF64_R_SYM(ULEV(rela.r_info));
240            if (symIndex >= getDynSymbolsNum())
241                throw BinException("Dyn relocation symbol index out of range");
242            // just set in gotSymbols
243            gotSymbols[i] = symIndex;
244        }
245    }
246   
247    // get metadata
248    const size_t notesSize = getNotesSize();
249    const cxbyte* noteContent = (const cxbyte*)getNotes();
250   
251    for (size_t offset = 0; offset < notesSize; )
252    {
253        const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
254        size_t namesz = ULEV(nhdr->n_namesz);
255        size_t descsz = ULEV(nhdr->n_descsz);
256        if (usumGt(offset, namesz+descsz, notesSize))
257            throw BinException("Note offset+size out of range");
258       
259        const size_t alignedNamesz = ((namesz+3)&~size_t(3));
260        if ((namesz==4 &&
261            ::strcmp((const char*)noteContent+offset+ sizeof(Elf64_Nhdr), "AMD")==0) ||
262            (namesz==7 &&
263            ::strcmp((const char*)noteContent+offset+ sizeof(Elf64_Nhdr), "AMDGPU")==0))
264        {
265            const uint32_t noteType = ULEV(nhdr->n_type);
266            if ((noteType == 0xa && namesz==4) || (noteType == 0x20 && namesz==7))
267            {
268                if (namesz==7)
269                    metadataV3Format = true;
270                if (namesz==4 && metadataV3Format)
271                    throw Exception("MetadataV2 in MetadataV3 compliant binary!");
272                metadata = (char*)(noteContent+offset+sizeof(Elf64_Nhdr) + alignedNamesz);
273                metadataSize = descsz;
274            }
275            else if (noteType == 0xb && namesz==4)
276                target.assign((char*)(noteContent+offset+sizeof(Elf64_Nhdr) +
277                                        alignedNamesz), descsz);
278        }
279        size_t align = (((alignedNamesz+descsz)&3)!=0) ? 4-((alignedNamesz+descsz)&3) : 0;
280        offset += sizeof(Elf64_Nhdr) + alignedNamesz + descsz + align;
281    }
282   
283    if (hasRegionMap())
284    {
285        // create region map
286        regionsMap.resize(regionsNum);
287        for (size_t i = 0; i < regionsNum; i++)
288            regionsMap[i] = std::make_pair(regions[i].regionName, i);
289        // sort region map
290        mapSort(regionsMap.begin(), regionsMap.end());
291    }
292   
293    if ((creationFlags & ROCMBIN_CREATE_METADATAINFO) != 0 &&
294        metadata != nullptr && metadataSize != 0)
295    {
296        metadataInfo.reset(new ROCmMetadata());
297        if (!metadataV3Format)
298            parseROCmMetadata(metadataSize, metadata, *metadataInfo);
299        else
300            parseROCmMetadataMsgPack(metadataSize,
301                    reinterpret_cast<const cxbyte*>(metadata), *metadataInfo);
302       
303        if (hasKernelInfoMap())
304        {
305            const std::vector<ROCmKernelMetadata>& kernels = metadataInfo->kernels;
306            kernelInfosMap.resize(kernels.size());
307            for (size_t i = 0; i < kernelInfosMap.size(); i++)
308                kernelInfosMap[i] = std::make_pair(kernels[i].name, i);
309            // sort region map
310            mapSort(kernelInfosMap.begin(), kernelInfosMap.end());
311        }
312    }
313}
314
315/// determint GPU device from ROCm notes
316GPUDeviceType ROCmBinary::determineGPUDeviceType(uint32_t& outArchMinor,
317                     uint32_t& outArchStepping) const
318{
319    uint32_t archMajor = 0;
320    uint32_t archMinor = 0;
321    uint32_t archStepping = 0;
322   
323    {
324        const cxbyte* noteContent = (const cxbyte*)getNotes();
325        if (noteContent==nullptr)
326            throw BinException("Missing notes in inner binary!");
327        size_t notesSize = getNotesSize();
328        // find note about AMDGPU
329        for (size_t offset = 0; offset < notesSize; )
330        {
331            const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
332            size_t namesz = ULEV(nhdr->n_namesz);
333            size_t descsz = ULEV(nhdr->n_descsz);
334            if (usumGt(offset, namesz+descsz, notesSize))
335                throw BinException("Note offset+size out of range");
336            if (ULEV(nhdr->n_type) == 0x3 && namesz==4 && descsz>=0x1a &&
337                ::strcmp((const char*)noteContent+offset+sizeof(Elf64_Nhdr), "AMD")==0)
338            {    // AMDGPU type
339                const uint32_t* content = (const uint32_t*)
340                        (noteContent+offset+sizeof(Elf64_Nhdr) + 4);
341                archMajor = ULEV(content[1]);
342                archMinor = ULEV(content[2]);
343                archStepping = ULEV(content[3]);
344            }
345            size_t align = (((namesz+descsz)&3)!=0) ? 4-((namesz+descsz)&3) : 0;
346            offset += sizeof(Elf64_Nhdr) + namesz + descsz + align;
347        }
348    }
349    if (llvm10BinFormat && archMajor==0 && archMajor==0 && archStepping==0)
350    {
351        // default is Navi
352        outArchMinor = 1;
353        outArchStepping = 0;
354        return GPUDeviceType::GFX1010;
355    }
356    // determine device type
357    GPUDeviceType deviceType = getGPUDeviceTypeFromArchVersion(archMajor, archMinor,
358                                    archStepping);
359    outArchMinor = archMinor;
360    outArchStepping = archStepping;
361    return deviceType;
362}
363
364const ROCmRegion& ROCmBinary::getRegion(const char* name) const
365{
366    RegionMap::const_iterator it = binaryMapFind(regionsMap.begin(),
367                             regionsMap.end(), name);
368    if (it == regionsMap.end())
369        throw BinException("Can't find region name");
370    return regions[it->second];
371}
372
373const ROCmKernelMetadata& ROCmBinary::getKernelInfo(const char* name) const
374{
375    if (!hasMetadataInfo())
376        throw BinException("Can't find kernel info name");
377    RegionMap::const_iterator it = binaryMapFind(kernelInfosMap.begin(),
378                             kernelInfosMap.end(), name);
379    if (it == kernelInfosMap.end())
380        throw BinException("Can't find kernel info name");
381    return metadataInfo->kernels[it->second];
382}
383
384const ROCmKernelDescriptor* ROCmBinary::getKernelDescriptor(const char* name) const
385{
386    RegionMap::const_iterator it = binaryMapFind(regionsMap.begin(),
387                             regionsMap.end(), name);
388    if (it == regionsMap.end())
389        throw BinException("Can't find kernel descriptor name");
390    return kernelDescs[it->second];
391}
392
393// if ROCm binary
394bool CLRX::isROCmBinary(size_t binarySize, const cxbyte* binary)
395{
396    if (!isElfBinary(binarySize, binary))
397        return false;
398    if (binary[EI_CLASS] != ELFCLASS64)
399        return false;
400    const Elf64_Ehdr* ehdr = reinterpret_cast<const Elf64_Ehdr*>(binary);
401    if (ULEV(ehdr->e_machine) != 0xe0)
402        return false;
403    return true;
404}
405
406
407void ROCmInput::addEmptyKernel(const char* kernelName)
408{
409    symbols.push_back({ kernelName, 0, 0, ROCmRegionType::KERNEL });
410}
411
412/* ROCm section generators */
413
414class CLRX_INTERNAL ROCmGotGen: public ElfRegionContent
415{
416private:
417    const ROCmInput* input;
418public:
419    explicit ROCmGotGen(const ROCmInput* _input) : input(_input)
420    { }
421   
422    void operator()(FastOutputBuffer& fob) const
423    {
424        fob.fill(input->gotSymbols.size()*8, 0);
425    }
426};
427
428class CLRX_INTERNAL ROCmRelaDynGen: public ElfRegionContent
429{
430private:
431    size_t gotOffset;
432    const ROCmInput* input;
433public:
434    explicit ROCmRelaDynGen(const ROCmInput* _input) : gotOffset(0), input(_input)
435    { }
436   
437    void setGotOffset(size_t _gotOffset)
438    { gotOffset = _gotOffset; }
439   
440    void operator()(FastOutputBuffer& fob) const
441    {
442        for (size_t i = 0; i < input->gotSymbols.size(); i++)
443        {
444            size_t symIndex = input->gotSymbols[i];
445            Elf64_Rela rela{};
446            SLEV(rela.r_offset, gotOffset + 8*i);
447            SLEV(rela.r_info, ELF64_R_INFO(symIndex + 1, 3));
448            rela.r_addend = 0;
449            fob.writeObject(rela);
450        }
451    }
452};
453
454class CLRX_INTERNAL ROCmLLVM10GlobalDataGen: public ElfRegionContent
455{
456private:
457    size_t gdataSize;
458    const cxbyte* gdata;
459    ElfBinaryGen64& gen;
460    cxuint gdataIndex;
461    std::vector<size_t> kernelOffsets;
462public:
463    ROCmLLVM10GlobalDataGen(size_t gsize, const cxbyte* _gdata, ElfBinaryGen64& _gen,
464            cxuint _gdataIndex, std::vector<size_t>&& _kernelOffsets) :
465        gdataSize(gsize), gdata(_gdata), gen(_gen), gdataIndex(_gdataIndex)
466    {
467        kernelOffsets = std::move(_kernelOffsets);
468    }
469   
470    void operator()(FastOutputBuffer& fob) const
471    {
472        size_t p = 0;
473        size_t f = 16;
474        size_t kdescOffset = 0;
475        size_t gdataOffset = gen.getRegionOffset(gdataIndex);
476        const size_t codeOffset = gen.getRegionOffset(gdataIndex+1);
477        for (size_t ko: kernelOffsets)
478        {
479            fob.writeArray(std::min(f, gdataSize)-p, gdata+p);
480            uint64_t v = 0;
481            SULEV(v, (codeOffset + ko) - (gdataOffset + kdescOffset));
482            fob.writeObject(v);
483            p = f + 8;
484            f += sizeof(ROCmKernelDescriptor);
485            kdescOffset += sizeof(ROCmKernelDescriptor);
486        }
487        fob.writeArray(gdataSize-p, gdata+p);
488    }
489};
490
491/*
492 * ROCm Binary Generator
493 */
494
495ROCmBinGenerator::ROCmBinGenerator() : manageable(false), input(nullptr),
496                rocmLLVMGDataGen(nullptr)
497{ }
498
499ROCmBinGenerator::ROCmBinGenerator(const ROCmInput* rocmInput)
500        : manageable(false), input(rocmInput), rocmGotGen(nullptr), rocmRelaDynGen(nullptr),
501          rocmLLVMGDataGen(nullptr)
502{ }
503
504ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
505        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
506        size_t globalDataSize, const cxbyte* globalData,
507        const std::vector<ROCmSymbolInput>& symbols) :
508        rocmGotGen(nullptr), rocmRelaDynGen(nullptr), rocmLLVMGDataGen(nullptr)
509{
510    std::unique_ptr<ROCmInput> _input(new ROCmInput{});
511    _input->deviceType = deviceType;
512    _input->archMinor = archMinor;
513    _input->archStepping = archStepping;
514    _input->eflags = 0;
515    _input->newBinFormat = false;
516    _input->llvm10BinFormat = false;
517    _input->metadataV3Format = false;
518    _input->globalDataSize = globalDataSize;
519    _input->globalData = globalData;
520    _input->symbols = symbols;
521    _input->codeSize = codeSize;
522    _input->code = code;
523    _input->commentSize = 0;
524    _input->comment = nullptr;
525    _input->target = "";
526    _input->targetTripple = "";
527    _input->metadataSize = 0;
528    _input->metadata = nullptr;
529    _input->useMetadataInfo = false;
530    _input->metadataInfo = ROCmMetadata{};
531    input = _input.release();
532}
533
534ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
535        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
536        size_t globalDataSize, const cxbyte* globalData,
537        std::vector<ROCmSymbolInput>&& symbols) :
538        rocmGotGen(nullptr), rocmRelaDynGen(nullptr), rocmLLVMGDataGen(nullptr)
539{
540    std::unique_ptr<ROCmInput> _input(new ROCmInput{});
541    _input->deviceType = deviceType;
542    _input->archMinor = archMinor;
543    _input->archStepping = archStepping;
544    _input->eflags = 0;
545    _input->newBinFormat = false;
546    _input->llvm10BinFormat = false;
547    _input->metadataV3Format = false;
548    _input->globalDataSize = globalDataSize;
549    _input->globalData = globalData;
550    _input->symbols = std::move(symbols);
551    _input->codeSize = codeSize;
552    _input->code = code;
553    _input->commentSize = 0;
554    _input->comment = nullptr;
555    _input->target = "";
556    _input->targetTripple = "";
557    _input->metadataSize = 0;
558    _input->metadata = nullptr;
559    _input->useMetadataInfo = false;
560    _input->metadataInfo = ROCmMetadata{};
561    input = _input.release();
562}
563
564ROCmBinGenerator::~ROCmBinGenerator()
565{
566    if (manageable)
567        delete input;
568    if (rocmGotGen!=nullptr)
569        delete (ROCmGotGen*)rocmGotGen;
570    if (rocmRelaDynGen!=nullptr)
571        delete (ROCmRelaDynGen*)rocmRelaDynGen;
572    if (rocmLLVMGDataGen!=nullptr)
573        delete (ROCmLLVM10GlobalDataGen*)rocmLLVMGDataGen;
574}
575
576void ROCmBinGenerator::setInput(const ROCmInput* input)
577{
578    if (manageable)
579        delete input;
580    manageable = false;
581    this->input = input;
582}
583
584// ELF notes contents
585static const cxbyte noteDescType1[8] =
586{ 2, 0, 0, 0, 1, 0, 0, 0 };
587
588static const cxbyte noteDescType3[27] =
589{ 4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
590  'A', 'M', 'D', 0, 'A', 'M', 'D', 'G', 'P', 'U', 0 };
591
592static inline void addMainSectionToTable(cxuint& sectionsNum, uint16_t* builtinTable,
593                cxuint elfSectId)
594{ builtinTable[elfSectId - ELFSECTID_START] = sectionsNum++; }
595
596void ROCmBinGenerator::prepareBinaryGen()
597{
598    if (input->globalData == nullptr && (input->llvm10BinFormat || input->metadataV3Format))
599        throw BinGenException("LLVM10 binary format requires global data");
600   
601    AMDGPUArchVersion amdGpuArchValues = getGPUArchVersion(input->deviceType,
602                GPUArchVersionTable::ROCM);
603    if (input->archMinor!=UINT32_MAX)
604        amdGpuArchValues.minor = input->archMinor;
605    if (input->archStepping!=UINT32_MAX)
606        amdGpuArchValues.stepping = input->archStepping;
607   
608    comment = "CLRX ROCmBinGenerator " CLRX_VERSION;
609    commentSize = ::strlen(comment);
610    if (input->comment!=nullptr)
611    {
612        // if comment, store comment section
613        comment = input->comment;
614        commentSize = input->commentSize;
615        if (commentSize==0)
616            commentSize = ::strlen(comment);
617    }
618   
619    uint32_t eflags = input->newBinFormat ? ((input->llvm10BinFormat) ? 51 : 2) : 0;
620    if (input->eflags != BINGEN_DEFAULT)
621        eflags = input->eflags;
622   
623    std::fill(mainBuiltinSectTable,
624              mainBuiltinSectTable + ROCMSECTID_MAX-ELFSECTID_START+1, SHN_UNDEF);
625    mainSectionsNum = 1;
626   
627    // generate main builtin section table (for section id translation)
628    if (input->newBinFormat)
629        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_NOTE);
630    if (!input->llvm10BinFormat)
631        if (input->globalData != nullptr)
632            addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_RODATA);
633    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_DYNSYM);
634    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_HASH);
635    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_DYNSTR);
636    if (input->llvm10BinFormat)
637        if (input->globalData != nullptr)
638            addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_RODATA);
639    if (!input->gotSymbols.empty())
640        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_RELADYN);
641    const cxuint execProgHeaderRegionIndex = mainSectionsNum;
642    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_TEXT);
643    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_DYNAMIC);
644    if (!input->gotSymbols.empty())
645        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_GOT);
646    if (!input->newBinFormat)
647    {
648        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_NOTE);
649        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_GPUCONFIG);
650    }
651    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_COMMENT);
652    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_SYMTAB);
653    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_SHSTRTAB);
654    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_STRTAB);
655   
656    const cxbyte abiVer = (input->newBinFormat && input->llvm10BinFormat) ? 1 : 0;
657   
658    elfBinGen64.reset(new ElfBinaryGen64({ 0U, 0U, 0x40, abiVer, ET_DYN, 0xe0, EV_CURRENT,
659            cxuint(input->newBinFormat ? execProgHeaderRegionIndex : UINT_MAX), 0, eflags },
660            true, true, true, PHREGION_FILESTART));
661   
662    static const int32_t dynTags[] = {
663        DT_SYMTAB, DT_SYMENT, DT_STRTAB, DT_STRSZ, DT_HASH };
664    elfBinGen64->addDynamics(sizeof(dynTags)/sizeof(int32_t), dynTags);
665   
666    // elf program headers
667    elfBinGen64->addProgramHeader({ PT_PHDR, PF_R, 0, 1,
668                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
669    elfBinGen64->addProgramHeader({ PT_LOAD, PF_R, PHREGION_FILESTART,
670                    execProgHeaderRegionIndex,
671                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 0x1000 });
672    elfBinGen64->addProgramHeader({ PT_LOAD, PF_R|PF_X, execProgHeaderRegionIndex, 1,
673                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
674    elfBinGen64->addProgramHeader({ PT_LOAD, PF_R|PF_W, execProgHeaderRegionIndex+1,
675                    cxuint(1 + (!input->gotSymbols.empty())),
676                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
677    elfBinGen64->addProgramHeader({ PT_DYNAMIC, PF_R|PF_W, execProgHeaderRegionIndex+1, 1,
678                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 8 });
679    elfBinGen64->addProgramHeader({ PT_GNU_RELRO, PF_R, execProgHeaderRegionIndex+1,
680                    cxuint(1 + (!input->gotSymbols.empty())),
681                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 1 });
682    elfBinGen64->addProgramHeader({ PT_GNU_STACK, PF_R|PF_W, PHREGION_FILESTART, 0,
683                    true, 0, 0, 0 });
684   
685    if (input->newBinFormat)
686        // program header for note (new binary format)
687        elfBinGen64->addProgramHeader({ PT_NOTE, PF_R, 1, 1, true,
688                    Elf64Types::nobase, Elf64Types::nobase, 0, 4 });
689   
690    if (!input->llvm10BinFormat)
691    {
692        target = input->target.c_str();
693        if (target.empty() && !input->targetTripple.empty())
694        {
695            target = input->targetTripple.c_str();
696            char dbuf[20];
697            snprintf(dbuf, 20, "-gfx%u%u%u", amdGpuArchValues.major, amdGpuArchValues.minor,
698                    amdGpuArchValues.stepping);
699            target += dbuf;
700        }
701        // elf notes
702        elfBinGen64->addNote({"AMD", sizeof noteDescType1, noteDescType1, 1U});
703        noteBuf.reset(new cxbyte[0x1b]);
704        ::memcpy(noteBuf.get(), noteDescType3, 0x1b);
705        SULEV(*(uint32_t*)(noteBuf.get()+4), amdGpuArchValues.major);
706        SULEV(*(uint32_t*)(noteBuf.get()+8), amdGpuArchValues.minor);
707        SULEV(*(uint32_t*)(noteBuf.get()+12), amdGpuArchValues.stepping);
708        elfBinGen64->addNote({"AMD", 0x1b, noteBuf.get(), 3U});
709        if (!target.empty())
710            elfBinGen64->addNote({"AMD", target.size(), (const cxbyte*)target.c_str(), 0xbU});
711    }
712   
713    metadataSize = input->metadataSize;
714    metadata = input->metadata;
715    if (input->useMetadataInfo)
716    {
717        // generate ROCm metadata
718        std::vector<std::pair<CString, size_t> > symbolIndices(input->symbols.size());
719        // create sorted indices of symbols by its name
720        for (size_t k = 0; k < input->symbols.size(); k++)
721            symbolIndices[k] = std::make_pair(input->symbols[k].symbolName, k);
722        mapSort(symbolIndices.begin(), symbolIndices.end());
723       
724        const size_t mdKernelsNum = input->metadataInfo.kernels.size();
725        if (!input->metadataV3Format)
726        {
727            std::unique_ptr<const ROCmKernelConfig*[]> kernelConfigPtrs(
728                    new const ROCmKernelConfig*[mdKernelsNum]);
729            // generate ROCm kernel config pointers
730            for (size_t k = 0; k < mdKernelsNum; k++)
731            {
732                auto it = binaryMapFind(symbolIndices.begin(), symbolIndices.end(),
733                            input->metadataInfo.kernels[k].name);
734                if (it == symbolIndices.end() ||
735                    (input->symbols[it->second].type != ROCmRegionType::FKERNEL &&
736                    input->symbols[it->second].type != ROCmRegionType::KERNEL))
737                    throw BinGenException("Kernel in metadata doesn't exists in code");
738                kernelConfigPtrs[k] = reinterpret_cast<const ROCmKernelConfig*>(
739                            input->code + input->symbols[it->second].offset);
740            }
741            // just generate ROCm metadata from info
742            generateROCmMetadata(input->metadataInfo, kernelConfigPtrs.get(), metadataStr);
743            metadataSize = metadataStr.size();
744            metadata = metadataStr.c_str();
745        }
746        else
747        {
748            std::unique_ptr<const ROCmKernelDescriptor*[]> kernelDescPtrs(
749                    new const ROCmKernelDescriptor*[mdKernelsNum]);
750            for (size_t k = 0; k < mdKernelsNum; k++)
751                kernelDescPtrs[k] = reinterpret_cast<const ROCmKernelDescriptor*>(
752                            input->globalData + k*sizeof(ROCmKernelDescriptor));
753            // just generate ROCm metadata from info
754            generateROCmMetadataMsgPack(input->metadataInfo,
755                                kernelDescPtrs.get(), metadataBytes);
756            metadataSize = metadataBytes.size();
757            metadata = (const char*)metadataBytes.data();
758        }
759    }
760   
761    if (metadataSize != 0)
762    {
763        if (!input->metadataV3Format)
764            elfBinGen64->addNote({"AMD", metadataSize, (const cxbyte*)metadata, 0xaU});
765        else
766            elfBinGen64->addNote({"AMDGPU", metadataSize, (const cxbyte*)metadata, 0x20U});
767    }
768   
769    /// region and sections
770    elfBinGen64->addRegion(ElfRegion64::programHeaderTable());
771    if (input->newBinFormat)
772        elfBinGen64->addRegion(ElfRegion64::noteSection());
773    if (!input->llvm10BinFormat)
774        if (input->globalData != nullptr)
775            elfBinGen64->addRegion(ElfRegion64(input->globalDataSize, input->globalData, 4,
776                    ".rodata", SHT_PROGBITS, SHF_ALLOC, 0, 0, Elf64Types::nobase));
777   
778    elfBinGen64->addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
779                ".dynsym", SHT_DYNSYM, SHF_ALLOC, 0, BINGEN_DEFAULT, Elf64Types::nobase));
780    elfBinGen64->addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 4,
781                ".hash", SHT_HASH, SHF_ALLOC,
782                mainBuiltinSectTable[ELFSECTID_DYNSYM-ELFSECTID_START], 0,
783                Elf64Types::nobase));
784    elfBinGen64->addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1, ".dynstr", SHT_STRTAB,
785                SHF_ALLOC, 0, 0, Elf64Types::nobase));
786    if (input->llvm10BinFormat)
787        if (input->globalData != nullptr)
788        {
789            std::vector<size_t> koffsets;
790            for (const ROCmSymbolInput& sym: input->symbols)
791            {
792                if (sym.type == ROCmRegionType::KERNEL)
793                    koffsets.push_back(sym.offset);
794            }
795            ROCmLLVM10GlobalDataGen* sgen = new ROCmLLVM10GlobalDataGen(input->globalDataSize,
796                        input->globalData, *elfBinGen64.get(),
797                        mainBuiltinSectTable[ELFSECTID_RODATA-ELFSECTID_START],
798                        std::move(koffsets));
799            rocmLLVMGDataGen = (void*)sgen;
800            elfBinGen64->addRegion(ElfRegion64(input->globalDataSize, sgen, 64,
801                    ".rodata", SHT_PROGBITS, SHF_ALLOC, 0, 0, Elf64Types::nobase));
802        }
803    if (!input->gotSymbols.empty())
804    {
805        ROCmRelaDynGen* sgen = new ROCmRelaDynGen(input);
806        rocmRelaDynGen = (void*)sgen;
807        elfBinGen64->addRegion(ElfRegion64(input->gotSymbols.size()*sizeof(Elf64_Rela),
808                sgen, 8, ".rela.dyn", SHT_RELA, SHF_ALLOC,
809                mainBuiltinSectTable[ELFSECTID_DYNSYM-ELFSECTID_START], 0,
810                Elf64Types::nobase, sizeof(Elf64_Rela)));
811    }
812    // '.text' with alignment=4096
813    elfBinGen64->addRegion(ElfRegion64(input->codeSize, (const cxbyte*)input->code, 
814              0x1000, ".text", SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR, 0, 0,
815              Elf64Types::nobase, 0, false, 256));
816    elfBinGen64->addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 0x1000,
817                ".dynamic", SHT_DYNAMIC, SHF_ALLOC|SHF_WRITE,
818                mainBuiltinSectTable[ELFSECTID_DYNSTR-ELFSECTID_START], 0,
819                Elf64Types::nobase, 0, false, 8));
820    if (!input->gotSymbols.empty())
821    {
822        ROCmGotGen* sgen = new ROCmGotGen(input);
823        rocmGotGen = (void*)sgen;
824        elfBinGen64->addRegion(ElfRegion64(input->gotSymbols.size()*8, sgen,
825                8, ".got", SHT_PROGBITS,
826                SHF_ALLOC|SHF_WRITE, 0, 0, Elf64Types::nobase));
827    }
828    if (!input->newBinFormat)
829    {
830        elfBinGen64->addRegion(ElfRegion64::noteSection());
831        elfBinGen64->addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1,
832                    ".AMDGPU.config", SHT_PROGBITS, 0));
833    }
834    elfBinGen64->addRegion(ElfRegion64(commentSize, (const cxbyte*)comment, 1, ".comment",
835              SHT_PROGBITS, SHF_MERGE|SHF_STRINGS, 0, 0, 0, 1));
836    elfBinGen64->addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
837                ".symtab", SHT_SYMTAB, 0, 0, BINGEN_DEFAULT));
838    elfBinGen64->addRegion(ElfRegion64::shstrtabSection());
839    elfBinGen64->addRegion(ElfRegion64::strtabSection());
840    elfBinGen64->addRegion(ElfRegion64::sectionHeaderTable());
841   
842    /* extra sections */
843    for (const BinSection& section: input->extraSections)
844        elfBinGen64->addRegion(ElfRegion64(section, mainBuiltinSectTable,
845                         ROCMSECTID_MAX, mainSectionsNum));
846    updateSymbols();
847    binarySize = elfBinGen64->countSize();
848   
849    if (rocmRelaDynGen != nullptr)
850        ((ROCmRelaDynGen*)rocmRelaDynGen)->setGotOffset(
851                elfBinGen64->getRegionOffset(
852                        mainBuiltinSectTable[ROCMSECTID_GOT - ELFSECTID_START]));
853}
854
855void ROCmBinGenerator::updateSymbols()
856{
857    elfBinGen64->clearSymbols();
858    elfBinGen64->clearDynSymbols();
859    // add symbols (kernels, function kernels and data symbols)
860    elfBinGen64->addSymbol(ElfSymbol64("_DYNAMIC",
861                  mainBuiltinSectTable[ROCMSECTID_DYNAMIC-ELFSECTID_START],
862                  ELF64_ST_INFO(STB_LOCAL, STT_NOTYPE), STV_HIDDEN, true, 0, 0));
863    const uint16_t textSectIndex = mainBuiltinSectTable[ELFSECTID_TEXT-ELFSECTID_START];
864    const uint16_t rodataSectIndex = mainBuiltinSectTable[ELFSECTID_RODATA-ELFSECTID_START];
865   
866    const cxuint kernelSymType = input->llvm10BinFormat ? STT_FUNC : STT_GNU_IFUNC;
867    const cxuint kernelSymVis = input->llvm10BinFormat ? STV_PROTECTED : STV_DEFAULT;
868    size_t kdOffset = 0;
869    size_t kdescIndex = 0;
870    if (input->llvm10BinFormat)
871        kdescSymNames.resize(input->symbols.size());
872    for (const ROCmSymbolInput& symbol: input->symbols)
873    {
874        ElfSymbol64 elfsym;
875        switch (symbol.type)
876        {
877            case ROCmRegionType::KERNEL:
878                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
879                      ELF64_ST_INFO(STB_GLOBAL, kernelSymType),
880                      ELF64_ST_VISIBILITY(kernelSymVis), true,
881                      symbol.offset, symbol.size);
882                break;
883            case ROCmRegionType::FKERNEL:
884                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
885                      ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), 0, true,
886                      symbol.offset, symbol.size);
887                break;
888            case ROCmRegionType::DATA:
889                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
890                      ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), 0, true,
891                      symbol.offset, symbol.size);
892                break;
893            default:
894                break;
895        }
896        // add to symbols and dynamic symbols table
897        elfBinGen64->addSymbol(elfsym);
898        elfBinGen64->addDynSymbol(elfsym);
899        if (symbol.type == ROCmRegionType::KERNEL && input->llvm10BinFormat)
900        {
901            std::string kdsym(symbol.symbolName.c_str());
902            kdsym += ".kd";
903            kdescSymNames[kdescIndex] = kdsym;
904            elfsym = ElfSymbol64(kdescSymNames[kdescIndex].c_str(), rodataSectIndex,
905                      ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT),
906                      ELF64_ST_VISIBILITY(kernelSymVis), true, kdOffset, symbol.size);
907            kdOffset += sizeof(ROCmKernelDescriptor);
908            elfBinGen64->addSymbol(elfsym);
909            elfBinGen64->addDynSymbol(elfsym);
910            kdescIndex++;
911        }
912    }
913    /* extra symbols */
914    for (const BinSymbol& symbol: input->extraSymbols)
915    {
916        ElfSymbol64 sym(symbol, mainBuiltinSectTable,
917                         ROCMSECTID_MAX, mainSectionsNum);
918        elfBinGen64->addSymbol(sym);
919        elfBinGen64->addDynSymbol(sym);
920    }
921}
922
923void ROCmBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
924             Array<cxbyte>* aPtr)
925{
926    if (elfBinGen64 == nullptr)
927        prepareBinaryGen();
928    /****
929     * prepare for write binary to output
930     ****/
931    std::unique_ptr<std::ostream> outStreamHolder;
932    std::ostream* os = nullptr;
933    if (aPtr != nullptr)
934    {
935        aPtr->resize(binarySize);
936        outStreamHolder.reset(
937                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
938        os = outStreamHolder.get();
939    }
940    else if (vPtr != nullptr)
941    {
942        vPtr->resize(binarySize);
943        outStreamHolder.reset(new VectorOStream(*vPtr));
944        os = outStreamHolder.get();
945    }
946    else // from argument
947        os = osPtr;
948   
949    const std::ios::iostate oldExceptions = os->exceptions();
950    try
951    {
952    os->exceptions(std::ios::failbit | std::ios::badbit);
953    /****
954     * write binary to output
955     ****/
956    FastOutputBuffer bos(256, *os);
957    elfBinGen64->generate(bos);
958    assert(bos.getWritten() == binarySize);
959   
960    if (rocmGotGen != nullptr)
961    {
962        delete (ROCmGotGen*)rocmGotGen;
963        rocmGotGen = nullptr;
964    }
965    if (rocmRelaDynGen != nullptr)
966    {
967        delete (ROCmGotGen*)rocmRelaDynGen;
968        rocmRelaDynGen = nullptr;
969    }
970    }
971    catch(...)
972    {
973        os->exceptions(oldExceptions);
974        throw;
975    }
976    os->exceptions(oldExceptions);
977}
978
979void ROCmBinGenerator::generate(Array<cxbyte>& array)
980{
981    generateInternal(nullptr, nullptr, &array);
982}
983
984void ROCmBinGenerator::generate(std::ostream& os)
985{
986    generateInternal(&os, nullptr, nullptr);
987}
988
989void ROCmBinGenerator::generate(std::vector<char>& v)
990{
991    generateInternal(nullptr, &v, nullptr);
992}
Note: See TracBrowser for help on using the repository browser.