提交 8f570c1e 编写于 作者: L Liangliang He

Resolve symbol obfuscation collision

上级 7eb89818
...@@ -19,7 +19,7 @@ namespace { ...@@ -19,7 +19,7 @@ namespace {
bool WriteFile(const std::string &filename, bool WriteFile(const std::string &filename,
bool binary, bool binary,
const std::vector<unsigned char> &content) { const std::vector<unsigned char> &content) {
std::ios_base::openmode mode = std::ios::out; std::ios_base::openmode mode = std::ios_base::out | std::ios_base::trunc;
if (binary) { if (binary) {
mode |= std::ios::binary; mode |= std::ios::binary;
} }
...@@ -124,17 +124,14 @@ cl::CommandQueue &OpenCLRuntime::command_queue() { return *command_queue_; } ...@@ -124,17 +124,14 @@ cl::CommandQueue &OpenCLRuntime::command_queue() { return *command_queue_; }
std::string OpenCLRuntime::GenerateCLBinaryFilenamePrefix( std::string OpenCLRuntime::GenerateCLBinaryFilenamePrefix(
const std::string &filename_msg) { const std::string &filename_msg) {
#ifdef MACE_OBFUSCATE_LITERALS // TODO This can be long and slow, fix it
return ObfuscateSymbolWithCollision(filename_msg);
#else
std::string filename_prefix = filename_msg; std::string filename_prefix = filename_msg;
for (auto it = filename_prefix.begin(); it != filename_prefix.end(); ++it) { for (auto it = filename_prefix.begin(); it != filename_prefix.end(); ++it) {
if (*it == ' ' || *it == '-' || *it == '=') { if (*it == ' ' || *it == '-' || *it == '=') {
*it = '_'; *it = '_';
} }
} }
return filename_prefix; return MACE_OBFUSCATE_SYMBOL(filename_prefix);
#endif
} }
extern bool GetSourceOrBinaryProgram(const std::string &program_name, extern bool GetSourceOrBinaryProgram(const std::string &program_name,
...@@ -145,21 +142,24 @@ extern bool GetSourceOrBinaryProgram(const std::string &program_name, ...@@ -145,21 +142,24 @@ extern bool GetSourceOrBinaryProgram(const std::string &program_name,
bool *is_opencl_binary); bool *is_opencl_binary);
void OpenCLRuntime::BuildProgram(const std::string &program_name, void OpenCLRuntime::BuildProgram(const std::string &program_name,
const std::string &binary_file_name_prefix, const std::string &built_program_key,
const std::string &build_options, const std::string &build_options,
cl::Program *program) { cl::Program *program) {
MACE_CHECK_NOTNULL(program); MACE_CHECK_NOTNULL(program);
bool is_opencl_binary = false; std::string binary_file_name_prefix =
GenerateCLBinaryFilenamePrefix(built_program_key);
std::vector<unsigned char> program_vec; std::vector<unsigned char> program_vec;
bool is_opencl_binary;
const bool found = GetSourceOrBinaryProgram(program_name, const bool found = GetSourceOrBinaryProgram(program_name,
binary_file_name_prefix, binary_file_name_prefix,
context(), context(),
device(), device(),
program, program,
&is_opencl_binary); &is_opencl_binary);
MACE_CHECK(found, "Program not found source: ", program_name, ", or binary: ", MACE_CHECK(found, "Program not found for ",
binary_file_name_prefix); is_opencl_binary ? "source: " : "binary: ",
built_program_key);
// Build program // Build program
std::string build_options_str = std::string build_options_str =
...@@ -173,7 +173,10 @@ void OpenCLRuntime::BuildProgram(const std::string &program_name, ...@@ -173,7 +173,10 @@ void OpenCLRuntime::BuildProgram(const std::string &program_name,
program->getBuildInfo<CL_PROGRAM_BUILD_LOG>(device()); program->getBuildInfo<CL_PROGRAM_BUILD_LOG>(device());
LOG(INFO) << "Program build log: " << build_log; LOG(INFO) << "Program build log: " << build_log;
} }
LOG(FATAL) << "Build program failed: " << ret; LOG(FATAL) << "Build program from "
<< (is_opencl_binary ? "source: " : "binary: ")
<< built_program_key
<< " failed: " << ret;
} }
if (!is_opencl_binary) { if (!is_opencl_binary) {
...@@ -222,9 +225,7 @@ cl::Kernel OpenCLRuntime::BuildKernel( ...@@ -222,9 +225,7 @@ cl::Kernel OpenCLRuntime::BuildKernel(
if (built_program_it != built_program_map_.end()) { if (built_program_it != built_program_map_.end()) {
program = built_program_it->second; program = built_program_it->second;
} else { } else {
std::string binary_file_name_prefix = this->BuildProgram(program_name, built_program_key,
GenerateCLBinaryFilenamePrefix(built_program_key);
this->BuildProgram(program_name, binary_file_name_prefix,
build_options_str, &program); build_options_str, &program);
built_program_map_.emplace(built_program_key, program); built_program_map_.emplace(built_program_key, program);
} }
......
...@@ -31,7 +31,7 @@ static void AddN(const std::vector<const Tensor *> &input_tensors, ...@@ -31,7 +31,7 @@ static void AddN(const std::vector<const Tensor *> &input_tensors,
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
auto dt = DataTypeToEnum<T>::value; auto dt = DataTypeToEnum<T>::value;
std::string kernel_name = MACE_KERNRL_NAME("addn"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("addn");
built_options.emplace("-Daddn=" + kernel_name); built_options.emplace("-Daddn=" + kernel_name);
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt));
built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt)); built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt));
......
...@@ -34,7 +34,7 @@ void BatchNormFunctor<DeviceType::OPENCL, T>::operator()( ...@@ -34,7 +34,7 @@ void BatchNormFunctor<DeviceType::OPENCL, T>::operator()(
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
auto dt = DataTypeToEnum<T>::value; auto dt = DataTypeToEnum<T>::value;
std::string kernel_name = MACE_KERNRL_NAME("batch_norm"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("batch_norm");
built_options.emplace("-Dbatch_norm=" + kernel_name); built_options.emplace("-Dbatch_norm=" + kernel_name);
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt));
built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt)); built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt));
......
...@@ -31,7 +31,7 @@ void BiasAddFunctor<DeviceType::OPENCL, T>::operator()( ...@@ -31,7 +31,7 @@ void BiasAddFunctor<DeviceType::OPENCL, T>::operator()(
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
auto dt = DataTypeToEnum<T>::value; auto dt = DataTypeToEnum<T>::value;
std::string kernel_name = MACE_KERNRL_NAME("bias_add"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("bias_add");
built_options.emplace("-Dbias_add=" + kernel_name); built_options.emplace("-Dbias_add=" + kernel_name);
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt));
built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt)); built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt));
......
...@@ -37,7 +37,7 @@ void BufferToImageFunctor<DeviceType::OPENCL, T>::operator()(Tensor *buffer, ...@@ -37,7 +37,7 @@ void BufferToImageFunctor<DeviceType::OPENCL, T>::operator()(Tensor *buffer,
kernel_name = i2b_ ? "arg_image_to_buffer" : "arg_buffer_to_image"; kernel_name = i2b_ ? "arg_image_to_buffer" : "arg_buffer_to_image";
break; break;
} }
string obfuscated_kernel_name = MACE_KERNRL_NAME(kernel_name); string obfuscated_kernel_name = MACE_OBFUSCATE_SYMBOL(kernel_name);
std::set<std::string> built_options; std::set<std::string> built_options;
std::stringstream kernel_name_ss; std::stringstream kernel_name_ss;
kernel_name_ss << "-D" << kernel_name << "=" << obfuscated_kernel_name; kernel_name_ss << "-D" << kernel_name << "=" << obfuscated_kernel_name;
......
...@@ -25,7 +25,7 @@ static void Concat2(const Tensor *input0, ...@@ -25,7 +25,7 @@ static void Concat2(const Tensor *input0,
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
std::string kernel_name = MACE_KERNRL_NAME("concat_channel"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("concat_channel");
built_options.emplace("-Dconcat_channel=" + kernel_name); built_options.emplace("-Dconcat_channel=" + kernel_name);
if (input0->dtype() == output->dtype()) { if (input0->dtype() == output->dtype()) {
built_options.emplace("-DDATA_TYPE=" + DtToCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToCLDt(dt));
......
...@@ -36,7 +36,7 @@ void Conv1x1(const Tensor *input, ...@@ -36,7 +36,7 @@ void Conv1x1(const Tensor *input,
MACE_CHECK(input_batch == batch); MACE_CHECK(input_batch == batch);
std::set<std::string> built_options; std::set<std::string> built_options;
std::string kernel_name = MACE_KERNRL_NAME("conv_2d_1x1"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("conv_2d_1x1");
built_options.emplace("-Dconv_2d_1x1=" + kernel_name); built_options.emplace("-Dconv_2d_1x1=" + kernel_name);
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt));
built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt)); built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt));
......
...@@ -28,7 +28,7 @@ static void Conv2d3x3S12(const Tensor *input, const Tensor *filter, ...@@ -28,7 +28,7 @@ static void Conv2d3x3S12(const Tensor *input, const Tensor *filter,
const index_t width_blocks = RoundUpDiv<index_t, 5>(width); const index_t width_blocks = RoundUpDiv<index_t, 5>(width);
std::set<std::string> built_options; std::set<std::string> built_options;
std::string kernel_name = MACE_KERNRL_NAME("conv_2d_3x3"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("conv_2d_3x3");
built_options.emplace("-Dconv_2d_3x3=" + kernel_name); built_options.emplace("-Dconv_2d_3x3=" + kernel_name);
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt));
built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt)); built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt));
......
...@@ -28,7 +28,7 @@ void Conv2dOpencl(const Tensor *input, const Tensor *filter, ...@@ -28,7 +28,7 @@ void Conv2dOpencl(const Tensor *input, const Tensor *filter,
const index_t width_blocks = RoundUpDiv4(width); const index_t width_blocks = RoundUpDiv4(width);
std::set<std::string> built_options; std::set<std::string> built_options;
std::string kernel_name = MACE_KERNRL_NAME("conv_2d"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("conv_2d");
built_options.emplace("-Dconv_2d=" + kernel_name); built_options.emplace("-Dconv_2d=" + kernel_name);
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt));
built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt)); built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt));
......
...@@ -33,7 +33,7 @@ static void InnerDepthwiseConvOpenclK3x3S12(const Tensor *input, ...@@ -33,7 +33,7 @@ static void InnerDepthwiseConvOpenclK3x3S12(const Tensor *input,
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
std::string kernel_name = MACE_KERNRL_NAME("depthwise_conv_3x3"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("depthwise_conv_3x3");
built_options.emplace("-Ddepthwise_conv_3x3=" + kernel_name); built_options.emplace("-Ddepthwise_conv_3x3=" + kernel_name);
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(input->dtype())); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(input->dtype()));
built_options.emplace(stride == 1 ? "-DSTRIDE_1" : ""); built_options.emplace(stride == 1 ? "-DSTRIDE_1" : "");
......
...@@ -28,7 +28,7 @@ static void Pooling(const Tensor *input, ...@@ -28,7 +28,7 @@ static void Pooling(const Tensor *input,
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
std::string kernel_name = MACE_KERNRL_NAME("pooling"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("pooling");
built_options.emplace("-Dpooling=" + kernel_name); built_options.emplace("-Dpooling=" + kernel_name);
if (type == MAX && input->dtype() == output->dtype()) { if (type == MAX && input->dtype() == output->dtype()) {
built_options.emplace("-DDATA_TYPE=" + DtToCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToCLDt(dt));
......
...@@ -32,7 +32,7 @@ void ReluFunctor<DeviceType::OPENCL, T>::operator()(const Tensor *input, ...@@ -32,7 +32,7 @@ void ReluFunctor<DeviceType::OPENCL, T>::operator()(const Tensor *input,
built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt)); built_options.emplace("-DCMD_DATA_TYPE=" + DtToUpstreamCLCMDDt(dt));
cl::Kernel relu_kernel; cl::Kernel relu_kernel;
if (max_limit_ < 0) { if (max_limit_ < 0) {
std::string kernel_name = MACE_KERNRL_NAME("relu"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("relu");
built_options.emplace("-Drelu=" + kernel_name); built_options.emplace("-Drelu=" + kernel_name);
relu_kernel = runtime->BuildKernel("relu", kernel_name, built_options); relu_kernel = runtime->BuildKernel("relu", kernel_name, built_options);
...@@ -40,7 +40,7 @@ void ReluFunctor<DeviceType::OPENCL, T>::operator()(const Tensor *input, ...@@ -40,7 +40,7 @@ void ReluFunctor<DeviceType::OPENCL, T>::operator()(const Tensor *input,
relu_kernel.setArg(idx++, *(static_cast<const cl::Image2D *>(input->buffer()))); relu_kernel.setArg(idx++, *(static_cast<const cl::Image2D *>(input->buffer())));
relu_kernel.setArg(idx++, *(static_cast<cl::Image2D *>(output->buffer()))); relu_kernel.setArg(idx++, *(static_cast<cl::Image2D *>(output->buffer())));
} else { } else {
std::string kernel_name = MACE_KERNRL_NAME("relux"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("relux");
built_options.emplace("-Drelux=" + kernel_name); built_options.emplace("-Drelux=" + kernel_name);
relu_kernel = runtime->BuildKernel("relu", kernel_name, built_options); relu_kernel = runtime->BuildKernel("relu", kernel_name, built_options);
......
...@@ -40,7 +40,7 @@ void ResizeBilinearFunctor<DeviceType::OPENCL, T>::operator()( ...@@ -40,7 +40,7 @@ void ResizeBilinearFunctor<DeviceType::OPENCL, T>::operator()(
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
std::string kernel_name = MACE_KERNRL_NAME("resize_bilinear_nocache"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("resize_bilinear_nocache");
built_options.emplace("-Dresize_bilinear_nocache=" + kernel_name); built_options.emplace("-Dresize_bilinear_nocache=" + kernel_name);
auto dt = DataTypeToEnum<T>::value; auto dt = DataTypeToEnum<T>::value;
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt));
......
...@@ -26,7 +26,7 @@ void SoftmaxFunctor<DeviceType::OPENCL, T>::operator()(const Tensor *logits, ...@@ -26,7 +26,7 @@ void SoftmaxFunctor<DeviceType::OPENCL, T>::operator()(const Tensor *logits,
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
std::string kernel_name = MACE_KERNRL_NAME("softmax"); std::string kernel_name = MACE_OBFUSCATE_SYMBOL("softmax");
built_options.emplace("-Dsoftmax=" + kernel_name); built_options.emplace("-Dsoftmax=" + kernel_name);
auto dt = DataTypeToEnum<T>::value; auto dt = DataTypeToEnum<T>::value;
built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt)); built_options.emplace("-DDATA_TYPE=" + DtToUpstreamCLDt(dt));
......
...@@ -30,7 +30,7 @@ void SpaceToBatchFunctor<DeviceType::OPENCL, T>::operator()(Tensor *space_tensor ...@@ -30,7 +30,7 @@ void SpaceToBatchFunctor<DeviceType::OPENCL, T>::operator()(Tensor *space_tensor
batch_tensor->ResizeImage(output_shape, output_image_shape); batch_tensor->ResizeImage(output_shape, output_image_shape);
kernel_name = "space_to_batch"; kernel_name = "space_to_batch";
} }
std::string obfuscated_kernel_name = MACE_KERNRL_NAME(kernel_name); std::string obfuscated_kernel_name = MACE_OBFUSCATE_SYMBOL(kernel_name);
auto runtime = OpenCLRuntime::Global(); auto runtime = OpenCLRuntime::Global();
std::set<std::string> built_options; std::set<std::string> built_options;
std::stringstream kernel_name_ss; std::stringstream kernel_name_ss;
......
...@@ -46,7 +46,7 @@ class Tuner { ...@@ -46,7 +46,7 @@ class Tuner {
&param_generator, &param_generator,
const std::function<RetType(const std::vector<param_type> &)> &func, const std::function<RetType(const std::vector<param_type> &)> &func,
Timer *timer) { Timer *timer) {
std::string obfucated_param_key = MACE_OBFUSCATE_SYMBOLS(param_key); std::string obfucated_param_key = MACE_OBFUSCATE_SYMBOL(param_key);
if (IsTuning() && param_generator != nullptr) { if (IsTuning() && param_generator != nullptr) {
// tune // tune
std::vector<param_type> opt_param = default_param; std::vector<param_type> opt_param = default_param;
...@@ -92,8 +92,7 @@ class Tuner { ...@@ -92,8 +92,7 @@ class Tuner {
int32_t key_size = kp.first.size(); int32_t key_size = kp.first.size();
ofs.write(reinterpret_cast<char *>(&key_size), sizeof(key_size)); ofs.write(reinterpret_cast<char *>(&key_size), sizeof(key_size));
ofs.write(kp.first.c_str(), key_size); ofs.write(kp.first.c_str(), key_size);
VLOG(1) << "Write tuning param: " VLOG(1) << "Write tuning param: " << kp.first.c_str();
<< MACE_OBFUSCATE_SYMBOLS(kp.first.c_str());
auto &params = kp.second; auto &params = kp.second;
int32_t params_size = params.size() * sizeof(param_type); int32_t params_size = params.size() * sizeof(param_type);
......
...@@ -65,26 +65,40 @@ inline std::string ObfuscateString(const std::string &src) { ...@@ -65,26 +65,40 @@ inline std::string ObfuscateString(const std::string &src) {
} }
// Obfuscate synbol or path string // Obfuscate synbol or path string
inline std::string ObfuscateSymbolWithCollision(const std::string &src) { inline std::string ObfuscateSymbol(const std::string &src) {
std::string dest = ObfuscateString(src); std::string dest = src;
if (dest.empty()) {
return dest;
}
dest[0] = src[0]; // avoid invalid symbol which starts from 0-9
const std::string encode_dict = const std::string encode_dict =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
for (int i = 0; i < src.size(); i++) { for (int i = 1; i < src.size(); i++) {
dest[i] = encode_dict[dest[i] % encode_dict.size()]; char ch = src[i];
int idx;
if (ch >= '0' && ch <= '9') {
idx = ch - '0';
} else if (ch >= 'a' && ch <= 'z') {
idx = 10 + ch - 'a';
} else if (ch >= 'A' && ch <= 'Z') {
idx = 10 + 26 + ch - 'a';
} else if (ch == '_') {
idx = 10 + 26 + 26;
} else {
dest[i] = ch;
continue;
}
dest[i] = encode_dict[(idx + 37) % encode_dict.size()];
} }
return std::move(dest); return std::move(dest);
} }
#ifdef MACE_OBFUSCATE_LITERALS #ifdef MACE_OBFUSCATE_LITERALS
#define MACE_OBFUSCATE_STRING(str) ObfuscateString(str) #define MACE_OBFUSCATE_STRING(str) ObfuscateString(str)
// This table is delibratedly selected to avoid '\0' in genereated literal #define MACE_OBFUSCATE_SYMBOL(str) ObfuscateSymbol(str)
#define MACE_OBFUSCATE_SYMBOLS(str) ObfuscateString(str, "!@#$%^&*()+?")
// OpenCL will report error if there is name collision
#define MACE_KERNRL_NAME(name) ObfuscateSymbolWithCollision(name)
#else #else
#define MACE_OBFUSCATE_STRING(str) (str) #define MACE_OBFUSCATE_STRING(str) (str)
#define MACE_OBFUSCATE_SYMBOLS(str) (str) #define MACE_OBFUSCATE_SYMBOL(str) (str)
#define MACE_KERNRL_NAME(name) (name)
#endif #endif
} // namespace mace } // namespace mace
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册