You could always get the shell to expand the wildcards.
With bash:
(
shopt -s nullglob
IFS=$'\n'
set -- $(<files) # split+glob
(($# == 0)) || printf '%s\n' "$@"
) | zip -@ out
With zsh (also removing duplicates with the u flag while we're at it):
print -rC1 -- ${(u)~${(f)"$(<files)"}/%/(N)} | zip -@ out
That assumes the file names don't contain newline characters.
With bsdtar:
(
shopt -s nullglob
IFS=$'\n'
set -- $(<files) # split+glob
(($# == 0)) || printf '%s\0' "$@"
) | bsdtar --null --format zip -T - -cf out.zip
(withzsh:
print -rNC1 -- ${(u)~${(f)"$(<files)"}/%/(N)} |
bsdtar --null --format zip -T - -cf out.zip
With bsdtar, you could also use more modern/useful archive formats than infozip's).
This time it's fine if file names contain newlines (though obviously the pattern themselves can't as there's one per line of files with no way to escape it).
Note that both shells have extensions over the standard shell wildcard operators (the list of which varies with the shells and the enabled glob options for each).