cbfstool: Make add-stage support multiple ignore sections

For x86 eXecute-In-Place (XIP) .data section support, cbfstool need to
to skip relocation of the .data section symbols in addition to
.car.data section symbols.

To support this requirement, this patch makes the `-S` option take a
multiple section names separated by commas.

TEST=With `-S ".car.data .data"`, XIP pre-memory stages with
     a `.data` section do not have any of the `.car.data` or `.data`
     section symbols relocated.

Change-Id: Icf09ee5a318e37c5da94bba6c0a0f39485963d3a
Signed-off-by: Jeremy Compostella <jeremy.compostella@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/77560
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/util/cbfstool/cbfs-mkstage.c b/util/cbfstool/cbfs-mkstage.c
index be85738..54150cc 100644
--- a/util/cbfstool/cbfs-mkstage.c
+++ b/util/cbfstool/cbfs-mkstage.c
@@ -10,47 +10,66 @@
 #include "cbfs.h"
 #include "rmodule.h"
 
-/* Checks if program segment contains the ignored section */
-static int is_phdr_ignored(Elf64_Phdr *phdr, Elf64_Shdr *shdr)
+/* Checks if program segment contains the ignored sections */
+static int is_phdr_ignored(Elf64_Phdr *phdr, Elf64_Shdr **shdrs)
 {
 	/* If no ignored section, return false. */
-	if (shdr == NULL)
+	if (shdrs == NULL)
 		return 0;
 
-	Elf64_Addr sh_start = shdr->sh_addr;
-	Elf64_Addr sh_end = shdr->sh_addr + shdr->sh_size;
-	Elf64_Addr ph_start = phdr->p_vaddr;
-	Elf64_Addr ph_end = phdr->p_vaddr + phdr->p_memsz;
+	while (*shdrs) {
+		Elf64_Addr sh_start = (*shdrs)->sh_addr;
+		Elf64_Addr sh_end = (*shdrs)->sh_addr + (*shdrs)->sh_size;
+		Elf64_Addr ph_start = phdr->p_vaddr;
+		Elf64_Addr ph_end = phdr->p_vaddr + phdr->p_memsz;
 
-	/* Return true only if section occupies whole of segment. */
-	if ((sh_start == ph_start) && (sh_end == ph_end)) {
-		DEBUG("Ignoring program segment at 0x%" PRIx64 "\n", ph_start);
-		return 1;
-	}
+		/* Return true only if section occupies whole of segment. */
+		if ((sh_start == ph_start) && (sh_end == ph_end)) {
+			DEBUG("Ignoring program segment at 0x%" PRIx64 "\n", ph_start);
+			return 1;
+		}
 
-	/* If shdr intersects phdr at all, its a conflict */
-	if (((sh_start >= ph_start) && (sh_start <= ph_end)) ||
-	    ((sh_end >= ph_start) && (sh_end <= ph_end))) {
-		ERROR("Conflicting sections in segment\n");
-		exit(1);
+		/* If shdr intersects phdr at all, its a conflict */
+		if (((sh_start >= ph_start) && (sh_start <= ph_end)) ||
+		    ((sh_end >= ph_start) && (sh_end <= ph_end))) {
+			ERROR("Conflicting sections in segment\n");
+			exit(1);
+		}
+		shdrs++;
 	}
 
 	/* Program header doesn't need to be ignored. */
 	return 0;
 }
 
-/* Find section header based on ignored section name */
-static Elf64_Shdr *find_ignored_section_header(struct parsed_elf *pelf,
-					       const char *ignore_section)
+/* Sections to be ignored are comma separated  */
+static bool is_ignored_sections(const char *section_name,
+				const char *ignore_sections)
+{
+	const char *cur, *comma;
+
+	for (cur = ignore_sections; (comma = strchr(cur, ',')); cur = comma + 1)
+		if (!strncmp(cur, section_name, comma - cur))
+			return true;
+	return !strcmp(cur, section_name);
+}
+
+/* Find section headers based on ignored section names.
+ * Returns a NULL-terminated list of section headers.
+ */
+static Elf64_Shdr **find_ignored_sections_header(struct parsed_elf *pelf,
+						 const char *ignore_sections)
 {
 	int i;
 	const char *shstrtab;
+	Elf64_Shdr **headers = NULL;
+	size_t size = 1;
 
 	/* No section needs to be ignored */
-	if (ignore_section == NULL)
+	if (ignore_sections == NULL)
 		return NULL;
 
-	DEBUG("Section to be ignored: %s\n", ignore_section);
+	DEBUG("Sections to be ignored: %s\n", ignore_sections);
 
 	/* Get pointer to string table */
 	shstrtab = buffer_get(pelf->strtabs[pelf->ehdr.e_shstrndx]);
@@ -62,13 +81,20 @@
 		shdr = &pelf->shdr[i];
 		section_name = &shstrtab[shdr->sh_name];
 
-		/* If section name matches ignored string, return shdr */
-		if (strcmp(section_name, ignore_section) == 0)
-			return shdr;
+		/* If section name matches ignored string, add to list */
+		if (is_ignored_sections(section_name, ignore_sections)) {
+			headers = realloc(headers, sizeof(*headers) * ++size);
+			if (!headers) {
+				ERROR("Memory allocation failed\n");
+				exit(1);
+			}
+			headers[size - 2] = shdr;
+		}
 	}
 
-	/* No section matches ignore string */
-	return NULL;
+	if (headers)
+		headers[size - 1] = NULL;
+	return headers;
 }
 
 static int fill_cbfs_stageheader(struct cbfs_file_attr_stageheader *stageheader,
@@ -98,7 +124,7 @@
 	struct parsed_elf pelf;
 	Elf64_Phdr *phdr;
 	Elf64_Ehdr *ehdr;
-	Elf64_Shdr *shdr_ignored;
+	Elf64_Shdr **shdrs_ignored;
 	Elf64_Addr virt_to_phys;
 	int ret = -1;
 
@@ -116,17 +142,17 @@
 	ehdr = &pelf.ehdr;
 	phdr = &pelf.phdr[0];
 
-	/* Find the section header corresponding to ignored-section */
-	shdr_ignored = find_ignored_section_header(&pelf, ignore_section);
+	/* Find the section headers corresponding to ignored-sections */
+	shdrs_ignored = find_ignored_sections_header(&pelf, ignore_section);
 
-	if (ignore_section && (shdr_ignored == NULL))
-		WARN("Ignore section not found\n");
+	if (ignore_section && (shdrs_ignored == NULL))
+		WARN("Ignore section(s) not found\n");
 
 	headers = ehdr->e_phnum;
 
 	/* Ignore the program header containing ignored section */
 	for (i = 0; i < headers; i++) {
-		if (is_phdr_ignored(&phdr[i], shdr_ignored))
+		if (is_phdr_ignored(&phdr[i], shdrs_ignored))
 			phdr[i].p_type = PT_NULL;
 	}
 
@@ -217,8 +243,7 @@
 
 struct xip_context {
 	struct rmod_context rmodctx;
-	size_t ignored_section_idx;
-	Elf64_Shdr *ignored_section;
+	Elf64_Shdr **ignored_sections;
 };
 
 static int rmod_filter(struct reloc_filter *f, const Elf64_Rela *r)
@@ -228,12 +253,13 @@
 	struct parsed_elf *pelf;
 	Elf64_Sym *sym;
 	struct xip_context *xipctx;
+	Elf64_Shdr **sections;
 
 	xipctx = f->context;
 	pelf = &xipctx->rmodctx.pelf;
 
 	/* Allow everything through if there isn't an ignored section. */
-	if (xipctx->ignored_section == NULL)
+	if (xipctx->ignored_sections == NULL)
 		return 1;
 
 	reloc_type = ELF64_R_TYPE(r->r_info);
@@ -241,8 +267,11 @@
 	sym = &pelf->syms[symbol_index];
 
 	/* Nothing to filter. Relocation is not being applied to the
-	 * ignored section. */
-	if (sym->st_shndx != xipctx->ignored_section_idx)
+	 * ignored sections. */
+	for (sections = xipctx->ignored_sections; *sections; sections++)
+		if (sym->st_shndx == *sections - pelf->shdr)
+			break;
+	if (!*sections)
 		return 1;
 
 	/* If there is any relocation to the ignored section that isn't
@@ -255,12 +284,12 @@
 		return -1;
 	}
 
-	/* Relocation referencing ignored section. Don't emit it. */
+	/* Relocation referencing ignored sections. Don't emit it. */
 	return 0;
 }
 
 int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
-			   uint32_t location, const char *ignore_section,
+			   uint32_t location, const char *ignore_sections,
 			   struct cbfs_file_attr_stageheader *stageheader)
 {
 	struct xip_context xipctx;
@@ -273,7 +302,6 @@
 	Elf64_Xword i;
 	int ret = -1;
 
-	xipctx.ignored_section_idx = 0;
 	rmodctx = &xipctx.rmodctx;
 	pelf = &rmodctx->pelf;
 
@@ -287,12 +315,8 @@
 		goto out;
 	}
 
-	xipctx.ignored_section =
-		find_ignored_section_header(pelf, ignore_section);
-
-	if (xipctx.ignored_section != NULL)
-		xipctx.ignored_section_idx =
-			xipctx.ignored_section - pelf->shdr;
+	xipctx.ignored_sections =
+		find_ignored_sections_header(pelf, ignore_sections);
 
 	filter.filter = rmod_filter;
 	filter.context = &xipctx;
diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c
index c825a13..3df7b52 100644
--- a/util/cbfstool/cbfstool.c
+++ b/util/cbfstool/cbfstool.c
@@ -46,7 +46,7 @@
 	const char *region_name;
 	const char *source_region;
 	const char *bootblock;
-	const char *ignore_section;
+	const char *ignore_sections;
 	const char *ucode_region;
 	uint64_t u64val;
 	uint32_t type;
@@ -1179,9 +1179,9 @@
 		uint32_t host_space_address = convert_addr_space(param.image_region, *offset);
 		assert(IS_HOST_SPACE_ADDRESS(host_space_address));
 		ret = parse_elf_to_xip_stage(buffer, &output, host_space_address,
-					     param.ignore_section, stageheader);
+					     param.ignore_sections, stageheader);
 	} else {
-		ret = parse_elf_to_stage(buffer, &output, param.ignore_section,
+		ret = parse_elf_to_stage(buffer, &output, param.ignore_sections,
 					 stageheader);
 	}
 	if (ret != 0)
@@ -1968,7 +1968,8 @@
 	     "        (linux specific: [-C cmdline] [-I initrd])           "
 			"Add a payload to the ROM\n"
 	     " add-stage [-r image,regions] -f FILE -n NAME [-A hash] \\\n"
-	     "        [-c compression] [-b base] [-S section-to-ignore] \\\n"
+	     "        [-c compression] [-b base] \\\n"
+	     "        [-S comma-separated-section(s)-to-ignore] \\\n"
 	     "        [-a alignment] [-Q|--pow2page] \\\n"
 	     "        [-y|--xip] [--ibb]                                \\\n"
 	     "        [--ext-win-base win-base --ext-win-size win-size]     "
@@ -2271,7 +2272,7 @@
 				param.cmdline = optarg;
 				break;
 			case 'S':
-				param.ignore_section = optarg;
+				param.ignore_sections = optarg;
 				break;
 			case 'y':
 				param.stage_xip = true;