+// <rdar://problem/9218847> Need way to formalize data in code
+template <typename A>
+class DataInCodeAtom : public LinkEditAtom
+{
+public:
+ DataInCodeAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "data-in-code info"; }
+ // overrides of LinkEditAtom
+ virtual void encode() const;
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ struct FixupByAddressSorter
+ {
+ bool operator()(const ld::Fixup* left, const ld::Fixup* right)
+ {
+ return (left->offsetInAtom < right->offsetInAtom);
+ }
+ };
+
+ void encodeEntry(uint32_t startImageOffset, int len, ld::Fixup::Kind kind) const {
+ //fprintf(stderr, "encodeEntry(start=0x%08X, len=0x%04X, kind=%04X\n", startImageOffset, len, kind);
+ do {
+ macho_data_in_code_entry<P> entry;
+ entry.set_offset(startImageOffset);
+ entry.set_length(len);
+ switch ( kind ) {
+ case ld::Fixup::kindDataInCodeStartData:
+ entry.set_kind(DICE_KIND_DATA);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT8:
+ entry.set_kind(DICE_KIND_JUMP_TABLE8);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT16:
+ entry.set_kind(DICE_KIND_JUMP_TABLE16);
+ break;
+ case ld::Fixup::kindDataInCodeStartJT32:
+ entry.set_kind(DICE_KIND_JUMP_TABLE32);
+ break;
+ case ld::Fixup::kindDataInCodeStartJTA32:
+ entry.set_kind(DICE_KIND_ABS_JUMP_TABLE32);
+ break;
+ default:
+ assert(0 && "bad L$start$ label to encode");
+ }
+ uint8_t* bp = (uint8_t*)&entry;
+ this->_encodedData.append_byte(bp[0]);
+ this->_encodedData.append_byte(bp[1]);
+ this->_encodedData.append_byte(bp[2]);
+ this->_encodedData.append_byte(bp[3]);
+ this->_encodedData.append_byte(bp[4]);
+ this->_encodedData.append_byte(bp[5]);
+ this->_encodedData.append_byte(bp[6]);
+ this->_encodedData.append_byte(bp[7]);
+ // in rare case data range is huge, create multiple entries
+ len -= 0xFFF8;
+ startImageOffset += 0xFFF8;
+ } while ( len > 0 );
+ }
+
+ static ld::Section _s_section;
+};
+
+template <typename A>
+ld::Section DataInCodeAtom<A>::_s_section("__LINKEDIT", "__dataInCode", ld::Section::typeLinkEdit, true);
+
+
+template <typename A>
+void DataInCodeAtom<A>::encode() const
+{
+ if ( this->_writer.hasDataInCode ) {
+ uint64_t mhAddress = 0;
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->type() == ld::Section::typeMachHeader )
+ mhAddress = sect->address;
+ if ( sect->type() != ld::Section::typeCode )
+ continue;
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ // gather all code-in-data labels
+ std::vector<const ld::Fixup*> dataInCodeLabels;
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ switch ( fit->kind ) {
+ case ld::Fixup::kindDataInCodeStartData:
+ case ld::Fixup::kindDataInCodeStartJT8:
+ case ld::Fixup::kindDataInCodeStartJT16:
+ case ld::Fixup::kindDataInCodeStartJT32:
+ case ld::Fixup::kindDataInCodeStartJTA32:
+ case ld::Fixup::kindDataInCodeEnd:
+ dataInCodeLabels.push_back(fit);
+ break;
+ default:
+ break;
+ }
+ }
+ // to do: sort labels by address
+ std::sort(dataInCodeLabels.begin(), dataInCodeLabels.end(), FixupByAddressSorter());
+
+ // convert to array of struct data_in_code_entry
+ ld::Fixup::Kind prevKind = ld::Fixup::kindDataInCodeEnd;
+ uint32_t prevOffset = 0;
+ for ( std::vector<const ld::Fixup*>::iterator sfit = dataInCodeLabels.begin(); sfit != dataInCodeLabels.end(); ++sfit) {
+ if ( ((*sfit)->kind != prevKind) && (prevKind != ld::Fixup::kindDataInCodeEnd) ) {
+ int len = (*sfit)->offsetInAtom - prevOffset;
+ if ( len == 0 )
+ warning("multiple L$start$ labels found at same address in %s at offset 0x%04X", atom->name(), prevOffset);
+ this->encodeEntry(atom->finalAddress()+prevOffset-mhAddress, (*sfit)->offsetInAtom - prevOffset, prevKind);
+ }
+ prevKind = (*sfit)->kind;
+ prevOffset = (*sfit)->offsetInAtom;
+ }
+ if ( prevKind != ld::Fixup::kindDataInCodeEnd ) {
+ // add entry if function ends with data
+ this->encodeEntry(atom->finalAddress()+prevOffset-mhAddress, atom->size() - prevOffset, prevKind);
+ }
+ }
+ }
+ }
+
+ this->_encoded = true;
+}
+
+
+
+
+
+
+template <typename A>
+class OptimizationHintsAtom : public LinkEditAtom
+{
+public:
+ OptimizationHintsAtom(const Options& opts, ld::Internal& state, OutputFile& writer)
+ : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) {
+ assert(opts.outputKind() == Options::kObjectFile);
+ }
+
+ // overrides of ld::Atom
+ virtual const char* name() const { return "linker optimization hints"; }
+ // overrides of LinkEditAtom
+ virtual void encode() const;
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ static ld::Section _s_section;
+
+};
+
+template <typename A>
+ld::Section OptimizationHintsAtom<A>::_s_section("__LINKEDIT", "__opt_hints", ld::Section::typeLinkEdit, true);
+
+template <typename A>
+void OptimizationHintsAtom<A>::encode() const
+{
+ if ( _state.someObjectHasOptimizationHints ) {
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->type() != ld::Section::typeCode )
+ continue;
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ uint64_t address = atom->finalAddress();
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
+ if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint)
+ continue;
+ ld::Fixup::LOH_arm64 extra;
+ extra.addend = fit->u.addend;
+ _encodedData.append_uleb128(extra.info.kind);
+ _encodedData.append_uleb128(extra.info.count+1);
+ _encodedData.append_uleb128((extra.info.delta1 << 2) + fit->offsetInAtom + address);
+ if ( extra.info.count > 0 )
+ _encodedData.append_uleb128((extra.info.delta2 << 2) + fit->offsetInAtom + address);
+ if ( extra.info.count > 1 )
+ _encodedData.append_uleb128((extra.info.delta3 << 2) + fit->offsetInAtom + address);
+ if ( extra.info.count > 2 )
+ _encodedData.append_uleb128((extra.info.delta4 << 2) + fit->offsetInAtom + address);
+ }
+ }
+ }
+
+ this->_encodedData.pad_to_size(sizeof(pint_t));
+ }
+
+ this->_encoded = true;
+}
+