use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::process::Command;
use object::{
write, Object, ObjectComdat, ObjectKind, ObjectSection, ObjectSymbol, RelocationTarget,
SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolSection,
};
use crate::cms::sign_sm2_cms_with_artifacts;
use crate::error::Error;
use crate::plain::{build_plain_bin, TA_SIGNATURE_SECTION};
use crate::{CmsSignAlgorithm, SignInputs};
pub fn write_outputs(
elf_path: &Path,
out_dir: &Path,
leaf_cert_der: &[u8],
intermediate_certs_der: &[Vec<u8>],
leaf_key_path: &Path,
leaf_key_pass: &str,
gmssl_path: Option<&Path>,
algorithm: CmsSignAlgorithm,
) -> Result<(), Error> {
fs::create_dir_all(out_dir)?;
let elf_bytes = fs::read(elf_path)?;
let plain = build_plain_bin(&elf_bytes)?;
fs::write(out_dir.join("plain.bin"), &plain)?;
let gmssl_resolved = crate::gmssl_cli::resolve_gmssl_path(gmssl_path);
let intermediate_refs: Vec<&[u8]> = intermediate_certs_der.iter().map(|v| v.as_slice()).collect();
let out = sign_sm2_cms_with_artifacts(SignInputs {
plain: &plain,
leaf_cert_der,
intermediate_certs_der: &intermediate_refs,
cms_attached: false,
cms_use_gmssl_oid: false,
leaf_key_path,
leaf_key_pass,
gmssl_path: Some(gmssl_resolved.as_path()),
algorithm,
})?;
fs::write(out_dir.join("signed_attrs.der"), out.signed_attrs_der)?;
fs::write(out_dir.join("signature.bin"), out.signature_der)?;
Ok(())
}
pub fn append_ta_signature(
input_elf: &Path,
signature_bin: &Path,
output_elf: &Path,
) -> Result<(), Error> {
append_ta_signature_objcopy(input_elf, signature_bin, output_elf, "objcopy")
}
pub fn append_ta_signature_objcopy(
input_elf: &Path,
signature_bin: &Path,
output_elf: &Path,
objcopy: &str,
) -> Result<(), Error> {
let section_arg = format!("{TA_SIGNATURE_SECTION}={}", signature_bin.to_string_lossy());
let flags_arg = format!("{TA_SIGNATURE_SECTION}=noload");
let st = Command::new(objcopy)
.args([
"--add-section",
§ion_arg,
"--set-section-flags",
&flags_arg,
&input_elf.to_string_lossy(),
&output_elf.to_string_lossy(),
])
.status()
.map_err(Error::Io)?;
if !st.success() {
return Err(Error::ElfWrite(format!(
"{objcopy} --add-section/--set-section-flags failed: {st}"
)));
}
Ok(())
}
pub fn append_ta_signature_object(
input_elf: &Path,
signature_bin: &Path,
output_elf: &Path,
) -> Result<(), Error> {
let elf_bytes = fs::read(input_elf)?;
let sig = fs::read(signature_bin)?;
let out = append_ta_signature_bytes(&elf_bytes, &sig)?;
fs::write(output_elf, &out)?;
Ok(())
}
pub fn normalize_relocatable_elf_bytes(elf_bytes: &[u8]) -> Result<Vec<u8>, Error> {
relocatable_copy_elf(
elf_bytes,
RelocElfOpts {
skip_ta_signature: false,
append_ta_signature: None,
},
)
}
pub fn strip_ta_signature_relocatable_bytes(elf_bytes: &[u8]) -> Result<Vec<u8>, Error> {
let normalized = normalize_relocatable_elf_bytes(elf_bytes)?;
relocatable_copy_elf(
&normalized,
RelocElfOpts {
skip_ta_signature: true,
append_ta_signature: None,
},
)
}
pub fn append_ta_signature_bytes(elf_bytes: &[u8], signature_der: &[u8]) -> Result<Vec<u8>, Error> {
relocatable_copy_elf(
elf_bytes,
RelocElfOpts {
skip_ta_signature: false,
append_ta_signature: Some(signature_der),
},
)
}
struct RelocElfOpts<'a> {
skip_ta_signature: bool,
append_ta_signature: Option<&'a [u8]>,
}
fn relocatable_copy_elf(in_data: &[u8], opts: RelocElfOpts<'_>) -> Result<Vec<u8>, Error> {
let in_object = object::File::parse(in_data).map_err(|e| Error::ElfWrite(e.to_string()))?;
if in_object.kind() != ObjectKind::Relocatable {
return Err(Error::ElfWrite(format!(
"仅支持 relocatable 对象文件(ET_REL),当前: {:?}",
in_object.kind()
)));
}
let mut out_object = write::Object::new(
in_object.format(),
in_object.architecture(),
in_object.endianness(),
);
out_object.mangling = write::Mangling::None;
out_object.flags = in_object.flags();
let mut out_sections: HashMap<object::SectionIndex, write::SectionId> = HashMap::new();
for in_section in in_object.sections() {
if in_section.kind() == SectionKind::Metadata {
continue;
}
let name = in_section.name().unwrap_or("");
if opts.skip_ta_signature && name == TA_SIGNATURE_SECTION {
continue;
}
let section_id = out_object.add_section(
in_section
.segment_name()
.map_err(|e| Error::ElfWrite(e.to_string()))?
.unwrap_or("")
.as_bytes()
.to_vec(),
in_section
.name()
.map_err(|e| Error::ElfWrite(e.to_string()))?
.as_bytes()
.to_vec(),
in_section.kind(),
);
let out_section = out_object.section_mut(section_id);
if out_section.is_bss() {
out_section.append_bss(in_section.size(), in_section.align());
} else {
let data = in_section
.data()
.map_err(|e| Error::ElfWrite(e.to_string()))?;
out_section.set_data(data, in_section.align());
}
out_section.flags = in_section.flags();
out_sections.insert(in_section.index(), section_id);
}
if let Some(data) = opts.append_ta_signature {
let section_id = out_object.add_section(
vec![],
TA_SIGNATURE_SECTION.as_bytes().to_vec(),
SectionKind::Data,
);
let out_section = out_object.section_mut(section_id);
out_section.set_data(data, 1);
out_section.flags = SectionFlags::Elf {
sh_flags: (object::elf::SHF_ALLOC | object::elf::SHF_WRITE) as u64,
};
}
let mut out_symbols: HashMap<object::SymbolIndex, write::SymbolId> = HashMap::new();
for in_symbol in in_object.symbols() {
let (section, value) = match in_symbol.section() {
SymbolSection::None => (write::SymbolSection::None, in_symbol.address()),
SymbolSection::Undefined => (write::SymbolSection::Undefined, in_symbol.address()),
SymbolSection::Absolute => (write::SymbolSection::Absolute, in_symbol.address()),
SymbolSection::Common => (write::SymbolSection::Common, in_symbol.address()),
SymbolSection::Section(index) => {
if let Some(out_section) = out_sections.get(&index) {
(
write::SymbolSection::Section(*out_section),
in_symbol.address()
- in_object
.section_by_index(index)
.map_err(|e| Error::ElfWrite(e.to_string()))?
.address(),
)
} else {
if in_symbol.kind() != SymbolKind::Section {
return Err(Error::ElfWrite(format!(
"symbol references skipped section: {:?}",
in_symbol
)));
}
continue;
}
}
_ => {
return Err(Error::ElfWrite(format!(
"unsupported symbol section for {:?}",
in_symbol
)));
}
};
let flags = match in_symbol.flags() {
SymbolFlags::None => SymbolFlags::None,
SymbolFlags::Elf { st_info, st_other } => SymbolFlags::Elf { st_info, st_other },
SymbolFlags::MachO { n_desc } => SymbolFlags::MachO { n_desc },
SymbolFlags::CoffSection {
selection,
associative_section,
} => {
let associative_section = match associative_section {
None => None,
Some(index) => Some(
*out_sections.get(&index).ok_or_else(|| {
Error::ElfWrite("CoffSection associative_section index missing".into())
})?,
),
};
SymbolFlags::CoffSection {
selection,
associative_section,
}
}
SymbolFlags::Xcoff {
n_sclass,
x_smtyp,
x_smclas,
containing_csect,
} => {
let containing_csect = match containing_csect {
None => None,
Some(index) => Some(
*out_symbols.get(&index).ok_or_else(|| {
Error::ElfWrite("Xcoff containing_csect symbol missing".into())
})?,
),
};
SymbolFlags::Xcoff {
n_sclass,
x_smtyp,
x_smclas,
containing_csect,
}
}
_ => {
return Err(Error::ElfWrite(format!(
"unsupported symbol flags for {:?}",
in_symbol
)));
}
};
let out_symbol = write::Symbol {
name: in_symbol.name().unwrap_or("").as_bytes().to_vec(),
value,
size: in_symbol.size(),
kind: in_symbol.kind(),
scope: in_symbol.scope(),
weak: in_symbol.is_weak(),
section,
flags,
};
let symbol_id = out_object.add_symbol(out_symbol);
out_symbols.insert(in_symbol.index(), symbol_id);
}
for in_section in in_object.sections() {
if in_section.kind() == SectionKind::Metadata {
continue;
}
let name = in_section.name().unwrap_or("");
if opts.skip_ta_signature && name == TA_SIGNATURE_SECTION {
continue;
}
let out_section = *out_sections
.get(&in_section.index())
.ok_or_else(|| Error::ElfWrite("missing out section for relocation pass".into()))?;
for (offset, in_relocation) in in_section.relocations() {
let symbol = match in_relocation.target() {
RelocationTarget::Symbol(symbol) => *out_symbols.get(&symbol).ok_or_else(|| {
Error::ElfWrite("relocation symbol index missing in out_symbols".into())
})?,
RelocationTarget::Section(section) => out_object.section_symbol(
*out_sections.get(§ion).ok_or_else(|| {
Error::ElfWrite("relocation section index missing".into())
})?,
),
_ => {
return Err(Error::ElfWrite(format!(
"unsupported relocation target: {:?}",
in_relocation
)));
}
};
let out_relocation = write::Relocation {
offset,
symbol,
addend: in_relocation.addend(),
flags: in_relocation.flags(),
};
out_object
.add_relocation(out_section, out_relocation)
.map_err(|e| Error::ElfWrite(e.to_string()))?;
}
}
for in_comdat in in_object.comdats() {
let mut sections = Vec::new();
for in_section in in_comdat.sections() {
sections.push(
*out_sections
.get(&in_section)
.ok_or_else(|| Error::ElfWrite("comdat section missing".into()))?,
);
}
out_object.add_comdat(write::Comdat {
kind: in_comdat.kind(),
symbol: *out_symbols
.get(&in_comdat.symbol())
.ok_or_else(|| Error::ElfWrite("comdat symbol missing".into()))?,
sections,
});
}
if let Some(in_build_version) = match &in_object {
object::File::MachO32(file) => file.build_version().unwrap(),
object::File::MachO64(file) => file.build_version().unwrap(),
_ => None,
} {
let mut out_build_version = object::write::MachOBuildVersion::default();
out_build_version.platform = in_build_version.platform.get(in_object.endianness());
out_build_version.minos = in_build_version.minos.get(in_object.endianness());
out_build_version.sdk = in_build_version.sdk.get(in_object.endianness());
out_object.set_macho_build_version(out_build_version);
}
out_object
.write()
.map_err(|e| Error::ElfWrite(e.to_string()))
}