18cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/**
28cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @file libutil++/op_spu_bfd.cpp
38cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * Encapsulation of bfd objects for Cell BE SPU
48cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd *
58cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @remark Copyright 2007 OProfile authors
68cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @remark Read the file COPYING
78cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd *
88cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @author Maynard Johnson
98cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * (C) Copyright IBM Corporation 2007
108cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */
118cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
128cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
138cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <fcntl.h>
148cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <sys/stat.h>
158cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <cstdlib>
168cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <cstring>
178cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
188cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <iostream>
198cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <cstring>
208cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <cstdlib>
218cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
228cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "op_bfd.h"
238cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "locate_images.h"
248cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "op_libiberty.h"
258cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "string_filter.h"
268cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "cverb.h"
278cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
288cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#define OP_SPU_DYN_FLAG		0x10000000	/* kernel module adds this offset */
298cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd						/* to SPU code it can't find in the map */
308cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#define OP_SPU_MEMSIZE		0x3ffff		/* Physical memory size on an SPU */
318cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
328cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddusing namespace std;
338cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
348cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddextern verbose vbfd;
358cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
368cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/*
378cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * This overload of the op_bfd constructor is patterned after the
388cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * constructor in libutil++/op_bfd.cpp, with the additional processing
398cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * needed to handle an embedded spu offset.
408cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */
418cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddop_bfd::op_bfd(uint64_t spu_offset, string const & fname,
428cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	       string_filter const & symbol_filter,
438cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	       extra_images const & extra_images, bool & ok)
448cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	:
458cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	archive_path(extra_images.get_archive_path()),
468cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	extra_found_images(extra_images),
478cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	file_size(-1),
488cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	embedding_filename(fname),
498cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	anon_obj(false)
508cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{
518cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	int fd;
528cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	struct stat st;
538cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	int notes_remaining;
548cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	bool spu_note_found = false;
558cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	size_t sec_size = 0;
568cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	unsigned int oct_per_byte;
578cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	asection * note = NULL;
588cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
598cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	symbols_found_t symbols;
608cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	asection const * sect;
618cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
628cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	image_error image_ok;
638cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	string const image_path =
648cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		extra_images.find_image_path(fname, image_ok, true);
658cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
668cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	cverb << vbfd << "op_bfd ctor for " << image_path << endl;
678cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	if (!ok)
688cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		goto out_fail;
698cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
708cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	fd = open(image_path.c_str(), O_RDONLY);
718cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	if (fd == -1) {
728cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		cverb << vbfd << "open failed for " << image_path << endl;
738cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		ok = false;
748cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		goto out_fail;
758cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	}
768cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
778cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	if (fstat(fd, &st)) {
788cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		cverb << vbfd << "stat failed for " << image_path << endl;
798cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		ok = false;
808cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		goto out_fail;
818cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	}
828cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
838cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	file_size = st.st_size;
848cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	ibfd.abfd = spu_open_bfd(image_path, fd, spu_offset);
858cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
868cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	if (!ibfd.valid()) {
878cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		cverb << vbfd << "fdopen_bfd failed for " << image_path << endl;
888cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		ok = false;
898cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		goto out_fail;
908cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	}
918cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
928cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	/* For embedded SPU ELF, a note section named '.note.spu_name'
938cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	 * contains the name of the SPU binary image in the description
948cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	 * field.
958cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	 */
968cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	note = bfd_get_section_by_name(ibfd.abfd, ".note.spu_name");
978cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	if (!note) {
988cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		cverb << vbfd << "No .note.spu-name section found" << endl;
998cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		goto find_sec_code;
1008cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	}
1018cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	cverb << vbfd << "found .note.spu_name section" << endl;
1028cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
1038cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	bfd_byte * sec_contents;
1048cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	oct_per_byte = bfd_octets_per_byte(ibfd.abfd);
1058cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	sec_size = bfd_section_size(ibfd.abfd, note)/oct_per_byte;
1068cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
1078cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	sec_contents = (bfd_byte *) xmalloc(sec_size);
1088cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	if (!bfd_get_section_contents(ibfd.abfd, note, sec_contents,
1098cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd				      0, sec_size)) {
1108cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		cverb << vbfd << "bfd_get_section_contents with size "
1118cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		      << sec_size << " returned an error" << endl;
1128cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		ok = false;
1138cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		goto out_fail;
1148cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	}
1158cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	notes_remaining = sec_size;
1168cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	while (notes_remaining && !spu_note_found) {
1178cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		unsigned int  nsize, dsize, type;
1188cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		nsize = *((unsigned int *) sec_contents);
1198cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		dsize = *((unsigned int *) sec_contents +1);
1208cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		type = *((unsigned int *) sec_contents +2);
1218cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		int remainder, desc_start, name_pad_length, desc_pad_length;
1228cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		name_pad_length = desc_pad_length = 0;
1238cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		/* Calculate padding for 4-byte alignment */
1248cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		remainder = nsize % 4;
1258cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		if (remainder != 0)
1268cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			name_pad_length = 4 - remainder;
1278cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		desc_start = 12 + nsize + name_pad_length;
1288cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		if (type != 1) {
1298cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			int note_record_length;
1308cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			if ((remainder = (dsize % 4)) != 0)
1318cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd				desc_pad_length = 4 - remainder;
1328cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			note_record_length = 12 + nsize +
1338cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd				name_pad_length + dsize + desc_pad_length;
1348cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			notes_remaining -= note_record_length;
1358cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			sec_contents += note_record_length;
1368cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			continue;
1378cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		} else {
1388cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			spu_note_found = true;
1398cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			/* Must memcpy the data from sec_contents to a
1408cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			 * 'char *' first, then stringify it, since
1418cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			 * the type of sec_contents (bfd_byte *) cannot be
1428cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			 * used as input for creating a string.
1438cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			 */
1448cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			char * description = (char *) xmalloc(dsize);
1458cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			memcpy(description, sec_contents + desc_start, dsize);
1468cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			filename = description;
1478cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			free(description);
1488cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		}
1498cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	}
1508cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	free(sec_contents);
1518cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	/* Default to app name for the image name */
1528cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	if (spu_note_found == false)
1538cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		filename = fname;
1548cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
1558cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddfind_sec_code:
1568cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	for (sect = ibfd.abfd->sections; sect; sect = sect->next) {
1578cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		if (sect->flags & SEC_CODE) {
1588cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			if (filepos_map[sect->name] != 0) {
1598cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd				cerr << "Found section \"" << sect->name
1608cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd				     << "\" twice for " << get_filename()
1618cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd				     << endl;
1628cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd				abort();
1638cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			}
1648cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
1658cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			filepos_map[sect->name] = sect->filepos;
1668cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd		}
1678cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	}
1688cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
1698cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	get_symbols(symbols);
1708cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
1718cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	/* In some cases the SPU library code generates code stubs on the stack. */
1728cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	/* The kernel module remaps those addresses so add an entry to catch/report them. */
1738cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	symbols.push_back(op_bfd_symbol(OP_SPU_DYN_FLAG, OP_SPU_MEMSIZE,
1748cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd			  "__send_to_ppe(stack)"));
1758cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
1768cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddout:
1778cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	add_symbols(symbols, symbol_filter);
1788cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	return;
1798cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddout_fail:
1808cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	ibfd.close();
1818cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	dbfd.close();
1828cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	file_size = -1;
1838cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd	goto out;
1848cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd}
1858cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd
186